pax_global_header00006660000000000000000000000064133203506320014507gustar00rootroot0000000000000052 comment=b3e5428d92de2eeedd09fb4ca3d290c50fa8b7e5 lxi-tools-1.21/000077500000000000000000000000001332035063200133645ustar00rootroot00000000000000lxi-tools-1.21/.gitignore000066400000000000000000000005471332035063200153620ustar00rootroot00000000000000*.tar.xz Makefile.in Makefile aclocal.m4 autom4te.cache/ configure compile depcomp install-sh missing src/Makefile.in src/Makefile config.log config.status .deps/ .libs/ src/include/config.h src/include/config.h.in src/include/config.h.in~ src/include/stamp-h1 *.swp *.asc /ar-lib .dirstamp *.a *.o *.la *.lo /ltmain.sh /config.guess /config.sub /libtool /m4 lxi-tools-1.21/.travis.yml000066400000000000000000000007431332035063200155010ustar00rootroot00000000000000sudo: true language: c dist: trusty install: - sudo apt-get -qq update - sudo apt-get install -y libavahi-core-dev libavahi-common-dev libavahi-client-dev libxml2-dev liblua5.2-dev libtirpc-dev - wget https://github.com/lxi/liblxi/archive/master.tar.gz - tar -xvf master.tar.gz - pushd liblxi-master && ./autogen.sh && ./configure --prefix=/usr && make && sudo make install && popd before_script: ./autogen.sh script: ./configure --prefix=/usr && make && sudo make install lxi-tools-1.21/AUTHORS000066400000000000000000000007711332035063200144410ustar00rootroot00000000000000Maintainer: Martin Lund Contributors: Robert Scheck Jakub Wilk RoGeorge from EEVBlog forum dpenev from EEVBlog forum PeDre from EEVBlog forum BloodyCactus from EEVBlog forum crispus from EEVBlog forum ralphrmartin from EEVBlog forum Dmitri Goutnik Hydron from EEVBlog forum gsocker from EEVBlog forum N0NB from EEVBlog forum Timur Aydin Thanks to everyone who has contributed to this project. lxi-tools-1.21/COPYING000066400000000000000000000033271332035063200144240ustar00rootroot00000000000000lxi-tools is available for use under the following license, commonly known as the 3-clause (or "modified") BSD license: ----------------------------------------------------------------------- Copyright (c) 2016-2017 Martin Lund All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------- lxi-tools-1.21/ChangeLog000066400000000000000000000336261332035063200151500ustar00rootroot00000000000000=== lxi-tools v1.21 === Changes since lxi-tools v1.20: * Fix handling of question commands in interactive mode Changes since lxi-tools v1.19: * Add bash completion for snap * Update README Add tested instrument Keysight AWG 33612A as tested by Timur Aydin. * Add const qualifier * Update AUTHORS * Require Lua 5.1 or newer * Include test dir in distribution * Update Travis * Fix bash completion for run command * Update basic-tests.lua * Move test directory * Add basic Lua tests Changes since lxi-tools v1.18: * Downgrade to Lua 5.2 * Update Travis configuration * Add Lua scripting feature to support automation Add run command which makes it possible to run Lua scripts to support advanced instrument automation. To run a Lua script simply do: $ lxi run test.lua The following LXI specific Lua functions are added and made available for use in the Lua scripts: device = connect(ip) scpi(device, command) scpi_raw(device, command) msleep(miliseconds) sleep(seconds) disconnect(device) See src/test/test.lua for a simple Lua script example. * Update README * Update AUTHORS * Improve regex of rs-hmo-rtb screenshot plugin Include instruments made with "HAMEG" identifier. * README: Add sponsors section * lxi-gui: Fix snap build * configure: use pkg-config to check for Qt5 * lxi-gui: Cleanup Qt5 configuration * Reconfigure R&S screenshot plugin to BMP * Add RTB2004 to list of tested instruments Dmitri Goutnik: * Use QT_SELECT value instead of hardcoded QT version Changes since lxi-tools v1.17: * lxi-gui: Add X-axis title to data recorder chart * lxi-gui: Fix data recorder chart colors and csv export * lxi-gui: Add SCPI 1999.0 commands * lxi-gui: Add data recorder save data feauture Add a save button which allows to save recorded data to file in CSV format. * lxi: Increase default discover mDNS timeout * lxi-gui: Optimize data recorder plotting * lxi-gui: Fix arm snap build * lxi-gui: Print machine type during qmake build * lxi-gui: Remove *OPT? SCPI command * lxi-gui: Use elapsed real time in data recorder * lxi-gui: Print SCPI command requests * Add screenshot support for RTB 2000 * Cleanup timeout handling, etc. * lxi-gui: Reduce minimum window size * Add support for adding custom Qt qmake arguments Add QMAKE_ARGUMENTS flag which allows to pass on arguments to qmake when building lxi-gui. * lxi-gui: Make sure to call QT5 qmake Changes since lxi-tools v1.16: * lxi-gui: Add input dialog for *ESE and *SRE commands * lxi-gui: Fix qmake compile flags * lxi-gui: Start with SCPI page * Add configure check for Qt5Charts * lxi-gui: Cleanup Name UI elements accordingly * lxi-gui: Add screenshot live view * Update README screenshot * lxi-gui: Add ID/IP instrument table header * lxi-gui: Tag as BETA * Update README Introduce lxi-gui and include screenshot. * Link QT5 Charts manually To avoid build issue with snap. * lxi-gui: Add data recorder feature * lxi-gui: Add settings * lxi-gui: Add QT5 source files * lxi-gui: Introduce responsive layout The lxi-gui application can now automatically resize to fit any window size. * lxi-gui: Add 'Open in browser' right-click feature * lxi-gui: Add IEEE 488.2 Common Commands * lxi-gui: Add about details * lxi-gui: Add screenshot feature * lxi-gui: Add benchmark feature * Split features into separate files * Update README * Update lxi-gui * Add keysight-dmm screenshot plugin This plugin supports Keysight Truevolt digital multimeters. * Set default discover timeout to 1 s * Add experimental QT5 GUI Can be enabled using configure option --enable-lxi-gui Requires QT 5.0.0 or newer. * Cleanup * Update Travis Changes since lxi-tools v1.15: * Update AUTHORS * Convert tabs to spaces * Remove experimental label from keysight-ivx plugin Tested with MSO-X 3024T by ralphrmartin from EEVBlog forum. * Fix keysight-iv2000x plugin Fix header strip and change image format to BMP. Improve regex. * Fix image format for rigol-dg4000 plugin * Update completion script * Cleanup Dmitri Goutnik: * Make code clang friendly Changes since lxi-tools v1.14: * Update man page * Add support for using raw/TCP in benchmark mode Add the option to run benchmark using raw/TCP. For example: $ lxi benchmark --address 10.0.0.42 --port 5555 --raw Also, cleanup all port handling code and update documentation accordingly. * Decrease timeout for discover to 2 s Changes since lxi-tools v1.13: * Make screenshot plugin only support Rigol DM3068 Rigol DM3068 is the only DM3000 series digital multimeter that seems to have screenshot support. * Fix entering interactive mode Regardless of using --interactive a SCPI command was still required to be provided to enter interactive mode. * Update AUTHORS * Remove experimental label from Siglent plugins Thanks to Siglent who helped fix and test all the screenshot plugins for their instruments. * Cleanup screenshot plugins * Consolidate Rigol DSA plugins into one * Update README and man page * Support writing screenshot image to stdout To write screenshot image to stdout simply use '-' as the output filename. This allows to pipe the screenshot image directly to other tools for image processing. For example, using imagemagick to automatically convert captured screenshot image to JPG: $ lxi screenshot -a 10.0.0.42 - | convert - screenshot.jpg * Cleanup Siglent screenshot plugins * Update siglent-ssa3000x plugin * Add siglent-sdg plugin * Add siglent-sdm3000 plugin * Move siglent-sds out of experimental * Extend Siglent plugin to include SDS2000X Changes since lxi-tools v1.12: * Update README * Update SSA3000X capture command * Add completion for benchmark command * Update AUTHORS * Fix get_device_id() This function was missing a call to lxi_disconnect() which resulted in some instruments being left hanging when capturing screenshots. Instruments that presumable only allow one active connection. * Add benchmark feature This benchmark feature is useful if you want to compare the VXI-11 request/response performance of your instruments. By default the benchmark sends 100 SCPI ID requests ("*IDN?" commands) to the instrument. For each request it waits for and reads the response. When done the resulting request rate is printed. * Fix screenshot command when using plugin autodetection The wrong timeout value was passed when trying to autodetect which screenshot plugin to use. * Cleanup * Fix Rohde & Schwarz HMO 1000 screenshot plugin Fix plugin so that it does not strip off the PNG header of the PNG image stream. Also, the source files and functions of the plugin is now named more explicitly according to the name of the instrument series (HMO 1000). * Fix Siglent SSA3000 screenshot plugin Changes since lxi-tools v1.11: * Update to new URL * Add snap status * Fix redirection of output to file * Update README * Cleanup configure.ac Jakub Wilk: * Use HTTPS in the configure script Changes since lxi-tools v1.10: * Update README * Update man page * Update AUTHORS * Expand tested instruments list * Rename screenshot plugin rigol-1000 -> rigol-1000z * Add various Rigol screenshot plugins Add the following Rigol screenshot plugins: rigol-dg4000 Rigol DG4000 series function generator rigol-dm3000 Rigol DM3000 series digital multimeter rigol-dp800 Rigol DP800 series power supply rigol-dsa800 Rigol DSA800 series spectrum analyzer rigol-dsa700 Rigol DSA700 series spectrum analyzer The code is added on behalf of PeDre from the EEVBlog forum. * Add authors section in README * Add README.md to prettify GitHub page The original README is still preserved because it is more readable when not reading it via GitHub. Jakub Wilk: * Strip trailing spaces * Fix typo * Fix grammar and typos Changes since lxi-tools v1.9: * Add support for mDNS/DNS-SD discovery Add "--mdns" option which enables the discover command to search for LXI devices/services using mDNS/DNS-SD. * Fix Siglent screenshot plugins Write correct response buffer to file. Improve .regex match expression. Changes since lxi-tools v1.8: * Update README * Fix newlines when redirecting to file or terminal * Rename --dump-hex to --hex * Fix missing error message when no SCPI command defined * Update man page * Remove --dump-file option The correct way to dump response to file is to use pipe output redirection. For example: lxi scpi --address 192.168.1.210 "*IDN?" > response.txt This way it is possible to dump any binary response to file. * Fix missing error message when no IP address defined * Print errors to stderr * Cleanup Siglent SDS1000 plugin name * Add Siglent SSA 3000X screenshot plugin * Cleanup script examples * Correct default SCPI raw/TCP port By default use port 5025 as described here: http://www.lxistandard.org/About/LXI-Protocols.aspx If a different port is needed use the '--raw-port' option. Apparently Rigol is not using the recommended port for raw SCPI commands. * Update descriptions of the plugin options Changes since lxi-tools v1.7: * Update README * Add Siglent SDS 1000 screenshot plugin Changes since lxi-tools v1.6: * Update README * Cleanup * Update .regex for Tektronix plugin * Update .regex for R&S plugin * Update .regex for Keysight plugin * Update man page * Embed instrument IP address in screenshot filename This helps identify screenshot files when capturing screenshots from multiple instruments. It also allows simplifying the APIs used by the screenshot plugins. * Change option '--model' to '--plugin' Lets remove any model vs. plugin confusion and only deal with plugin names. Each plugin includes support for one or more instruments models as described in the plugin description. * Add automatic loading of screenshot plugin feature If no screenshot plugin is specified the tool will automatically try to select the best plugin by matching the instruments ID string against the regular expressions defined in each plugin. Each screenshot plugin defines a .regex string entry containing space separated regular expressions. Each regular expression is matched against the instrument ID string. The plugin with most matches is selected. Note: This mechanism is slightly slower than manually specifying which screenshot plugin because it needs to retrieve the instruments ID string first. * Improve description of Rigol plugins * Fix Rigol 2000 screenshot plugin Remove trailing newline in received image data. * Add screenshot plugin for Rigol 2000 series Also, make existing Rigol plugin only apply for 1000z series. Changes since lxi-tools v1.5: * Add date-time stamp to screenshot filename * Improve command handling In case of a misspelled command the tool would misleadingly respond: "Error: No IP address specified" With this fix, it now responds: "Error: Unknown command" * Update README * Added screenshot plugin for Tektronix 2000 series scopes * Improve scpi response output * Add --raw and --raw-port options to scpi command One can now use choose to use raw/TCP instead of VXI11 when firing SCPI commands. Simply append the --raw option like so: lxi scpi --raw --address 192.168.0.42 "*IDN?" By default raw/TCP port 5555 is used but it can be changed using the --raw-port option. Warning: Using raw/TCP is faster than VXI11 but does not provide any timeout/control mechanisms so if your command somehow stalls it will stall forever. * Use new lxi_connect() function * Cleanup * Make screenshot filename optional In case no screenshot filename is provided the tool will write the screenshot image to an automatically resolved and incremented filename on the form screenshot-###.. For example, screenshot-000.png, screenshot-001.png, etc. * Improve screenshot model listing * Increase default timeout for screenshot command Transferring screenshot image data takes time so lets increase the timeout so we do not easily interrupt a good but slow transfer. * Collapse Rigol screenshot plugin Support all Rigol oscilloscope models via one model name. * Add screenshot support for Keysight IV 2000 X * Cleanup plugins * Add screenshot support for R&S HMO1000 series * Add screenshot support for Rigol 2000/4000 * Create directory for screenshot plugins Changes since lxi-tools v1.4: * Remove -Og flag Not supported by older gcc versions. * Fix various bugs in script and interactive mode Changes since lxi-tools v1.3: * Add screenshot feature to lxi tool The old rigol_1000z_screenshot tool is retired in favor of a screenshot feature in the lxi tool. A new lxi command 'screenshot' is added and also a small screenshot plugin framework which makes it easy to add new screenshot plugins for new instruments. Currently there is only a screenshot plugin for Rigol 1000Z series oscilloscopes. * Update README Changes since lxi-tools v1.2: * Added AUTHORS file * Use new lxi_connect() function * Reconfigure default timeout to 3 seconds Changes since lxi-tools v1.1: * Increase default timeout value for lxi tool Increase the default timeout value for the lxi tool from 1 to 5 seconds * Fix timeout for Rigol screenshot tool Timeout is increased to avoid failure to capture screenshot * Fix typo * Cleanup test script * Fix crash in rigol_1000z_screenshot tool Add error handling to avoid crashing when connecting remote device fails. Changes since lxi-tools v1.0: * Fix missing src/bash-completion/lxi An automake rule was fixed to make sure that the file src/bash-completion/lxi is included in the distributed tarball. * Update README lxi-tools v1.0: * First release (stable) lxi-tools-1.21/Makefile.am000066400000000000000000000001221332035063200154130ustar00rootroot00000000000000SUBDIRS = src man EXTRA_DIST = test if LXI_GUI SUBDIRS += src/gui/lxi-gui endif lxi-tools-1.21/README000066400000000000000000000221251332035063200142460ustar00rootroot00000000000000 === lxi-tools === 1. Introduction lxi-tools is a collection of open source software tools that enables control of LXI compatible instruments such as modern oscilloscopes, power supplies, spectrum analyzers etc. All features are consolidated in the 'lxi' application which provides a simple commandline interface to discover LXI instruments, send SCPI commands, and capture screenshots from supported LXI instruments. Also provided is a GUI application which provide some of the features of the commandline tool. lxi-tools rely on liblxi for all communication. 2. Usage 2.1 lxi-gui (BETA) (See screenshot images/lxi-gui-beta.png) The lxi-gui application provides some of the same features as the commandline tool but presents them in a GUI frontend. 2.2 lxi The commandline interface of the lxi application is described in the output from 'lxi --help': Usage: lxi [--version] [--help] [] -v, --version Display version -h, --help Display help Commands: discover [] Search for LXI devices scpi [] Send SCPI command screenshot [] [] Capture screenshot benchmark [] Benchmark Discover options: -t, --timeout Timeout (default: 3) -m, --mdns Search via mDNS/DNS-SD Scpi options: -a, --address Device IP address -p, --port Use port (default: VXI11: 111, RAW: 5025) -t, --timeout Timeout (default: 3) -x, --hex Print response in hexadecimal -i, --interactive Enter interactive mode -s, --script Run script file -r, --raw Use raw/TCP Screenshot options: -a, --address Device IP address -t, --timeout Timeout (default: 15) -p, --plugin Use screenshot plugin by name -l, --list List available screenshot plugins Benchmark options: -a, --address Device IP address -p, --port Use port (default: VXI11: 111, RAW: 5025) -t, --timeout Timeout (default: 3) -c, --count Number of request messages (default: 100) -r, --raw Use raw/TCP 2.2.1 Example - Discover LXI devices on available networks $ lxi discover Searching for LXI devices - please wait... Broadcasting on interface lo Broadcasting on interface eth0 Found "RIGOL TECHNOLOGIES,DS1104Z,DS1ZA1234567890,00.04.03.SP2" on address 10.42.1.20 Found "RIGOL TECHNOLOGIES,DP831,DP8F1234567890,00.01.14" on address 10.42.1.67 Broadcasting on interface wlan0 Found 2 devices 2.2.2 Example - Send SCPI command to an instrument $ lxi scpi --address 10.42.1.20 "*IDN?" RIGOL TECHNOLOGIES,DS1104Z,DS1ZA1234567890,00.04.03 To dump response to file simply do: $ lxi scpi --address 10.42.1.20 "*IDN?" > response.txt 2.2.3 Example - Capture screenshot from a Rigol 1000z series oscilloscope: $ lxi screenshot --address 10.42.1.20 --plugin rigol-1000z Saved screenshot image to screenshot_10.42.1.20_2017-11-11_13:45:47.png Or using plugin autodetection simply: $ lxi screenshot --address 10.42.1.20 Loaded rigol-1000 screenshot plugin Saved screenshot image to screenshot_10.42.1.20_2017-11-11_13:46:02.png 2.2.4 Example - Capture screenshot and convert it to any image format By default the format of the captured screenshot image is dictated by which screenshot plugin and instrument are in play. However, it is possible to write the screenshot image to stdout and pipe it directly to other tools for image processing. For example, use ImageMagicks convert tool to automatically convert screenshot image to JPG: $ lxi screenshot --address 10.42.1.20 - | convert - screenshot.jpg 2.2.5 Example - List available screenshot plugins $ lxi screenshot --list Name Description keysight-ivx Keysight InfiniVision 2000X/3000X series oscilloscope rigol-1000z Rigol DS/MSO 1000z series oscilloscope rigol-2000 Rigol DS/MSO 2000 series oscilloscope rigol-dg4000 Rigol DG 4000 series function generator rigol-dm3000 Rigol DM 3000 series digital multimeter rigol-dp800 Rigol DP 800 series power supply rigol-dsa Rigol DSA 700/800 series spectrum analyzer rs-hmo1000 Rohde & Schwarz HMO 1000 series oscilloscope (experimental) siglent-sdm3000 Siglent SDM 3000/3000X series digital multimeter siglent-sdg Siglent SDG 1000X/2000X/6000X series waveform generator siglent-sds Siglent SDS 1000X/2000X series oscilloscope siglent-ssa3000x Siglent SSA 3000X series spectrum analyzer tektronix-2000 Tektronix DPO/MSO 2000 series oscilloscope (experimental) 2.2.6 Example - Benchmark instrument request/response performance $ lxi benchmark --address 10.42.1.20 Benchmarking by sending 100 ID requests. Please wait... Result: 24.7 requests/second 3. Installation The latest release version can be downloaded from https://lxi-tools.github.io 3.1 Installation from release tarball Install steps: $ ./configure $ make $ make install Note: lxi-tools depends on liblxi so you need to go install liblxi first. 3.2 Installation using package lxi-tools comes prepackaged for various GNU/Linux distributions. Visit https://lxi-tools.github.io to see list of supported distributions. 4. Tested instruments The commandline lxi tool is tested to work successfully with the following LXI compatible instruments: Instrument Working features Keysight Technologies AWG 33612A (discover+scpi+screenshot) Keysight Technologies DMM 34461A (discover+scpi+screenshot) Keysight Technologies MSO-X 3024T (discover+scpi+screenshot) Rigol Technologies DG4062 (discover+scpi+screenshot) Rigol Technologies DG4102 (discover+scpi+screenshot) Rigol Technologies DG4162 (discover+scpi+screenshot) Rigol Technologies DP831 (discover+scpi+screenshot) Rigol Technologies DP832 (discover+scpi+screenshot) Rigol Technologies DM3068 (discover+scpi+screenshot) Rigol Technologies DS1104Z (discover+scpi+screenshot) Rigol Technologies DS2302 (discover+scpi+screenshot) Rigol Technologies DSA815 (discover+scpi+screenshot) Rigol Technologies MSO1104Z (discover+scpi+screenshot) Rigol Technologies MSO2302A (discover+scpi+screenshot) Rohde & Schwarz HMC 8012 (discover+scpi+screenshot) Rohde & Schwarz HMC 8043 (discover+scpi+screenshot) Rohde & Schwarz HMO 1202 (discover+scpi+screenshot) Rohde & Schwarz HMO 3054 (scpi+screenshot) Rohde & Schwarz RTB 2004 (discover+scpi+screenshot) Siglent Technologies SDG1032X (discover+scpi+screenshot) Siglent Technologies SDG2122X (discover+scpi+screenshot) Siglent Technologies SDG6052 (discover+scpi+screenshot) Siglent Technologies SDS1202X-E (discover+scpi+screenshot) Siglent Technologies SDS1204X-E (discover+scpi+screenshot) Siglent Technologies SDS2304X (discover+scpi+screenshot) Siglent Technologies SDM3045X (discover+scpi+screenshot) Siglent Technologies SDM3055 (discover+scpi+screenshot) Siglent Technologies SDM3065X (discover+scpi+screenshot) Siglent Technologies SPD3303X-E (scpi) Siglent Technologies SSA3032X (discover+scpi+screenshot) Note: Feel free to add your instrument(s) to the list via GitHub pull request or simply create a GitHub issue reporting your instrument(s) and which features work. 5. Contributing lxi-tools is open source. If you want to help out with the project please join in. All contributions (bug reports, code, doc, ideas, etc.) are welcome. Please use the github issue tracker and pull request features. Also, if you find this free open source software useful please consider making a donation: https://www.paypal.me/lundmar 6. Website Visit https://lxi-tools.github.io 7. License This code is released under BSD-3, commonly known as the 3-clause (or "modified") BSD license. 8. Authors Created by Martin Lund See the AUTHORS file for full list of authors. 9. Sponsors A thank you to the following sponsors that have donated test equipment to support the lxi-tools open source effort: * KIKUSUI Electronics Corp. lxi-tools-1.21/README.md000066400000000000000000000226441332035063200146530ustar00rootroot00000000000000# lxi-tools [![Build Status](https://travis-ci.org/lxi-tools/lxi-tools.svg?branch=master)](https://travis-ci.org/lxi-tools/lxi-tools) [![Snap Status](https://build.snapcraft.io/badge/lxi-tools/lxi-tools.snapcraft.svg)](https://build.snapcraft.io/user/lxi-tools/lxi-tools.snapcraft) ## 1. Introduction lxi-tools is a collection of open source software tools that enables control of LXI compatible instruments such as modern oscilloscopes, power supplies, spectrum analyzers etc. All features are consolidated in the 'lxi' application which provides a simple commandline interface to discover LXI instruments, send SCPI commands, and capture screenshots from supported LXI instruments. Also provided is a GUI application which provide some of the features of the commandline tool. lxi-tools rely on liblxi for all communication. ## 2. Usage ### 2.1 lxi-gui (BETA)

The lxi-gui application provides some of the same features as the commandline tool but presents them in a GUI frontend. ### 2.2 lxi The commandline interface of the lxi application is described in the output from 'lxi --help': ``` Usage: lxi [--version] [--help] [] -v, --version Display version -h, --help Display help Commands: discover [] Search for LXI devices scpi [] Send SCPI command screenshot [] [] Capture screenshot benchmark [] Benchmark Discover options: -t, --timeout Timeout (default: 3) -m, --mdns Search via mDNS/DNS-SD Scpi options: -a, --address Device IP address -p, --port Use port (default: VXI11: 111, RAW: 5025) -t, --timeout Timeout (default: 3) -x, --hex Print response in hexadecimal -i, --interactive Enter interactive mode -s, --script Run script file -r, --raw Use raw/TCP Screenshot options: -a, --address Device IP address -t, --timeout Timeout (default: 15) -p, --plugin Use screenshot plugin by name -l, --list List available screenshot plugins Benchmark options: -a, --address Device IP address -p, --port Use port (default: VXI11: 111, RAW: 5025) -t, --timeout Timeout (default: 3) -c, --count Number of request messages (default: 100) -r, --raw Use raw/TCP ``` #### 2.2.1 Example - Discover LXI devices on available networks ``` $ lxi discover Searching for LXI devices - please wait... Broadcasting on interface lo Broadcasting on interface eth0 Found "RIGOL TECHNOLOGIES,DS1104Z,DS1ZA1234567890,00.04.03.SP2" on address 10.42.1.20 Found "RIGOL TECHNOLOGIES,DP831,DP8F1234567890,00.01.14" on address 10.42.1.67 Broadcasting on interface wlan0 Found 2 devices ``` #### 2.2.2 Example - Send SCPI command to an instrument ``` $ lxi scpi --address 10.42.1.20 "*IDN?" RIGOL TECHNOLOGIES,DS1104Z,DS1ZA1234567890,00.04.03 ``` To dump response to file simply do: ``` $ lxi scpi --address 10.42.1.20 "*IDN?" > response.txt ``` #### 2.2.3 Example - Capture screenshot from a Rigol 1000z series oscilloscope ``` $ lxi screenshot --address 10.42.1.20 --plugin rigol-1000z Saved screenshot image to screenshot_10.42.1.20_2017-11-11_13:45:47.png ``` Or using plugin autodetection simply: ``` $ lxi screenshot --address 10.42.1.20 Loaded rigol-1000 screenshot plugin Saved screenshot image to screenshot_10.42.1.20_2017-11-11_13:46:02.png ``` #### 2.2.4 Example - Capture screenshot and convert it to any image format By default the format of the captured screenshot image is dictated by which screenshot plugin and instrument are in play. However, it is possible to write the screenshot image to stdout and pipe it directly to other tools for image processing. For example, use ImageMagicks convert tool to automatically convert screenshot image to JPG: ``` $ lxi screenshot --address 10.42.1.20 - | convert - screenshot.jpg ``` #### 2.2.5 Example - List available screenshot plugins ``` $ lxi screenshot --list Name Description keysight-ivx Keysight InfiniVision 2000X/3000X series oscilloscope rigol-1000z Rigol DS/MSO 1000z series oscilloscope rigol-2000 Rigol DS/MSO 2000 series oscilloscope rigol-dg4000 Rigol DG 4000 series function generator rigol-dm3000 Rigol DM 3000 series digital multimeter rigol-dp800 Rigol DP 800 series power supply rigol-dsa Rigol DSA 700/800 series spectrum analyzer rs-hmo1000 Rohde & Schwarz HMO 1000 series oscilloscope siglent-sdm3000 Siglent SDM 3000/3000X series digital multimeter siglent-sdg Siglent SDG 1000X/2000X/6000X series waveform generator siglent-sds Siglent SDS 1000X/2000X series oscilloscope siglent-ssa3000x Siglent SSA 3000X series spectrum analyzer tektronix-2000 Tektronix DPO/MSO 2000 series oscilloscope (experimental) ``` #### 2.2.6 Example - Benchmark instrument request/response performance ``` $ lxi benchmark --address 10.42.1.20 Benchmarking by sending 100 ID requests. Please wait... Result: 24.7 requests/second ``` ## 3. Installation The latest release version can be downloaded from https://lxi-tools.github.io ### 3.1 Installation using release tarball Install steps: ``` $ ./configure $ make $ make install ``` Note: lxi-tools depends on liblxi so you need to go install liblxi first. ### 3.2 Installation using package lxi-tools comes prepackaged for various GNU/Linux distributions. Visit https://lxi-tools.github.io to see list of supported distributions. ## 4. Tested instruments The commandline lxi tool is tested to work successfully with the following LXI compatible instruments: | Instrument | Working features | |-----------------------------------|----------------------------| | Keysight Technologies AWG 33612A | (discover+scpi+screenshot) | | Keysight Technologies DMM 34461A | (discover+scpi+screenshot) | | Keysight Technologies MSO-X 3024T | (discover+scpi+screenshot) | | Rigol Technologies DG4062 | (discover+scpi+screenshot) | | Rigol Technologies DG4102 | (discover+scpi+screenshot) | | Rigol Technologies DG4162 | (discover+scpi+screenshot) | | Rigol Technologies DP831 | (discover+scpi+screenshot) | | Rigol Technologies DP832 | (discover+scpi+screenshot) | | Rigol Technologies DM3068 | (discover+scpi+screenshot) | | Rigol Technologies DS1104Z | (discover+scpi+screenshot) | | Rigol Technologies DS2302 | (discover+scpi+screenshot) | | Rigol Technologies DSA815 | (discover+scpi+screenshot) | | Rigol Technologies MSO1104Z | (discover+scpi+screenshot) | | Rigol Technologies MSO2302A | (discover+scpi+screenshot) | | Rohde & Schwarz HMC 8012 | (discover+scpi+screenshot) | | Rohde & Schwarz HMC 8043 | (discover+scpi+screenshot) | | Rohde & Schwarz HMO 1202 | (discover+scpi+screenshot) | | Rohde & Schwarz HMO 3054 | (scpi+screenshot) | | Rohde & Schwarz RTB 2004 | (discover+scpi+screenshot) | | Siglent Technologies SDG1032X | (discover+scpi+screenshot) | | Siglent Technologies SDG2122X | (discover+scpi+screenshot) | | Siglent Technologies SDG6052 | (discover+scpi+screenshot) | | Siglent Technologies SDS1202X-E | (discover+scpi+screenshot) | | Siglent Technologies SDS1204X-E | (discover+scpi+screenshot) | | Siglent Technologies SDS2304X | (discover+scpi+screenshot) | | Siglent Technologies SDM3045X | (discover+scpi+screenshot) | | Siglent Technologies SDM3055 | (discover+scpi+screenshot) | | Siglent Technologies SDM3065X | (discover+scpi+screenshot) | | Siglent Technologies SPD3303X-E | (scpi) | | Siglent Technologies SSA3032X | (discover+scpi+screenshot) | Note: Feel free to add your instrument(s) to the list via GitHub pull request or simply create a GitHub issue reporting your instrument(s) and which features work. ## 5. Contributing lxi-tools is open source. If you want to help out with the project please join in. All contributions (bug reports, code, doc, ideas, etc.) are welcome. Please use the github issue tracker and pull request features. Also, if you find this free open source software useful please consider making a donation: [![Donate](https://www.paypal.com/en_US/i/btn/x-click-but21.gif)](https://www.paypal.me/lundmar) ## 6. Website Visit https://lxi-tools.github.io ## 7. License This code is released under BSD-3, commonly known as the 3-clause (or "modified") BSD license. ## 8. Authors Created and maintained by Martin Lund \ See the AUTHORS file for full list of authors. ## 9. Sponsors A thank you to the following sponsors that have donated test equipment to support the lxi-tools open source effort: * KIKUSUI Electronics Corp. lxi-tools-1.21/autogen.sh000077500000000000000000000000401332035063200153570ustar00rootroot00000000000000autoreconf --force -v --install lxi-tools-1.21/configure.ac000066400000000000000000000050341332035063200156540ustar00rootroot00000000000000AC_PREREQ([2.68]) AC_INIT([lxi-tools], [1.21], [], [lxi-tools], [https://lxi-tools.github.io]) AC_CONFIG_HEADERS([src/include/config.h]) AM_INIT_AUTOMAKE([1.11 foreign dist-xz no-dist-gzip subdir-objects -Wall -Werror]) AM_SILENT_RULES([yes]) AM_PROG_AR AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL LT_INIT AC_LANG([C]) PKG_PROG_PKG_CONFIG AC_CHECK_LIB([readline], [readline], [], [AC_MSG_ERROR(libreadline not found)]) AC_CHECK_LIB([lxi], [lxi_connect], [], [AC_MSG_ERROR(liblxi not found)]) # Check for Lua 5.1 or newer lua_found=0 PKG_CHECK_MODULES([lua], [lua5.3],[lua_found=1],[x=0]) PKG_CHECK_MODULES([lua], [lua5.2],[lua_found=1],[x=0]) PKG_CHECK_MODULES([lua], [lua5.1],[lua_found=1],[x=0]) PKG_CHECK_MODULES([lua], [lua >= 5.1],[lua_found=1],[x=0]) if test $lua_found == 0 then AC_MSG_ERROR([lua not found]) fi # Handle bash completion AC_ARG_WITH([bash-completion-dir], AS_HELP_STRING([--with-bash-completion-dir[=PATH]], [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]), [], [with_bash_completion_dir=yes]) if test "x$with_bash_completion_dir" = "xyes"; then BASH_COMPLETION_DIR="${datadir}/bash-completion/completions" else BASH_COMPLETION_DIR="$with_bash_completion_dir" fi AC_SUBST([BASH_COMPLETION_DIR]) AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"]) # Handle GUI option AC_ARG_ENABLE([lxi-gui], AS_HELP_STRING([--enable-lxi-gui], [Enable lxi-gui])) AS_IF([test "x$enable_lxi_gui" = "xyes"], [ if ! `$PKG_CONFIG Qt5Core`; then AC_MSG_ERROR([Qt5Core is required.]) fi if ! `$PKG_CONFIG Qt5Gui`; then AC_MSG_ERROR([Qt5Gui is required.]) fi if ! `$PKG_CONFIG Qt5Widgets`; then AC_MSG_ERROR([Qt5Widgets is required.]) fi if ! `$PKG_CONFIG Qt5Charts`; then AC_MSG_ERROR([Qt5Charts is required.]) fi AC_CHECK_PROGS(MOC, [moc-qt5 moc]) AC_CHECK_PROGS(UIC, [uic-qt5 uic]) AC_CHECK_PROGS(RCC, [rcc]) AC_CHECK_PROGS(QMAKE, [qmake-qt5 qmake]) AC_CHECK_PROGS(QTCHOOSER, [qtchooser]) if test -z "$QMAKE" || test -z "$MOC" || test -z "$UIC" || test -z "$RCC"; then AC_MSG_ERROR([Qt utility programs qmake, qtchooser, moc, uic, and rcc not found]) fi AC_ARG_VAR([QMAKE_ARGUMENTS],[arguments to pass on to Qt qmake]) lxi_gui=yes ]) AM_CONDITIONAL([LXI_GUI], [test "$lxi_gui" = yes]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([src/Makefile]) AC_CONFIG_FILES([src/gui/lxi-gui/Makefile]) AC_CONFIG_FILES([man/Makefile]) AC_OUTPUT lxi-tools-1.21/images/000077500000000000000000000000001332035063200146315ustar00rootroot00000000000000lxi-tools-1.21/images/lxi-gui-beta.png000066400000000000000000002577771332035063200176570ustar00rootroot00000000000000PNG  IHDRp>[bKGD pHYs  tIME&(¢* IDATxw|TOZv){Ȗ=EDEQ(EODQPTA"(Cl]wJ-P+~^y$&s(dwʱMF'B!B!.8էgdn=t(v=] CFFB!B\ NUzURR7\# `kQ >iRZB!Bq L&qO+5 VCu))!B!t]GU5FV9nltMw^*%$B! Έh 38먪&=oB!Bq qz&rϛB!ruv{ M&#VÆfu `uM7! w<9 r]BSIMDu*PbEBBBHOOC>r%S!,4xQ3:-cCR1!vP kqt=8׭t B+x5R5*/8:eh3TMFd/.K!.AnԴ *WD PRزy $,,|AT1'Prvx5%PJiV͸ϴbB!>IH!O`-^r28r,Ԍ<gtrȇ kr OxMG8XCV2mg>8V nI*&~٢h ,!gQb,*T@U}~SB3H8@xx_ǘ@zDY<.eaWz &e_$~B+^߅u$wlf4ΰA4ӛ}Zo9h$,ɜ:>AAT|j~B(:,2sfe!o@C.> F3"Xy%5 `bCmg,!¸=^Сf깽f=R0T~yYtܩ#mh5 56zu2Yҩ%ë-1X~'8N!BݬMk<@& -;_hn!v0;J?i:F}yg=>EA T,DͰ.P=L^^8/V' bD%+U's))MR6rQtg؈6ԯѝE?78-+mrt̾1Y/&⑦v~ cл_wے7od~S,dr]<ұqvpgt+cx<}46gYS{}(0jpٖ%W<Q躆"HKMl6w~E{JʯYEan6fd2r6^7;vۉ^:,,N6M2R!`%L5a1-gI9[>=4XtҲ} 5tk$co+K@:tGՀIL) '*w)%MЩ.hcoM%yDqgk(a8RO+7Jm<50z^=ԕM}%̕RxS-S6I|aGϰҤ !^s};pEFpx6ڶm]U~-Rky'ҼP"Orys]D[-LQXA&{Z+B\%mXWǮ BA#ylfPJn+Ω }nl-xKyz*N;e}0&[7@ß>bCϒf)AhD[t&?(&;,n\Og 1 SNG 鋽}0^t/V fr]d96+vjQOayc.{=+',9?=&]q5P5 vaGUmXiiE?/)7dLV埼޶H⢉TbU3** !@TNg AU5l'Y3cULa׋^ z"$Bʯ}@=9_NgCy&6!zbc2ɴ[.,>;WF6qA[{Am N/y{1 Nl+ji|JhFz2< M zVVov0U~Ϝ5B+|NDg˯0mi;ͨiX!,BW|Nb"WA wڹ7yF hiIGQr73)G>^gDAC}aw Vj6/'L4'w3k}NT娘Fµ/SA"VҲ4 f?MB` ;;?++䃟JϟRhb?4HGK~{q}LAHqW8! Skyov& ,kg{/CC|OwgA cAC(1^y[Q?>oD_w-uL z=li hh*8>CqeTϫ>Gi03M[` r1F'A=o-CEblq8WB\5L&#II)8mڴy''`6Oa%Whg JoEƖ"?ZyR(LSxr؉dًW͕N!HX@\ߗOos0yx-s|ug|f#nM~k0;RG7qdeԾyƃ6Lxطh5Μ`Qs8AEwr/>[uĖDjȊH)ŗnmw:T!}|GȬ-ݡ5ogaѱ^yL25iQ#CN;|fU B QvdY%W&1áCvZScvy1S0<$q'Â`-X+_M,ljz0b(_Nڥn0d/B+FnF69%݇qZUr\(&C "-Ӄ 9@Y^"íXy#<7.>MG1l8mb/>[%;׍U5t=Efm/ÉٝCK``ƀ'; bK7` tMCq%'n*GQ㸷#߼I;h(dh5#,B+e4sr)W.IOO'!8N@I~ٹFG_- `v TN8!qei95tB&pfenB*.B?B!. ݊~7& !1 Xg:RO^-B! yB!B\6N!B!$B!BpoB!BqRҒIB!B\pB!B!B!B B!B8!B! !B!N!B!$B!B!B!B B!B8!B!钉$&1R@A1Wq*]蚊hu#BqZ4ovpb`2܄t\(a˾/R Ny¤Ko*e{-pL2t򒱮vA}tؖus5]w(֋#:21՛s!]AhQ1n/X]+neӒ]g9}d8-k8}D|gs5m1 ~ߖnN]ˮ1ePo0n}o٪/WZLywRͪC<9)}QPOh!Is~0YҨD56M"3ԯiM{bۡ #ahKx_(duȰI!. ΈlyI' z[ ƂI[kln`Ђygi ,";9$8@qj-ljb(ވjq=b{ii}*yVzfj葥ln)\ݟr)\_Uooj `͗p!.LuaHucЇ-5V+AA uOYC :7ŪcO4$911 c cZf"O *ȫ N/Җ3] 'k,2L=Pqĩq ЦzlH]vU.ܖj&+x $_3Uvu`tA_.nȗf]Dr #kG*^]HR / k*I*zNJ&OQ^S.PEF]'oD\~mۙwf_^{`KwdZ3``AB\If+_VZ.d*!n%~gmeMQDTD ֐PᐛZV1zqYރ0cq Zi.iױws+˛y ?b@W $!\ >|e{M8I9WdҨm};mz|>Vªj*NHB7Pyl FO3Xp@˟Ғ[:%2?Ԁ|:k \AxeAus'~UYg%ѰdĠp)?7!4tM. QG??]hѵT6|2'"K3Tv?uM:J -7j}ћ>wJ΀gհ[vzZҦm,;-Yu q2s2_!s̍58R6o&{&=Ll׿Ɇn>e8n1c0LݘPn:EG~0-wZ QE. w]q70w!~!{v ȌpBC=#tt%n/˶)hZ4mCRg)sg&qCAd"rt>c'XkW_A%-yhd$ES޵ERB!D2ܹx)+ڵkiѼϻhHi q3'B~I{;`4S')#鼂8iZB28[pWWx]=q.;>E&[ E!S-d/R/sn+wdz*S3w+=ɃT@?]~ MULܷ0hn|x7|dC1̓$~DG-ac>_ngb@n^PAST'/xE<[*ذS~MXy2`4;P}0scuԳуtj'\);45%T@e'i]a+j\$׏-lYOOn[b#`>ܐIɿNѡ̘O)I[&&#jF( 7(K䚘@ b+ΰ'W+5Ss} [-4gro^W !Vn$'m6FGnOLL%|`=>}5S39|=oj;<=<2p(.;/BQP5y;17Z=O6e{S8iO7?j]/v_)\&suuV /Ln]nm.*gEVWZA;!}w_>~6g~-:Ύe1(7FuqkXoq4s G29^&–B֌Q2^ -'޷w#/vؙ'|Lҋ5Wk)ҊZ{L'>yP- jY*+1z#q#? O8Ni06,{/$ۃ\Їwȫ<S7^{]HܫIIqϽP|ݯT~.,؟y-ep"c3NI>[5[20|oGsܚ4~ϭ,Jt{v^:ދ/vUj,ĀwwZbzX{1Fzeg2i+|6Ng|ܿ&~TU Frה{|7aqngzfq,}_gѧc˯J8!ĕB10,Th=5 {xx1b;ú֡\gxUz}$L@&>~Ğ[ Ҏaӧ3(w cgԀjfaY0*`m\XKXm۰ZoWyf4vAAPL}?L{.=S!Ǹ;]^ZrUF8s{=?`ćZ֠ۘϘ}'|Cji߯/MX'f`RuY8p 98"V3,&̶@kWygu7EB۹[uBVbi}\W12k=F8Nkna цaowƨ:m րq Y̅'{C39"iC@ fExx& ]F)xM*+2~KY<FwQ8a;9oVkH]Ga^Lغ ^;]3wilT=lin'.85mlQ͎Z4c)Bkj}:K^dqT*us'`7U" z7[ LVI9G.\Uɷ?nZ-DY4 LC sIѺ~[߮->=mƟkҀs^g*GT&.ąRB >Dސϗ^cʸ6Un}:u( ?+ W7A1`/ߖYw1 ;W#-9%<)+V.Gr(WfҒIEVEDEQt#6"SثoDs#Y9J.{,yanB^D='wQVHBRHvRmwVEeNJ4}-,zyjFU->ʹ\4GJ' XIOcd&T @!Wss,reZ鴓NqL8v=s爵7RwsεtGNrsAN"G?ⷛ&3N5U]8'/r)NnsHy,}ZGU /߈oJʉmӣ(_.fLJ/vtoB<<|)-^~s>pKK#{~47i=;;C'#m>H7;+̡ԶES8\rpTP8- /3rnyy߶`f}@ٹEKv9izDf/̡d|؎]a̴̒Mme4>}D@Vw1<>}j\zs=ߞˈ/F:Mf✡aգ2Q$= W<`Btb6e7Si%P; &SedmJU:YGja㛷sW`s\WZq%d1y'?`zzX}{*l TlD\di?^^?$tnE`Y3 P0F`Hk)ᩏq C*%7l!t^}d/9[{Y:E7ZOcɼe(m;Ӥ ~_Hdl /3P<| ׺?X,_o64:m%L5C=b9u;2 U[^x-Y:UcSW\rT>Y_3j3 dzϧ2dTz&gnM۷;ѣfK c:rd^oމv]*%/H}9Φr+õדb ?ďӏumۉ^2=e+[WxyZMQ#ƶN<EttayExp oÈq %Ky2ɍ[Ӵ {\v}M ,dǎvPF Fc 5zKwr/;p*|v\.$BGqT{7xWJsxLm0ḷlYc 㚳fdoz.YV!& rr|x.\A=[T/nW|hcL(/>&97c[6TcTإDss#0!ze {ݸvhb?~go-Y:^人xuKrZ9jo ӟykvœo|543 Ä"W45TՇϝMҞ~F2>(:^ė|07c~ʻ%Io] .rˍǗts>]kF/_Gg@Lqnμ7?2ȗ<4`Nct#Sw/=7K5pӏ~@ƼU%w]_⋽yR~ӷo{{![T|ȤᶻcC ye468K^ >c޿ z7 _;L')ya$4s 9ʢS]g:k^;ݩ^~{wo}Ixl6mQO$<}˷ 93#0zۗP8qJsv]Ld ~-k=q OpwO4oS Svvj\=*:`՛I8ׇ.ݛ`D9p:Er ~Hn p1L_zrgOGX]z^WN!ąԕGO}S7a7-3P^LTK ݈{o`Uxo m7u|H،ITsluf$xfˇyO<S^71ᶌܟ%: d39*]!1?KJO+gΝ_E"-[x}; }*ri> Qsϧ|YpSm'ݙv{ SRs'B! B!B8!B! !B!pB!B!$B!B!B!BH'B!qU{n2s7`/mOHY4JQ!o[Yi_[b$lyMq=v)r=]*l?jA1nM+ߵ[ɿtͯtAVKΥ~nt dN.s̜*ʇ2exb˅@"k,|4N]+7We|dٛkζٺ\߬}\:kdQ4nHz֯ͷՒ~A2e!awc^7"#V]`ܳ. !BHwmsLkW}Yek_be{H9e9lfߟspr5_^HlyF+O( V˱lp'$U❐sɇ{XqX{g#FUPJ7c.JÕHg|_GG+#躎Pb&K:y'P%Tƴ85hT ̌Ǻ-yyM %,]+-ڌ'=?ea)e`i|Ha܍L>X?66XyulaviAzt7uu/废 ޒͧeps"; />,@U6F۷'|,!k7ӏߐFu(Gpzv6 Ve7> ބGJ@3 .ϢL'K-qpk.>_OOhVЈiiEZ *3y8{?)h  C^Dl\ש.Cfݼ߃V:BLJ<6rRә|Ni޼#k8v8o,=o:ZiC(XƊVQ?H#r?{^E;skz!=!"RDXֆe]];kö P#BOOH=7̼2I$y-y IDATGa3gΝ{~sڧ"ve$ 55uP:Ҭz|7&WIŖVҐk2 1碇vk 09 t |1ŭB (=(=Wt^85K:+osW!W<ۄPn McTs'1>PGr QE_6 8:i)HAd@Jö[|$ "k6IM"=FʀlŽ$ 8!p{q[~ǡ: @9>;X5}y ixl3nj; q]_qP[P }ECtŝ}jq66T4kevUCQ VZapX)NEV[Wk`^gìBiuw6x{˖o=cYEՆ[~jX7}D8n=t5"<{XT wL 5uj#qص\*=09@h&0+P7F 1S8$!-O^ S)^~Ly1xg~܏zG?^\a|\צ#DP5Ak\j/F~MUrJpXA>Bh`@ Ҋa:i&ʈ5BsS"OjjŎm=(_NE b#Kq}Km2~jpCL.F||k$ې_kN68 ~ Ҝ\,:6i' { @Brf5L>SOQt0? IV?_^#@k\rXfAj^μ&rN1NR6ga1>'feҏ`y'&k.ȟǻ#) [q{>Nbe,ɰ 3C#5T4p7?id j nR|'`:Ro`ԋ>xt ^f(KwM ~Imhu(M9XL17u5v3j ɘ>ܳW´k'N̨ 3!<9 [n8pꁌiXX˲`G1CIw tN|Aqų=pxk~TFN%Obڏ%p'Hu5sgyðp(H5xI<(%~ax:0C(ްfW)9ݦxzXĆ-X ڭz#w8PXm8꓍,G|̕XkлᆉqxyrN|sy9iQUcA52ܚh/ てQ\Nm8 $ɰjDtxAWCq0%Qz[k[P!},@wN \]n8ۓٌ9k'hUYV-|Vv}pB|)7&܌\AƆQ?7ތsptltϹnNN -F X[ @t~#pg;{Gyb TC0XG'}gI4x?*u kq8Q9_p1ǜk͑0,1vt,Ԡ1\eߒ2>+FS %#CF9Agf d<1[ƛ%xH<7lr1P e5frWM15 !_nɕU\K0rgDS$~MZUll-Yln4(\m뫱pa,׻V`uTگ慇p l2Yx:t?62ARM V zaPK]Fl̇V^#B{~); d JŐI$JRV]n~΢\@<<\"Al:Q3}{;G90.L\_UlTa!3&Gp{ߘ ] iy 8yaz"zZS"=Čh$fo`6~ @9=4γJ3Gx"P+/]BFjA|0C(hJ;"٦zBZAa"o^@*(ǞNPQѰC4y7^Q4-Vv>p  ue\[~X?YD}E%]N+s ŬR8TJY@̈XzFvٽ[>\ORo˞(vD w i5HQUx)(Ƹ>S5gKbUӝXjuoeꋹNzvU qDOIx&cvkq9 ko⏠-[Y4+RШ&+U6YE|!zd #Dt?J0b0;JW ۙ@FSg $8&+ ;(WIV?t S:K$ G23 wLقj}xuo=DWITO7%5 9 &G@Pt5g++kN2v(5&N3_\( 0*MUyM\Uԙ,ٓV4xQx_TKb{˶nZ3V<<.z]([g Iظg15Kú=u(<]TM{PP^} VR$FbmS).ckѻ/*S{e@^Оհv1lw3˷ _WgCt"j@DDDDP Q=a}vgRr;dិC]"+wgNe$PCp,{ԕݜ u#UMd+vz\z^f5Lf״aײ/GEI4s9tW:W g\u{܅淧/-RSNf_dˍ86;Lju ߛ nza1RsKU= h޵7(SMy:}"@DDDDt= =B`` 7&"""" 2Q~Gn%ˠdFjni0]""""riyeZrƾfܠ(z.[G2 a$u8%""""G @^=pz5|(îH)&u2؟zbYψ8;,`(~<9ŕz.@z_wV0""""r P?o-]Nz7""""r`KahkR&ג2,r˕EŊYŪ^^""""rd6Uv<7 kUQG3 aTM pG2 ذVY~i5n*kM'j.U&2ϒ=DDDD`=֎7WM3_SE4XSnFoDyIDDDDDݰvs6 *!+jW")K{S{싂1O"""""rNuC6eYCĆi]&O/%aI8Yz=#""""rb A!Y!^[ [KiSؒqq" u8tJDDDD.ɬ*2|0^Ľ%/y 0ʀ%)K-rMӱh TgFbB|""""" q"R-.Ϸr_Q|bT4""""" nX V ܒ*\ U z=)vmUBd_po""""rNdNWy>Y]׀JTMW`s{,ח.- 5fU7""""r.%5 x75כ X p(Uq1*:z6ϒimz{ϓpijcU[Nza~|p)4H7}]SFwc#1_~t"i""""" TnX kK6qxiM_olwie&/`oDDDDp,HJWas%mIzbٙͮyX QK6BvaE[VaʫUM+Pp娨Qwڰ>U_\^o^X-,uY6l{ڽo/ p\D*\ESwN/7'\[=z_d}/r=ӈ"ȡ8{F4~=W5!Rt: ą*:^{Lf+_۽(Qǧ9Vv:ojXH cDs_ =(s=SwS$"""v̫=b27$& mgeY`^*2~@92Rk2~ O=((Fayi&r""""r. E6SsKoukig_^K7R=Ow^=tE??s\c&斪n{@}U;ڣT{X[Wվx_ݏ7nV=?݇w( (bލW,XNL^^A}_qߣoV#ODW*\7{ifzk9W DX`ԛUnʉ 񆛳O""""rPzQ߶G3 1oaI%< ^W'uVGoj˪Ś=vxQnO9\ӇP&Fmzsw6໗f#@^omqǔ!qS5wێA"u_LҪ:T vu,}~FQ#*U૵KpDDDDp=Vj([ba+KGÕvwO _U|m0[lp)v  lKeہtҝ1k^QL4s+dAUR{/x9CDD 4 IDATDDݰj-{ҰXNV7gV^2,rCiT5 =L|q$9\WU׀ź=)xT? ,zv*{Ń׍^4X$w%AG>\9Tw05,R|fB|틷[[x9UMbqLa.LCDDDDԃIsKTa拋QS^onB|/5ϻ U[,|TKuo!bD/]tZ >gwLjIiX'K(`9f{DAlaKq4{]qĵ?> ZUǒMmL})9%W""""ˋ1;ĈNY{0ytkÇ)>^du 8""""""pDDDDDDUiC&m:wA]ua{N`n[LӏADDD*1#"""""r@b#xyum ͻxȡq%8""""""bGDDDDDQhY]jC W'.ߚz3,VDQM@e &: +yI*sNbLLCz^PgwQ3<10&Ecܠ(8:7)ĶÙq$9%H+E^i5dY>8/7'z8_d LJZ>2˰bVvy)̹m ,xz~,-GPUЮs;0{@~ »oǏ]QN`j^/t$/rx kTK3 _o? ^Ns4<-R=R0O΃Lo>vgVg\dO]@J^ȇF[M:Y|d\„?GNqeӐ$~Bog-6(".vƑB+>i;P0uz 2^nNCuW'I2j͈ VԚ-#"ڔщ"|q9z.F=n4bl8/;;P^](6ƜaE`Vi6OSb#^ܣCipQD@]U]XIѱWqy0xB#$I8MӱHy=.p(`'3 jo学ތY,EEIG'f ǭOGB?67;d)JwO0FW'=5sՠf}xL7j)jhl*運kϗn8j_=3Sǵx\|0yX55&$X;fc@t NDDDD3I+r@!k} )9}nxmXECݜs"G2 /2cNdɏW+kT7V:Wq-jp+Y6U0^'W+|ioMt5bք5a@e|:EDGS%3Ym>):vލ+χgλV-(2}J0/W8nahEǮۓj^Ndb}l̢߆hD838.\ϧ9fdTcÿ=7$ɏV+ZO0I:ȾapÙۊGm(Ո[ݮg2[/9c.`b6IBqEm_&Qu ZzmGq,ܜ _BSGVt\rNI7)JFbp\H:ap4OfrG{T&}bkJDDDDD S; .XҀɠcTEfqʫ^iq޹Z<\I  sVT^Q#zr1k3YqHAÿ#'gaωVp1Ϋt}_{Ss;L&mʯ̓Mp8h쵁F}=u& v+b缅+^6bKR,|QU(m=(:nքp2t|y{W=LԬ5WSi5m U斶_c nLDQ^ؒloYq %RM0;c#q8 9""""bwC_'w\*)>p~ڦV7( ^*צ8{.`206nu+).`bǑ,8,ل@o7̝<fBP!칩1PZC :'+Wt^- ST*Լ9Sa!pAR%jt3󵨩7Lt05OcSZ\/ϋn@hX|jJY~Úo[^qw[]2ͅSlc W'9vw#=cG2 ?JN*Vt\LUT'$d ^F^~};cxB/06xUM79W=W$"""" r+QRY k*^ϐ/`p T֚-"BT00VpNW'z6oT0 ^ \htb$}0n U7S3S:#=7y {L<\ۛlSѧywIFԼ F;t՚cD|&Oay ~c5p>Ί{նP͊ c5/D뤷 ݜ|2p_P[ay Ni9$IZ!7oQDE\@dǑ,|bc;ŠRy nzaZ=&B-\dJ7g}:t+G@(:~bk9gz'{xzѸV7<6ns^sJ0{׊Mp jDxiUO~.XK+ŶÙ786DDDDD)i'Fvx#f/7'L#xU$e֙,e1oCn?@=6|(\ќ5OW#>xZUAM(يwgkSҮjp*ѻzDo>I=rZ2 [=NawտPtއ:1 ˷_mPt0<^ck˜HlIhزzLy?x1k€fRV,\w@R('f}CIe=$ϚWBTCeI3Yee1"fOqZeDv !1|2p]щ62v* ~q`tb.@o7lJ*kq8Ƥ> |ϓp-R^]{ZW CT7ܜ k = ;e@J=ƴt֤ lUd˿қnSuF ox9ܸmS@ Nh>"""")mTq;N9(P6WbůR7" +_ q!xxcfş,(ǂ:\Nm<2Uc}/^%IE5iqU1yXfO""""WSZ_ɠC\j u gӮsH\~{V"6HiTB)Јx?[T% '-)=ʧO?2uNAMՇ]/^]Oa;o$~э|LheH5xo]S>uJ}ij+ Iq8S?w(~~NxADDDDP\Ɍv50&1߻nDžW⽇ر : >s^csmjp٦V֚_)uf@s!3 v8{6 %""""N/P86~޽Xrf+J*k;fB?8 & hqe X_v'w8Mg3FfheG ڴ0JG$6{T7~/XVl=襀/FĔqxHS7*@dPA!v͇N3.Sa#a18ZeKjDb0&17]ڊe/B,Zwxv #0yXNrEQQ~$;dcld> 5IΧP^CFgGH#8zV֢A] pu2 " ^H淬;f!(GnAT7jZyeH/Cu T֚Ոp1 nNHGbt \W]`)vO1|,#-k.kQb 8""""""bGDDDDD8""""""pDDDDDD1#""""""pDDDDDD1#""""""pDDDDDD 1#"""""bGDDDDDD 8""""""bGDDDDD8""""""pDDDDDDZeQ6vO,2"""";ʋnpj%֦!,Ĉ1|'"""v9j" """n{4|%Q $K88"&DDDD]AlVwԒ0"YA԰&xP˂ ""U7A1hpF߻dD3#uJR#ry""RGuU5 ָ9(3x0TUU6jFǂ#r2OJJKKyʎ],K%Q9B&ٸؠdA ̉'P[Wצϸ8;#>>;p`4@kpfp6K Tdµ5~S)ں:Ķ3))=!lVW'[ ,;Ai.l8ʰYL,""*)8tLsfctNވ2 H^!JG#Y02dنS?>iz#Q#1(?W?}S1rp"n46?/m+CF□ >CR޵v\t*)Azr2MEr~ ̖jf!I(A@T 0ǦEQUT&.Z>nzhnpsU;ɒaVD=b,qI""7F ~$_*NglY_[e*G`ϼi,Cr܆Լ\X6܃W7~@ ȟ[²{PQFrr y\$6WLXeCO LJy.̓ԓ8ͨ8?+] cP$'De-mh7TWwguu; /|rV. Sr +PUg,bGfnKCHV $+DDdN ~i*7قuo?)#6h&^YlCsxH8 ZGƞď`W{ H ]cN_7x ,CK_Wq`ohea0$B:GH_-DayM%PvOWxzY#@йY oA4݀SCYY܃D'9'_DGPܬ 9 ;P 6 rUJ*8t:+S%)%33lz>̽Iza|m: *!(oblM+MԞ}#ȍl(("0 Ҝ.Q٬~w]44Мm Z=A[ͺl&^0OߒS(' !.^ 9,6">Cf vB'2, u]A䃉o, uX; t gc{h!pZ\py^X[]7DE4c8j 4e|'`Mf.lrCd(ϟ@>K #/_((E3&hu+Ap$-YTǹ3D/zuώZ -G](1" _]0 Bkgaɨʰʐc g jQS`8 2,?5EY;/¤qRXd+j`o; zv"jCyA+5wGSkh|৩BQl l.E^Y--6HK}%08(HZu,Zh9n6 WC IDATQgGp$;|tßb^K'?y+}a&=X bWA֏Ƽ;Ga_1dDbLN}6O6ϕp ??lO z;-7!H huprqCH7̈́^n(Ϊ㯴ʊQ$CH?C 8_!D).JY`3s+""O[Ν3s3gQJ! DrړOVǮAbFVW"A'㻤e Zx K(;$e.SJ߶Ͽ.V@ 0Ff& ߰y o3`JמS Kvs&qV6IlqnD]Y43>Jߓ ?cE> Ɲ R 0{7 :~D܂8Ǭ\~˜uZSbdSLM%qsr#cBg ĻS'йa]ʖ&m ϕyȗ/Շ{ f]_p5NܞTh _u@cz7+>rU/9͕{{V?j?Od_6=*pӬvI[F  8Í3sګ8Rq;#aʟYd~L"^ȨZWT(Ujӷc2ϟs= *G`XpѨd7\O P^ńb/gT]W9{)a'"gPMӍx,I׶0L&l' 0D-o߰h Q9s~?]4S5#'7&F \uߛz< ;7֒>V3t/IXd˘$g+/\Vd1Ts3|n*(_ 8JJLVpKѹ)KСN%Jzq,?o3\A>ghIZ]8)41u+Sty*֝Y,5#%b5 "Te^wd',E޳cl{-CنXp2 4(+nvFnnB&5 ,SRA-7 Iy0? ]F<uYQSj]$*g_*5{̋? cdNI0a2r,JM(z;swg1M-ʖ.K`lu[vۂe)Y-F&'z4lPQ ~:;;'3l4ќz&W~gP JVAoCރ{'֬D|{Wm"Rl٫}W߭aڭégGfЏHȈ^s# `c߷iTAw&;nC~P%W]V~ zn6UÅ܈*R"G,T+y](";l&eBh\mrͶ.suj6 j;*buf6J`JViB7Hɘu;5k֦lNprݎ gf>l\gVL`g9^䰑hkb3V6bO}|ҥ~"km8YW1s]ʖ.KZQǎTi a=eTw9~`\Me”Bؾ3x7mE|=2/iy4Ŋr&T7lankiy*v`si9oK)UEͰї \Ϳsvh }ޜ!k \YGE*<bdHB6Ĉ ^+&P?~Rɉ91dI6TB ;BQ#_Dҭ=sLl^?{)$t&v."/146^cf=¡4<)b-o ]ǩz&mn{ݙu; r& g9Gw`Ɓ MI8-; w\{el_PoNq:! x&î ЦV? 1{W ʹ8ŝ x/> 8z4{gYYio8Fh$m۞尗pEɛ8{0yD&,Nj-1׍1:Nępcú^/bv,C.RC{ul>δ*sS٩A1+0` ҉뱎AÖpIv6Žɟ&VΏ?q[ܨ7b!{NDr~w45E.y);sWyv~~8 ٽKU 8k? Լk$0]%?fHά'׎u[W*YFxx_](=GD>w6\ewd /_ǎK0+W?l,wb annfA{<]2/K5ЕIߞoj+vؚ|#'O࣌  '>#=y͉j?jdKM)S@ =5dꊹtw`4R>cUSHJWwGdBORJVjw)mjb_ɡ<]&g E[:s{ eYؘ8{`n徨)&7oG WH  ٤ \!;t2 *V=_v+0T8pAj+ZPP| 3Tǯ:LJpN_YႻb$[[ki`L 6|In2*M,닿ef<L)l]Qʉ*}6k+vmлgEz4,sSt`Jbۊ;wFu厙KδU(]өN_ΘJO>lYk6/5e½~k.p&(߁&e](8Ẁz"EG3N1_g?e?t#S8iɩS1Ov]{Mϕ n 4F_FxLlS[ X"vƒs"39l$$+7kMa浜5JaGSd'kix sНXƌ y nu38u^ƱBѡ/oоQVt3,$vF?Cq$?G47>zo[QEگ%m'ql&@Y ]I-vwj}gbʣe] PᵚX?õ1 'yqrng\2^{wb'WzcFl_SFS}<}"'O Ҡ/O_BoJdÔ!MH؍@WVu,ѩzN%Z3{Y)l[-ؽj 񦻺»@.gBN{a[НXǺ Jslgd?"@ o)ֈA_5b}Ec0t/Vv 0R!5GvNX:d=3LI64ܛZfpꪉ`oR]#Iϓ%ǾYb$[W\m7gaFc2w*=wd:4sG؛I~ܡTY6 c|, %$}|PzedW\ٝJr2%đm^ H'U눫9V JӮ]i ܓfŘH\"xzq&D#ؘnzo턵ެ5*:o܎Vn&4odK@AӎIv8RD\Oﻃ O/<ϸ9ƴXoAW 1HޱFnl מYӅ}]lㅷSq &s{(E++Bڡ^@𠱼0"۳VqLپ9tCqpg>*?ɢI3Xv[Z27lP-@MN=qp:Kш.G]iݳX0[d:2ngܛ2T cNeoIZ\ݽњعyjD4`OryTV_”sδ\qFs喞F)q |tq"T}{wnǑ;\V Oq_^ a ѽ H^vvt;5G{k_~4}VD!8Cߩ,PbӀv&~Wk6y=f#K~~ E}s,n`cN_5U|'kggb{20ЇA~ ()޶~ԵI3Q-]7ܞΞ[vL$$yJ=љ2r6c_]}[-TY=#?yljm;رuoZ&׎hLy? 3-f?C-[rH.8np-6,z\\4ur0ioږfO0HR<O9k~2 '݋݈䯯& 3Rs2TRq%ʝ涞/g":l.g8H8wݭQ$'\S뭫s3\aJ?M'4XIbc6+}~;s.V-é5un';3oU+3䃦ƩH%jp-oxDTKEHj;g|JUXk*N:koV^ѭ+ vMz2ȭ{t`?vt.ړ}AL*pjPWd|jQR \Ȯ_Ӎ |ڮ*25'[W'f(A՞x:P|~{5dҿ G u9l31Iu Y0ӧiW}RMNQh*ߧèLЊZϧ)=AM }s:\θp*MmJ$.̘I3p(ѐAQ_46puj|i}(rsӋZ eTGMuq *JĪt{}ۏJ@Y&!sYZ3뙣;U|HFY<_.+B؃RGPj5W(JvA]_;Lb4jԸםTǏ{5ګ@SNshgX-iߵzjY2'KL IDATylv/N?9˸ݠ UhUBu@ ^ >H52wj $Q4@ W1?$W_xQ@ {  B&a^S(s!;(wQNWn zWEi EjQ!;Q@y(۶xe^\PоCT3۷EQ@ ѴIǾg:vꔔ^XA׫WBz@  !W?ODZ ZF @ 3 Qj@ @P8@ @ N @ €@ a @ @p@ @ @ @ N @ €@ 0@ @p@ @ xyDzԨUWsĨ"G6ѤF)#>* ApF P*<ؿo?E)Y$)))=rl&@Kx$&&rMٲex>..̌L/4͛7yѥj pŖυ_FW!Q@ 'xhݶ5={D|} oy6GRA=GGLJ>'vaL箝8~C 87w7+U|5a\~N]:Y ,Hđ<5j҈7`m&hINJ~'쬣CX:q3׶m֯dxM71rG89נa"%KfgE+U*%wlm t{3ݸeގn'a\puRB:cqJNv>O69>$*0֥[oVZ IǎxOtlΜ> IpRc.0|vcBhij4{)s4hl;[Jw{_~%߹VmZqiE]s|̸1ןy6^ϦЮC;U#GY橊OV:G^۬E3կgsNv{:tފ}&11WWs|L<Ҷ~7^Jޣ;̜>S("@ xBsݹyhKlXT5 TNvg̞Gu%akk@3n:ڼqbyzPABA4?P̒u[ϊ^$ע9sFE2T‘' TTW#8.d=榣L`0'xA *:43NwAfF&~[DXhc |1b$0c E`0Xf,ݍ˩_>Ј[p_ @zuywCOoޞF#_)м!J\[7? P(4nVn9^ݓ'w瓙a^K7p@233֣_Nbl}=@zzxuiڼ)۶l{:ၝc][HքȸQFԭ_0(/o/5n8+Ww;,&!><۬0 =%,yrrttdQ̚1K(#@ xLܵ-Ej Vztj#'hE]p=Ǐ9/1dXqqVxxh (>dYx6J`N7 OJYf22 01Y+.].2jX+rdpގw_'!A gkkJգ+:ll  NbA?==K>rqH<<0€FTBR% &>z ѫO/njюm;8y.vŵkT j9.IǏ `x"nHf\pĤ'z51BHOK'662eXs{UUF.pvqs.du}73@P#`<ő3%J董ܱvY9{,ID_S~7p^^U KC  9{wsMIfy֎%ҹv͚ %o[hlpuHcnƉ5R8l^C]v2߷ׅ ذm]'osT4%IIj:5k%~ާaDvtEpJh\<Ǐ9Jp<mW^,yФY#G_6qٸۻەZ-Μgz\^A>75sS-$paM&\3Ժm:wSUKB0Czz#4._L@YYY܎ͷ|˘c~_Ng8:gF̈́0!;'Vk.xiii(UJayóYp7>gw]&̄cͿ ڠj6{L&Ξ9k} x? LGr(PW\-0jbvvv߼q3魘[ONף?t ߭[ҽrJQQQDEE KƆ>>p7l$@n >r_}MFFQ#pv6r<˴Ўի[~oXa}OOa  3#Sh06F!g@ՂwM1L+c{`0 3 3M65 nB ](t3}t~<ǾcFy4f7 ϕWgߢU RSx,F Gc?",4dlILLhgggV._ie떤y6lޢ0JlqrquP[ץDRbh/d ؠ$I89˰ OBJ9ܱ.>V+llD;Pȸ)Y$'Ow ˔ۻnjÜYs/7nܰmeeE%JYGo>˹S2dn`O2)]4J]w~B2)U@3pْ!egee?A 9ZL&;L&2eZ^=y2:Ã8DB!B g=cǎu5CGPREڵo# 7_s_}sˏBW~F2;w*UDIIIa]̟72@ҭ{7ʔ-{Yn%]р]uXbF6={EGRpG$IH2_ I+\~r/C)b;ߎG$(ЅBƅWwsw[[[9Ʉ / 66-[Шq|ܿIVz=3IXh IA2vlݲ(J.^ȯ 5Yn=䉓RJe7zJ-N0|g0^Fَ+ 4Vdge ?T0t .IlhsMjJleT)QUv"tq!Յc4Ypŋg|1I&q(>@v$vlg̯g?RRE^o:ii?-6ϝAU<IFFOr,ZVGwN+X'-wʕL:  Yw fke?DZJ0=5].Z-ٙ٢](d\u,^W~0 *0޵aC_H~RSRETJp.^ҭ333X[[c`OѢEܥ3?O&5YYfV\3vX~YD8lqpt )!I!''G* lle5L&lG%Idٖو3ӈSը*23E;Pȸ9yd>7°0̿?EJ%ʗ\uV._ +iּ ]3g|2iiw$Ο;ٰfDHIq1"mANE"OZG^ᆽ=QWdk 9]V1 Y$ Z%Q cJduB+dNTP(dm ](tЅGRRrE9.: dB5&z5M4~o(5\qprullP;T)2~NJ:_z da kkN.2.ĺP !\(E2d=3ce r 3HKo*Q[1dɳhZٻ ](tЅ^ @}OVzfAPVE*JR$IeRXkXkdGBJDB!B a 2`Ⱥ(TR)d?o`+gggec4e= )$o,tЅB <:C@5 ;3[#Bπ مRa陲n':N .PB@p/bN [\d?[{[Y(UJ셌kkYIRALllmD;PȸB࿆ȖLyoz`ee(dz( k ](tЅOBg 5M 6:t`b TUMn @mXn}g,UJMdd 役d$\r ?24E|aDB!W@2dIOKm{4?%t>Au?CX.@G IDAT7UV?}>A42u 5jx`^ʕ+GfMDGQps=6ֲ^xP*d&w[id&Ivv.C[Y Qղw PB #s1BV:?ɘc Cw kBX YBPPPkj֪z$IcΝb9k _˓3AՂGo_oʗ/]Da /Y/#=V'/dze2HIo*J23d.'',N.2.ĺ^}ywXh}yEew557t_(oo׮]|nB|)))(Q&͚m<1LtЅvۡꋯXr:ҥCtBʕD?b"/&g͟,%_%eefT)Lϔq, POBkm0K@-f0W&{tSN߫׬9=ܾ}Vt'J||x&Wj@Q6sg,nJnۺt"Vl(2am4G ,b: P6* ?p |`z f8Z[qRnض]:.{pe۶E7RtKWuva ıNGmg8"}l)7/z1p9?85~O;I/xֳxWdh͵7Noy HQĴL)]0INzݫtJWؽIrg?:>7~0$Gu7FtCt ۵tglƫ$Ɵij:ߛQwMGk7Eܻ~W!sF} g^vLh67N $%iTQA9w?N̚u|_/Ao;/-=}lk Y$=.4MLsԡjҢ Y Z􆏛Czv'd'%xwu0IqkV ]B[F 5zeD/.<UN/^^NVCq{|?҆C[J%LstT="cIO'o܁ 3el}9{ϩ#Ū?sL4!9gl]L*q\ u\c!쎒P< !=c x _ڲU=F+ݨ2Z62m2tgTmKvleil׾2}ԧ/rqv5s=ssƒg[3{\ri1Ax0Y8@}~G蘬%aRJ4ng&omr>$&YOUxfc /*$<ok 8f*Ae[P5 |/i) bPu05@EJQPŢ M->'zLeY^aJao׊M,lb,[|&mQ,{Z>A<^-6gR@sC~?EFېt7#idg(]wA'IRɒ=|,]+1OXX< œeu(~b!|=(QUYE?U^,&yuf?ڋ$Lsw/y/92eD:ϊr (T+۱iĹN!M ^6ռok/ڻ%ñ6g 4|;WgyPRB>^ٿ)~YK>!/;(C'4@g4^:(W,[*00sq+:ZmۏnW~ q>~ci}6.Hz]O9a]@YU f)kHY)p6ٿE/K^O~j3Cz7++:,߷Y'YMQ@7.`qS_")@^t 0{78\Dzц(Du^j.W p= v X]{6Pdaa{s$I4c{gȷ/"}CR'ˮ;`|zsǼS;u>UgG \p^'S%@-(@28k?,eOQį.~moQT|.^6٫[E[4(Ccf@P YTanp2) dqҎ&AQ2xOgTz:[M^{[,w e_?_}蝮(ݫVFq{s4˄Ľ彇^d㘀=ސElŷEK{> fKgSU.2`-5( pƙD׿V,nC&wsh{3 bc ^JR'?8^*.hL-B+1̦B9cQ[n^g>#jpSfʆfJ,>'X| w5T4R.}s*l,[6og )?z3$5u+z{ivcoO3ޯY48M1MRH$ #~[~4fiǕfeXz_*] 10|-geo&JKg̸O } }_{Cߑf_[EQ-{^%+=̐ev8Kޟ߶Fjagd<Ek:Ao||UYi`0zZ C./e.4߆ ~xbG=C|(J:*o0ߊ4>m?9aM7V=w;&yR!8@l ^^qec90G1j*y-իxh,>s z/?>^:;^wկ῾[j' ffb\9h>$wkwsGp sܡ|0ՕY9>0)% VI;.NYqBphWZB+x_sIx)s} ON$f.N婪*nTx>VRJd 99ʫ<"blia6Ǻʫ'3fY>.sڝ9qB,s9IIS.Z8Z0/dB}iėP:>.i0 K(+iNm$[n0m㤤R_BI] +UGԵp#ѴO2S> UZ 7w.n4#]#l|!$h,PNpiu{~QL~y8h!qB nkFq{Y7NaaFb01nQi^CǚAJ8F!,"4MhJETM QQ4g-,c 㱗cjjQ߿wsKOz{yf2% ݋S}y ! 7(R>.c9T4rNnEYvGCi8 Z|ԧP$ ^|xL2aZ_=uiOGތM3#ۮ]3GyӬOG0,ꈎWF'w\g ˶Fo2LcdzqEQ`ZQTS3S#;=ۧMx5PՋ|+skv^1-ZeZqUUјnZ)hۧGv,B*h#|T4W[qa;kV3USG^#ǦhImS崰ŵpC6Y?f߸i ߸i ^_},6o4/}q_u۶q;IP2+ibª<ǑGF~ّZg>~dvtF}%:{.~w0G(G.R\2gs Cs|PsGѕq*gAw(ȿ,Fk*룵u)K9G~{[Ǧ29-lkhM-\Z\ri;x IDAT^sON&m/Z-}ldY1o5ݧrp t;]틢o&5;>d'&)4iO>Z7>/igمk8t#"5c1̆i7,LWy#&ۇuS{JVqwy0K'sȜ&8 O27QO@=E'kR>cY 96xtA|AUU( BB>.#Bz9t Kp E@QZ(X Y oKvL)׷k3 NQ8N6B BBaN)`jH/ Gcl'wG1vl#2rt1vb-d8,7uۮM{㾪x~,{ T&1RA8ILX 7 k!01dØt$:[H}\iJ:qD&q8#\F =ZZZ0 !MS˳I"A&wN8>,h%rsBꅼkaZ0̱K(xUt,ǂ} a4M$!BK!B7tqRR )'  -2 8f КqBl,0Et7K)':ُ8tZ$t(n}-\gl^ҚJ%Tfv qAwBӎ^A`ٺm2m"to5OZZhF0/}򽎓_9ԱGgvqUGppp`)qo`?jߞvZ?0w$0n©*]$Neldža&^CBLt؎<ϑD AP5mM!Ġc0Eu،"k4L .L\}2¦} Mҁn ԗ،RE )%(Z26K쎲i;C6 [6Y2dСG=Viqt.t]v<Q$Sfu,_¦*0 c`3K3$IMsG15j>LFAa3Jq\ftd6 ؍2gOzq#">Lk5Mt:*4dERnALFN;86XS ֨mnM*J^e}Z8vn}LQ 9xs MV^ss*j/m| uOyxƿ}hC)5c/7N.Jȳ];ؤw|di0QlP;>򬘦OyˎdȲ8)lveyi.O(Ve-?G#M%6?@.y'Gfe3X<;ڦLNM!hX(φl~SX;^Ƽc9k{EbZ;6=CIam4&m(Y!ɺuD\S 7fmfiqS8fn&StYV<}[l~9x]x.fs;>Aprq񺉣u.stH53'pZ>OMnEva"ӟTyyT_}L_Qᝧ(~})}m >g݅iO1IGs sA>mTX $Pp 1M.E*n>n1>ѝ|'Bɐvl#yMU׷c]ס霅 eB(pk$>v;߮ rbv?p42di7I}Dw8>zKpp8^߆d}Z笅eh7k;kF1?mgK~ל˱1lU8hubZ&<'z9;@(4;p_ 0&[{ЫVp뷝i۪ۿ>x |_w: :cg>8ɏ+ɏ+K޻9ft,(g9dFxS\}EsPe[:aKҳGc  -d1 SRS$ID)ݎ_d4%]OJII@v<#r x\82 Ztׁ]j*\e,& BTtaJVc,8):kaY'k!L!K&ݾ@tHfiF~uGaD ~GҝqBʳ3㢅QB4x! ޚA!]Q>.P!@Ǻ**t ERަG^ p CS@挪 ݎ@!O@ЏM%]([(Y 7 2ZHyG] f| PN ˆp,#}|L=e8,v3t$IA Iׁ-ڨY ː9U`1&Q0wxlׁZNUU8B}N,BdPU=(J TM}Z!pT5eh.4[ȝ'f"p,an8Ύ}\4MI׏R">7M|,_Faax{h¦2>x9e,@y%rY c 3I CBne[)۱%0MA7{t}$N`4#8aZ& k!xdc<ǐ9$>#IoO3tZq $= $(pt^QLv8i!qBc-,ա48s–D !Иj8Y uЌv\LӄhQm"mu:dNwc#iXtA5unqRB 8&p\`-,CZ;'&*:M1 3̧[y(# y5j趏ixSgOj[0,CnOFRڙע b c9 2zqBtD S[j5tv>_(R3Nآp,Ft\%[TxU}\۶I'BZ>jma]ㄵ}ߣ~Gv82|F_?swO}W |_~gہ}pJ1ɿ8ڗ8>>BJz대"q8k!kx`Yh qyϕWw:X ~1ű{xc_p?@9#f3EֶH0 o0 q 4]]'i]G1U!S5qBEe-d-~tY1.,~~oG<][Fj`x0Gsvn$l(2a"cRl>V8vCZZȬy ]`axSa6JB}cC>STW[}^L$^B;99gu]'=Z8Z2C<^gAd s@a6ʛ⩮)/ROӣKH3@&$_FpkgSSž8vpǁ1N>zO$ui^9$a|HIcǴa$ag8a-d>Z|ÀϺ7 7?xpfן c1 * ޶kCC4o\,Y`2tS !PV0wns-4g@ Àk$Z!Aȸh!Zx2yq¿VQ˶x a}laB.sh;6%_6ZN{7}&^_޶g.}=lM_ss[n=麣o?Oq!~ؾ};n-- / @ӐeTM-"SPԢӕg}afPUu:\xI#M(rMȮm dYf(eH#Ϧ2K ܔn(6l394U[pf|&fSU]WGhgBKmAȢ}2ͅfacmjg/,;>m͍ngEHdPQrvlu 6<ˡ֤ja"jgs#L{uh63:vg7^Xع"᧟q:[q}?N;4\s5g?o}[8x .b8n ˶h4p"MS9_fk3pPT4Mhsj*,˂m{tCap]rM͚V|F4fmzC6!Rc0%6M˄e[Kls vaSciMk6¦+ڴ,(,9t4LcM۶iQ6uM_i t]1t{9dqlvu>l2\+ulC6e6N]׏<&L\e6UU-l`SaXۦaG\jcb@EjjjYsYM]+4hҴH'@ W96Z,l#ǫkkJ6OY /=:\puSOPn^U*@{pu]yǝR‹.s9p=v(s9?Ϲ:E_Wg?gLgLoyz ͅ&9{bE TUXx`~6(e[ll!vnya.9=>C4`b3 -u1bcSM컛fk!B&9S ,+8/`SQ}_ }kW} 3pO}SGuoxnx h8>_>|`^gǛm |q5WGx! =33Qq',aBNА88a-\?0m&}vٍj3:w.v8,\\?+v0d(f(?̳S]>t)%M]$NHNzǘԵdrwn\S;,$޵kv؁{3nx xK_28G_ wݵ8pO~$,xI?R1HE}~u\b׏~UU(P2W~+]Xz߽} aY|K_ȇ?2y}uWzl>2*k3NKi鎟&4HwUUk'1B4x!KkE}]4!!/c Bhf rqudx0[fsS)%$ce9)$/Ks\J’qZU8"y/y1JӟtA}a߾}xo-Zaˆʡ*PL/,빤3/d-|*kV`zf=2u̲,yg}&{^e/<[q84 Y}yW|gIL '1bݐㄵ}jGG1v xwiE>p s2,OɧV>.iP5e' _F@_Fp9s,ݺV<g<WajzjϚ -<⬳[-RGz\{ /pMW_\zZ.P\G}Lr%o!7k!x+hf0=3DZwɥ{]R6GvlxfffVl7}sGǹ睋;Gc;p 9t !Ҕp,G %BO[u]Z$Af'%t1BAZ8n4>]=<\}Hqcmxӟ/~ዸd)36wlDž]9^'cAlOj#GHϫx0$0:A8haDcRkibG;_Z$q]Ws`߽l6qc޽wus^GXu=p YVL8I?ϳSS>Nt5)%n9D.diFv1VZ2}.}߿}c8pcnv~m};{'^qС%}v3p ]$o#:|9Nu$!e_o؍k'>5_XLL~p{oM|'cBnir)BuQEb J؎M:i3y2 ŧ‘>)Kè'?c=h.4Ilޠ.c5E )'1-lY |FO*鎋e[дΟ*V}N Ӏ "kC(>|]ӡ*?8)Qka(y<1Ę EZ7>VX Y 3Pͤ">.1T?ANA;Ϭ  Z~@~Te;>Q!bRj>kIL>k6k!0.i !Ui'gp+.C7` kRa-d8$& YӾ4II,2G$diF; $8!}<RpX p CS@e3fY>.sڝ 8 SK9N8g-d-ddK(P_f;6qq Lӄa_BIW0mtGa&IY-Y Y 9Y CM]4!\(HA 3 \tZDaSN'q_^7ZHyk\r8Zxgb& cB=aPUViKYV!sh&$&O1?Z*UOgb& cHt)#\Xtqq1IsHB@  B“&>3a}d> U/y t<tg+b$1uRJ?$0IDaZZ}LQ ׀z {1 Sus=+USI'8!>FEw:QZRc0`iF(d-,O^8Cfq_Ðn_ER,GdxqL;O{f!ki8'+NZ˯aB48iUuM!*kOq1Z3ZeA1sx>bviMD0 X5][ԑEAZ)uqV«zKRgK򤹞;r#g,ZG#>rvGT;G|(q}t,q&FP\ڈ8shf8{0˖B⮡2kjJqkx3Pp?~:ɦ1@FV:!lFقX@F֪huqy>Zh Nb6 MxY XMӠ:ڭ6* (Ffp<,Yk3wz.<@4؎0U=$Q4Mv6[E4G1M۵ќon4M̎:P5ilR s$"A^TUvF qCB«z2znq-T$q$IPViw`Z& (lk$$\֫j:ijvmH)6}84I!X_()%ns栝I$N u{-9hgvSgse! #4#"MSԧN)%v 4jE;`4p\ ThgقW&頝~LJiRfDqd[P5֨E-TE-v-yµ0 A;har-|x[+ر辌|3Ysa/xֳ Hg\`ixoS|sէҕZs>-kL70wxn6l۱ ^SwZ;??cgW=>m Gg+/Oiu&+o߾hLw OD$]u9,j():WVgjD醎jC+@Qu=p-u|-OoK %˷8wS]u}ZJ|mէ֊zߩl7۫qULTTUϮgvnO֌3;<7/giqbMUgU`r5:%p\g0PZчn1;IY-l4tWk8@۷qZ|pܵ1Uï?^G ~.o!wW |H>q㍸yr8 ԧX8@}wp[|TMثvz;e 2dӊx3L:x8YsID-\ufs aKqR}2'c5-|{}$f=p Y0$Y<ϑD =]z 3diK$qBz4ZHjZ oɄgPoᾳp+.5:fs,ѬdmwtJ&I1 3y CƁ,HGaIx NJNTG0NH08 s8ZHZ Nb_a(\u#(kE @s*s(KBEj*qBPSPeTW[(# c:"כ0-S3StD8وNR~GdƐlI(8,c3[ԗPv.hӝJK(Iʭ 3i CqHbBUUzĤ =ILtHY7 NbR۵i'1-u-dIga'̑ io%Dq!2m$'IJh!uk!a02{S>tq\"9I88Z0/dU=> e[UiB7& BҰ29NJ`9$!㠅ԡ 3i C<'1)CfkQqqrQ$I8NJtR2̤3p Y' ܊4% ̂qRUSɧ'iN1?ZHZ0 C9 VP|1BPB484|J08 B:pirNbu඾2̤3p YxyV8>.i3!z._%u 8tqBPB!MS> e <7Se[ %٣xRga& ^Bɐ%b> %oIvׁ|$Eۘd"9NJ$ :pcԡ 3i CJPATز,&B4EÈl $z8C:8h!uk!L<ǐlI(AfGQqFqrrqL~"8 :p㠅ԡ 3i Cꩽ@uq TUM>pChE۝-$'Եa& c^\s\^>.SIs(qRs*tg1A C] p CvP!2t.[/$!o$R2̤K(5> %PUز ')8$1'䓘dk!a!y*=7V4FTP0!? "AQ]e E\XaŜQ5Hf%(U "Af`B 7z̭yy~fsy=$Y)$(Z~2.,B(9"AzB;l# =J'=u$(f&58卟$ac@kǨۡH%=A,$$#EyIUM\e뺰)D aR=F۶f\֓2ٌY逸93HScfOj46}2TϦKSi4i*CMD*M0`_&'O3VƱC/c{Ou)Ӥ#'yݬ16;N0ci&L)ÜMcyYpr4M+QzXOR(Gr{ !J'=uX{RUn@6e8l=p[˶DmY3Z꿛qEP:鱐ӰӾ'~B3ZSPE]nz[ ؀#W^^Ɓ*Ea$~٨'A dłW \DC(I,K/u\渝\+;S'=uX23!8Q)WX)x'~IìIZCsXHTT `q+4 Ce, at\=iR b!QGIfI%}o&[W 26[xiY?3Mmu\=i%۶E7!J'=u\dTCC̄(?NzK_ Dk-z+e((B\(}Aqix\L;BX,#]EfB i#l8n(7RJ^!qlыضL6z%bC,Nz,$4#?`&AE/,0[e# ">44@n=Tyrc֓iEvIDl !}XtZkDA2NsŲ>Ҭ'0ֈz>c%J҉DC(IBa&r\q 1q3.C(2ۑ?Rz,l!˱Ӱ"&Da$~YYj5I^Ĥ^g.bJrq$x6I< IDATD=p$V&a&܆۵Y)X6siF`6I+@BB"6;=Aq nDoP nP܊Fp'֓Vh7^m : PX##̄(FR2n墌^ZJ}_¨I+*5٫PA,Nz,$4|$Ebut3R0-S8͸F + d\͟'A6zFumP:鱐ӰĪT*̄(t6ncd5U= ##?'X*m  ؀3Ã(u|aB!drtv2R0?B8QP.adhmq m}qZu4J4N&\EitRu$84,4Gaou8‡P 7`ybvlYI+oų9C(BBN8khFGX)Ԫ5hYu|f-ԫ+{!J'=uX_ 벌SlKJ)֓Llq+pG  :N2 HECPJ)֓^M+Vn䭔 G4-yI'bT+Uq '{L5J)Day Z&{6IDnInfB e^8zB%8lՓv yP!J'=uX 3!8Q:[8}ij= ia(~yt<ѽ  : {0[Bf>f c!pD-}{RVn?Ezێ ۖ;4L)lNBLip\$ eBBN!$@3!vhVʸ#jî֢AW9O10;8-loUF*1>ps?uXtrޙ*c7N8R B^(ym{gf-,L6#zvIDl G13!]3D?2Z8NZ}֓Kݑ$laeד6IB"6َbWqMDWO'ag;= }NwvOAnWwWB~'\vʧnƝwqJ?a.O>]NWӽ>]L@WOהso])meS?ud3SOr\g˶P|ӜABnVh{Z ]eO^ ”sr2̔l2lkz6*CYu0LOpXHa8&B0rM8S>ݲo: S??8šTO+ʔV{5|T 詟qTy8]Lte]|! ԔÚj-goƔC˶!BC0'Mӄ2P͢XH 0Tne^nʸ )z+%zo\!7N;h3«e[o!J'=uF4khӈreWDZVr b1B9I+rU>pm  : {HRwi2nHL63 ۛR RQtSkHV t\=iegJ.=btc!Qa|fB \kΖq Ka(zztxz DC,Nz,$bH˿92f[x3Z! 6&F⎫'~m _;BN!$T *iL\-V,;ۛRCҞ/=&s-||eD/P:鱐ӰnwsЎ#X.f -םGۑ֚{l=WI {+#DvIDlQۻ5Wߑe9o!0. 2Ve#cyfJ)d֓L˄eYΪ'-tt\#eƚvIDw, ืq+Xx2P~z4\'a+렻Kp5Q-]mIsH;B^ 8O )cc7 gHYT^vwgQj*gYѺ|f[ԝhQ)W/8gI,+FuiժKYdBify #J-c!MC(稛ǁϞtu}p3!]]KCt FwO2FWJf} @/gXfFc~QiQ ,\ԍϮEGM,4zJ*B"s^i( 8 @#!P=9EɹR@FHu=퐤8wMT7zk>`1~mBT@$1#J>A Pn@rSe4՛溡&K[}`۞;tu]X'v>1Q_~JDQis{Uk|Q\k|ꦴ}q>F2 %&rkWil} ~BC3|M.h0oԝy&3Vס;O<&x{68Q!L͚O?򤀳VU8%rY V\ B4k'0I.kW,7B.BwR@W xIC䦶~t*ˬa7NS `~!K@o@ C6+dɿF®u _.ߵQ4ͤ=͌GYv *@o0\I>q\?4VV9/'D M>$8HaF:F@uR7_;hj؅zϊ=}"&Dltƙ%K\h"L35z8Zfr-H.fa5z 0ykɍ+Tr֯o,X|)k&1zl?HޫF#buM.$}΂fk*xɓz WuӸYUc=J%i$fXVn74(<cfθze듑 ٤7!&uIa%u,y3`Ʀ7N F=7zM0nviX#P,&窮I01=O50U*&da`qRWbw۲ ў?Ө^0vcnfcYiꧻmײ6`JF4qM[MD&}* 8"6hրwv{H93%{7/Lz*;,Mi͸Ukb1(Gܘ3{{~-7F7L˄m=t0>0+)Taݞmnٶ -7؀6< _ Ayru2\q`Z-njS#_^7؀9K/.~=v*n7H7͑ji2`nɰ3CT*c؍P HX8%%i^#uXB Q}hg 8N4M׺)zlҽK=9},)lrcTDyJ]!ruQh1Nx],gz_]_xg\ߕJJB'i%cC ,DD)iZ1L3ωCc箎dž5+|=V?^]֦ h=VgMk4B#V4=j@ȝ;c6,B\zjX`; J 1MV!9SOͿxtn^k0pOW >4ƢYFC:ݍ!ZFc\^/pzkԵBcƮ-h&[cau| wlqëy"bfˁ/M3L p\{٨1<[lĩˌ-bb@kĮ(s|l``(I7KVD̍ր6(%cs0>_q~, Dq&χ$i:m7Z(YŮ"jL!pl.0 E2 *ldž2\>F/ ܬ k~l>Y\QMfcy#(F7nilf!ˠZ¶mZz,M7BC|Tia4'OӱQ-Of6Zk{ O滤\Mgm WEOj5@ɹ#33iPFrnUG^jpo|jmskc FP'ǂMvT87=TFа-u^'bD%RX[B'ekYr@AyrjRprlq#dsY*5 2Q{d3[fIlIs z=M MlavҜ2Ӝ,?}=j=:XYcZV54ұ5M0LcKebYF>V}.i&{^Ҙ V\fw(גI &?KVK 8m_IB!J'u2VƮf}`@R]QtCe<Ne)Ӵl anf8&x7q (BSfx[e4Mnq@lܳf p'>\ \kg4|DQkq8>(Fc?zsJbzcaV={6M3-J3fбz4&IMS4 <%܌NVs~ ktxuOxۤLЩ0yk|jx5XF1[Ѩ716[5==ko=pqsnuYi#k޼5B=Q l\!tRZ{臀j:èzi|Dq&ߏ;iXo]ASiMiffFǃIh<MӜW6KہO&=TlcDuQ/*?/u'>i(M܋8n[7xv߉hѸQl 41Zq7KIǻgdI6^V=pQ\ Xi|m~]j44CT4qz4Չ4@Ll4(\!:_fgͿǬ\t7L-X4~G(7B7B_=mz %mmy[$oJI㭖bRw jMmnkxska&[b_73|!]>IMiɼ Cyذn2Pr3&P&ܶ'[V@y8偞=;Xf GӊX;™2-+^UwMBLSqء<1枍-l nܭC-hT+ PQu۷s +hc5^.{MtsPMy8;W}adK4JB6Ķ"ؘe,% fI8, ! 5楱i0 K+/c۶EGh\>'i,2 W&]EyX(D?w\eƱ 8"!j @tBcek%ZqM6|Q?IX32Ѷ!$+qԸ)*58eI_HC# #DCn|M,ԌDeyd3W3L\e:)Dn~uUڶ 7㲞08uX?`=a,dq,$4#rs]WԆaͺ,RJ|B&Gig`,d,d,$"6Zr,`9?1MSB͸+P(^!Wȉf۶= ScD=p$QADzˆeBDzZ~=~XF0uEa,㱐 8"!|J/u\R!wxr8BqBN8kdxDժ5a((F\ayEL#eyX)WDp~ z3BBB"bچi0 (P}E+Sc!c!c!mV'$=\öm7~2į&-}c:oMӄe[',6D=p\%zL{\}|A0 ǨX[/c@C1*5Ud~%(DazX2nX(y "6e|G6/gfQ[b&p4  U3# Qr"X,B6~Sl6 G7 LqO:: BcKwus3<4aX*~1G/JPċ!TwIֵj-Y 8j+ƒ%Kp.Ɔ F_;sg /C{h{vuǟgzz?WW]qnqiื79a^'W?9}k;=o=Iҟ__,l""""b[Xđ;wq'xg/_z9{}{~Շ+Zџ˻G}ko8 ?)uލ\6{CC^1}skUeќ9p4^}ȫˮ'?ɸ_ 8'.;CC痍IDDDDus;g~L\yx_%K,ݖ5kLa}S|q6]v-yM7qsQ""""BIXh֬Y{O?~嫸 ?xr? Rxٍ?g,H""""bW*yx푯m߈{i\rw7`p`pu""""N!mǿ bA0q/ño>vE^ϟy_ł"""""bn_1Xze/vsNy)IOX;;W^q%֮];'p"."wϜ~8wN)8cNׯٟ=_ G?Zw/2{L9k֬ ؀}tf?Zԏt޸9F_ < 8,=5>Osٹ;Zks9Y?tޗF;:]-W\~ݣ߯g7hNQͿǬ\ h͜*ws""""BU [c \}5;sPR wXe-cf̢kǟ@%7͛E1c 8"""";,]2̡kZk~b5(R i2chJ"""Ze3b~EqfGDDDDDDlpDDDDDDlpDDDDDD4SFhfG1nm9=pDDDDs̲-O/)> 2CKwYBModfGDDDD"o, 'd,^-mk:UX|~ݎUbAGDDDDS9E/~tחw^셯xGX Dld~?ǕW\>0oq[CCَ`wBOO98'RP؀#"""~3N?{zbţ]?77fV>臱=YDW$"""#?׏ w3hbx/%"6HGy=3bl}ת5lذClf&LbcVǁe[y_fpDDDDDyǝ5@OO2C-p"""""68""""8U GyFFFGL!6hd3Xemy}/^~wA(GDDDDnwË}6bf<pDDDD%N/Rx'._NhѢI_uٮ?0 'OGDDDDgqoQG?n ݀(˵s-,k~?jɧj\mp+ 8""""j0 qkgf4}ظq#jՉpc``7ldHFwG(^:d{}8MGaE8WFlm.;#.RT+UW{}ɒ%mpUWc͚58Ci{ןS;?Ebqu `=vGo_a=vG6efpDDDDD3O?<ܶ{w֭CV}?L#6OƎ;33f&ֶg~Gc>6O)\ Q'Y((ۮ̌Yg"#bHU DDDDDsW$"""""b؀#"""""b؀#""""""6: "E?_==x _2JDDlka>!w < ,mDD8ɍ o[X8_ 8""q֭[('W""bH$ F2X)_ 8""-8 W""bH^57߿|g==tbNz?tـ#""A'_ D_]8~NIzG|fp4e8_7᜷vطp{EDD8"l8n@ww( xޤH۾SE0Msj7oY@ǣGig'..V {x;jV18hhVp`9.\ׅ0`:N{_7/X^v;6z,*xo^/> Ur8w␗ |fMqbׅ 0x~" kpǾ}^ ǂt1xXÎXpǝg[]^>Yaމoޱpi'x?}}p…O!jʓ,}`Ճ> ;ۋ]{N^7w;o5y { \#㣿GUk#򴣰 ѷp7]ǟ8䔈 8"nm8qU|1>tmxU'?T~::~qe6\ +8c 6]p4㱇hqg7?|מ(4vn`~s]x"3񍷿7/?3X}YX+E5{˓xބ?p!iCZ#;8%?>5<^a'#iw_q |x{wr36t4'1l v+z#Įo޵W ?B;|1O N7w֗a˾0o~/;8gwrv̛Xvi߀{ W2iW כoı8pe1vp@#oZٛ>l8"om^ppƹ?<֏4Geh4ObU F♵<ƼcM>[a黾?=83Žϭ˱7_BЇk|f44 b?H/sC`T*@5Ͳa|a/ g?aY_Yo޼)ׅ{u6{bP?Fc1v^'~֕at@Wy Obs?o]/LTz *x/¹WkV Շ3q{vl>;+?wV~.~gu.e=4^8ўi>3B,\x,sǗqix[輾w`#o5b஧1kt+3Xy󻠰 :,޺2M%8}'o}̪Ocw.Op<7pKy ? \B.}^0r 3{U')6f PfnA~1R-b勑S_>iHBpǟ?x}kw61n#sVbޅΘ0 Xu_ ܄Օqm=X5>xLkp¼_nJ U[񫻞E6~]7;a+0P#/ݲ^\/.\{(VFzKxb`S~ <X;x Ecn@u c׆qEᮠiyG`K>mjޛp A8U\qǚP~>1spDD41q}+po.Ԁh^o?Ny+ξ.=WRx)p>w?Ps߈߲?vi)_ٯ/A[{?6^Y8dNXS.t0Féoϴ.>?pw¢]!!!t>Zx!\7bŮ;cnƹ߂|ꕰyoXxb/>@b^~Xr?GW$Ym/evxmKɋ ,=Wq^#: ?R/ƋB/8ǭƎG xg^x;>~arO&lȶѬQ8[pʕ DD3p5Gq332 8cX0_Sqء-GD4!|X'_hGD4$|X'_ 8""6 86؀#"-ׇ8* @cn|%""6Ń{/x lcm?),W""bH+Vǽ܃ 60CCo_V'p+GD$^{`& ZyGDDDDDDlGDDDDDDlpDDDDDDlpDDDDDDpDDDDDDGDDDDDGDDDDDDlGDDDDDDlpDDDDDDlpDDDDDDlpDDDDDDpDDDDDDGDDDDDGDDDDDDlVK)5W_\#"""""T5z<44#"""""jR4ߵf+a""""""j-.bBDDDDDGDDDDDGDDDDDDlGDDDDDDlpDDDDDDlpDDDDDDpDDDDDDGDDDDDGDDDDDDlGDDDDDDlGDDDDDDup @[%DDDDDD25oW """""jJ""""""aMr%`&9HaBIDDDDD$d&9HɆMr%0 J""""""f2lC(ɰGDDDDD&?WfS==RIENDB`lxi-tools-1.21/man/000077500000000000000000000000001332035063200141375ustar00rootroot00000000000000lxi-tools-1.21/man/Makefile.am000066400000000000000000000000261332035063200161710ustar00rootroot00000000000000dist_man_MANS = lxi.1 lxi-tools-1.21/man/lxi.1000066400000000000000000000054351332035063200150240ustar00rootroot00000000000000.TH "lxi" "1" "March 2018" .SH "NAME" lxi \- a tool for controlling LXI compatible instruments. .SH "SYNOPSIS" .PP .B lxi .RB [\| \-\-help \|] .RB [\| \-\-version \|] .I .I [] .SH "DESCRIPTION" .PP lxi is a commandline tool for controlling LXI compatible instruments such as modern oscilloscopes, power supplies, spectrum analyzers, etc. .SH "OPTIONS" .TP .B \-h, \--help Display help .TP .B \-v, \--version Display program version .SH COMMANDS .PP .B discover .I [] .RS Search available networks for LXI devices .RE .PP .B scpi .I [] .RS Send SCPI command .RE .PP .B screenshot .I [] [] .RS Capture screenshot .RE .PP .B benchmark .I [] .RS Benchmark .RE .PP .B run .I [] .RS Run Lua script .RE .SH "DISCOVER OPTIONS" .TP .B \-t, \--timeout Timeout in seconds .TP .B \-m, \--mdns Search via mDNS/DNS-SD .SH "SCPI OPTIONS" .TP .B \-a, \--address IP address of LXI device .TP .B \-p, \--port Use port .TP .B \-t, \--timeout Timeout in seconds .TP .B \-x, \--hex Print response in hexadecimal .TP .B \-i, \--interactive Enter interactive mode .TP .B \-s, \--script Run script (plain text file with one SCPI command per line) .TP .B \-r, \--raw Use raw/TCP protocol .SH "SCREENSHOT OPTIONS" .TP .B \-a, \--address IP address of LXI device .TP .B \-t, \--timeout Timeout in seconds .TP .B \-p, \--plugin Use screenshot plugin by name If this option is omitted the tool will automatically try to select the most suitable plugin. .TP .B \-l, \--list List available screenshot plugins .TP To write screenshot image to stdout simply use '-' as the output filename. .SH "BENCHMARK OPTIONS" .TP .B \-a, \--address IP address of LXI device .TP .B \-p, \--port Use port .TP .B \-t, \--timeout Timeout in seconds .TP .B \-c, \--count Number of request messages .TP .B \-r, \--raw Use raw/TCP protocol .SH "RUN OPTIONS" .TP .B \-t, \--timeout Timeout in seconds .SH "EXAMPLES" .TP Search for LXI instruments: lxi discover .TP Search for LXI instruments using mDNS/DNS-SD: lxi discover --mdns .TP Send SCPI command: lxi scpi --address 10.0.0.42 "*IDN?" .TP Send SCPI command and dump response to file: lxi scpi --address 10.0.0.42 "*IDN?" > response.txt .TP Capture screenshot from a Rigol 1000Z series oscilloscope: lxi screenshot --address 10.0.0.42 --plugin rigol-1000z Or by using screenshot plugin autodetection simply: lxi screenshot --address 10.0.0.42 .PP Note: Some LXI devices are slow to process SCPI commands, in which case you might need to take care to increase the timeout value. .SH "WEBSITE" .PP Visit https://lxi-tools.github.io .SH "AUTHOR" .PP Written by Martin Lund lxi-tools-1.21/src/000077500000000000000000000000001332035063200141535ustar00rootroot00000000000000lxi-tools-1.21/src/.gitignore000066400000000000000000000000441332035063200161410ustar00rootroot00000000000000*.o *.a /lxi rigol_1000z_screenshot lxi-tools-1.21/src/Makefile.am000066400000000000000000000030701332035063200162070ustar00rootroot00000000000000bin_PROGRAMS = lxi noinst_LIBRARIES = libapp.a lxi_SOURCES = main.c libapp_a_SOURCES = options.c \ discover.c \ scpi.c \ screenshot.c \ benchmark.c \ run.c \ lxilua.c \ include/error.h \ include/options.h \ include/discover.h \ include/scpi.h \ include/screenshot.h \ include/benchmark.h \ include/run.h \ include/lxilua.h \ plugins/screenshot_keysight-dmm.c \ plugins/screenshot_keysight-ivx.c \ plugins/screenshot_rigol-1000z.c \ plugins/screenshot_rigol-2000.c \ plugins/screenshot_rigol-dg4000.c \ plugins/screenshot_rigol-dm3068.c \ plugins/screenshot_rigol-dp800.c \ plugins/screenshot_rigol-dsa.c \ plugins/screenshot_rohde-schwarz-hmo-rtb.c \ plugins/screenshot_siglent-sdm3000.c \ plugins/screenshot_siglent-sdg.c \ plugins/screenshot_siglent-sds.c \ plugins/screenshot_siglent-ssa3000x.c \ plugins/screenshot_tektronix.c libapp_a_CFLAGS = @lua_CFLAGS@ lxi_LDADD = -llxi -lreadline libapp.a @lua_LIBS@ if ENABLE_BASH_COMPLETION bashcompletiondir=@BASH_COMPLETION_DIR@ dist_bashcompletion_DATA=bash-completion/lxi \ bash-completion/lxi.snap endif lxi-tools-1.21/src/bash-completion/000077500000000000000000000000001332035063200172375ustar00rootroot00000000000000lxi-tools-1.21/src/bash-completion/lxi000066400000000000000000000043711332035063200177630ustar00rootroot00000000000000# # Bash completion script for lxi # _lxi() { local cur prev firstword opts discover_opts scpi_opts screenshot_opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" firstword=$(get_firstword) # The options we'll complete. opts="-h --help \ -v --version \ discover \ scpi \ screenshot \ benchmark \ run" discover_opts="-t --timeout \ -m --mdns" scpi_opts="-a --address \ -p --port \ -t --timeout \ -x --hex \ -i --interactive \ -s --script \ -r --raw" screenshot_opts="-a --address \ -t --timeout \ -p --plugin \ -l --list" benchmark_opts="-a --address \ -p --port \ -t --timeout \ -c --count \ -r --raw" run_opts="-t --timeout" # Complete the options case "${COMP_CWORD}" in 1) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) ;; *) case ${firstword} in discover) COMPREPLY=( $(compgen -W "${discover_opts}" -- ${cur}) ) ;; scpi) COMPREPLY=( $(compgen -W "${scpi_opts}" -- ${cur}) ) ;; screenshot) COMPREPLY=( $(compgen -W "${screenshot_opts}" -- ${cur}) ) ;; benchmark) COMPREPLY=( $(compgen -W "${benchmark_opts}" -- ${cur}) ) ;; run) COMPREPLY=( $(compgen -o filenames -A file -W "${run_opts}" -- ${cur}) ) ;; *) COMPREPLY=() ;; esac ;; esac return 0 } get_firstword() { local firstword i firstword= for ((i = 1; i < ${#COMP_WORDS[@]}; ++i)); do if [[ ${COMP_WORDS[i]} != -* ]]; then firstword=${COMP_WORDS[i]} break fi done echo $firstword } # Bind completion to lxi command complete -o default -F _lxi lxi lxi-tools-1.21/src/bash-completion/lxi.snap000066400000000000000000000044151332035063200207220ustar00rootroot00000000000000# # Bash completion script for lxi # get_firstword() { local firstword i firstword= for ((i = 1; i < ${#COMP_WORDS[@]}; ++i)); do if [[ ${COMP_WORDS[i]} != -* ]]; then firstword=${COMP_WORDS[i]} break fi done echo $firstword } _lxi() { local cur prev firstword opts discover_opts scpi_opts screenshot_opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" firstword=$(get_firstword) # The options we'll complete. opts="-h --help \ -v --version \ discover \ scpi \ screenshot \ benchmark \ run" discover_opts="-t --timeout \ -m --mdns" scpi_opts="-a --address \ -p --port \ -t --timeout \ -x --hex \ -i --interactive \ -s --script \ -r --raw" screenshot_opts="-a --address \ -t --timeout \ -p --plugin \ -l --list" benchmark_opts="-a --address \ -p --port \ -t --timeout \ -c --count \ -r --raw" run_opts="-t --timeout" # Complete the options case "${COMP_CWORD}" in 1) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) ;; *) case ${firstword} in discover) COMPREPLY=( $(compgen -W "${discover_opts}" -- ${cur}) ) ;; scpi) COMPREPLY=( $(compgen -W "${scpi_opts}" -- ${cur}) ) ;; screenshot) COMPREPLY=( $(compgen -W "${screenshot_opts}" -- ${cur}) ) ;; benchmark) COMPREPLY=( $(compgen -W "${benchmark_opts}" -- ${cur}) ) ;; run) COMPREPLY=( $(compgen -o filenames -A file -W "${run_opts}" -- ${cur}) ) ;; *) COMPREPLY=() ;; esac ;; esac return 0 } # Bind completion to lxi-tools.lxi command complete -o default -F _lxi lxi-tools.lxi lxi-tools-1.21/src/benchmark.c000066400000000000000000000073711332035063200162610ustar00rootroot00000000000000/* * Copyright (c) 2016-2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "options.h" #include "error.h" #include #define ID_LENGTH_MAX 65536 int benchmark(char *ip, int port, int timeout, lxi_protocol_t protocol, int count, bool no_gui, double *result, void (*progress)(void)) { struct timespec start, stop; double elapsed_time; int device, i, bytes_received; char id[ID_LENGTH_MAX]; char *command = "*IDN?"; // Check for required options if (strlen(ip) == 0) { error_printf("Missing address\n"); exit(EXIT_FAILURE); } if (protocol == RAW) command = "*IDN?\n"; // Connect device = lxi_connect(ip, port, NULL, timeout, protocol); if (device != LXI_OK) { error_printf("Unable to connect to LXI device\n"); return 1; } if (no_gui) printf("Benchmarking by sending %d ID requests. Please wait...\n", count); // Start time if ( clock_gettime(CLOCK_MONOTONIC, &start) == -1 ) { error_printf("Failed to get start time\n"); return 1; } // Run benchmark for (i=0; i #include #include #include #include #include #include #include "error.h" #include static int device_count = 0; static int service_count = 0; static void broadcast(const char *address, const char *interface) { printf("Broadcasting on interface %s\n", interface); } static void device(const char *address, const char *id) { printf(" Found \"%s\" on address %s\n", id, address); device_count++; } static void service(const char *address, const char *id, const char *service, int port) { printf(" Found \"%s\" on address %s\n %s service on port %u\n", id, address, service, port); service_count++; } int discover(bool mdns, int timeout) { lxi_info_t info; // Set up info callbacks info.broadcast = &broadcast; info.device = &device; info.service = &service; printf("Searching for LXI devices - please wait...\n\n"); // Search for LXI devices / services if (mdns) { lxi_discover(&info, timeout, DISCOVER_MDNS); if (service_count == 0) printf("No services found\n"); else printf("\nFound %d service%c\n", service_count, service_count > 1 ? 's' : ' '); } else { lxi_discover(&info, timeout, DISCOVER_VXI11); printf("\n"); if (device_count == 0) printf("No devices found\n"); else printf("Found %d device%c\n", device_count, device_count > 1 ? 's' : ' '); } printf("\n"); return 0; } lxi-tools-1.21/src/gui/000077500000000000000000000000001332035063200147375ustar00rootroot00000000000000lxi-tools-1.21/src/gui/.gitignore000066400000000000000000000000111332035063200167170ustar00rootroot00000000000000/build-* lxi-tools-1.21/src/gui/lxi-gui/000077500000000000000000000000001332035063200163155ustar00rootroot00000000000000lxi-tools-1.21/src/gui/lxi-gui/.gitignore000066400000000000000000000000721332035063200203040ustar00rootroot00000000000000.qmake.stash *.qmake moc_* qrc_* ui_mainwindow.h /lxi-gui lxi-tools-1.21/src/gui/lxi-gui/Makefile.am000066400000000000000000000016251332035063200203550ustar00rootroot00000000000000EXTRA_DIST = lxi-gui.pro \ lxi-gui.pro.user \ lxi-gui.qrc \ lxi-tools_256x256.png \ photo-camera.png \ main.cpp \ mainwindow.cpp \ mainwindow.h \ mainwindow.ui \ workerthread.cpp \ workerthread.h QT_SELECT ?= 5 Makefile.qmake: lxi-gui.pro $(QTCHOOSER) -run-tool=qmake -qt=$(QT_SELECT) -makefile -o Makefile.qmake lxi-gui.pro \ QT_INCDIR="$(SNAPCRAFT_STAGE)/$(includedir)" \ QT_LIBDIR="$(SNAPCRAFT_STAGE)/$(libdir)" \ SNAPCRAFT="$(SNAPCRAFT)" \ $(QMAKE_ARGUMENTS) all-local: Makefile.qmake $(MAKE) -f Makefile.qmake $(AM_MAKEFLAGS) all install-exec-local: cp lxi-gui $(DESTDIR)/$(bindir) check-local: Makefile.qmake $(MAKE) -f Makefile.qmake $(AM_MAKEFLAGS) test clean-local: Makefile.qmake $(MAKE) -f Makefile.qmake $(AM_MAKEFLAGS) clean rm -f Makefile.qmake lxi-gui lxi-tools-1.21/src/gui/lxi-gui/lxi-gui.pro000066400000000000000000000034711332035063200204220ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2017-12-31T00:31:18 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = lxi-gui TEMPLATE = app isEmpty(SNAPCRAFT) { QT += charts } !isEmpty(SNAPCRAFT) { system("echo \"Machine: `uname -m`\""): ; system("if [ \"`uname -m`\" = \"x86_64\" ]; then exit 0 ; else exit 1; fi"): TRIPLET=x86_64-linux-gnu system("if [ \"`uname -m`\" = \"i686\" ]; then exit 0 ; else exit 1; fi"): TRIPLET=i386-linux-gnu system("if [ \"`uname -m`\" = \"armv7l\" ]; then exit 0 ; else exit 1; fi"): TRIPLET=arm-linux-gnueabihf system("if [ \"`uname -m`\" = \"armv8l\" ]; then exit 0 ; else exit 1; fi"): TRIPLET=arm-linux-gnueabihf LIBS += -lQt5Charts LIBS += -L"$$QT_LIBDIR/$$TRIPLET" INCLUDEPATH += $$QT_INCDIR/$$TRIPLET/qt5 } INCLUDEPATH += $$QT_INCDIR LIBS += -llxi ../../libapp.a -lreadline -L"$$QT_LIBDIR" CONFIG += c++11 # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ mainwindow.cpp \ workerthread.cpp HEADERS += \ mainwindow.h \ workerthread.h FORMS += \ mainwindow.ui RESOURCES += \ lxi-gui.qrc lxi-tools-1.21/src/gui/lxi-gui/lxi-gui.pro.user000066400000000000000000000574051332035063200214050ustar00rootroot00000000000000 EnvironmentId {f0a92465-d8f2-4b04-941f-d73de3e1087d} ProjectExplorer.Project.ActiveTarget 0 ProjectExplorer.Project.EditorSettings true false true Cpp CppGlobal QmlJS QmlJSGlobal 2 UTF-8 false 4 false 80 true true 1 true false 0 true true 0 8 true 1 true true true false ProjectExplorer.Project.PluginSettings ProjectExplorer.Project.Target.0 Desktop Qt 5.10.0 GCC 64bit Desktop Qt 5.10.0 GCC 64bit qt.qt5.5100.gcc_64_kit 0 0 0 /home/lundmar/projects/lxi/lxi-tools/src/gui/build-lxi-gui-Desktop_Qt_5_10_0_GCC_64bit-Debug true qmake QtProjectManager.QMakeBuildStep true false false false true Make Qt4ProjectManager.MakeStep -w -r false 2 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Debug Qt4ProjectManager.Qt4BuildConfiguration 2 true /home/lundmar/projects/lxi/lxi-tools/src/gui/build-lxi-gui-Desktop_Qt_5_10_0_GCC_64bit-Release true qmake QtProjectManager.QMakeBuildStep false false false false true Make Qt4ProjectManager.MakeStep -w -r false 2 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Release Qt4ProjectManager.Qt4BuildConfiguration 0 true /home/lundmar/projects/lxi/lxi-tools/src/gui/build-lxi-gui-Desktop_Qt_5_10_0_GCC_64bit-Profile true qmake QtProjectManager.QMakeBuildStep true false true false true Make Qt4ProjectManager.MakeStep -w -r false 2 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Profile Qt4ProjectManager.Qt4BuildConfiguration 0 true 3 0 Deploy ProjectExplorer.BuildSteps.Deploy 1 Deploy locally ProjectExplorer.DefaultDeployConfiguration 1 false false 1000 true false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2 lxi-gui Qt4ProjectManager.Qt4RunConfiguration:/home/lundmar/projects/lxi/lxi-tools/src/gui/lxi-gui/lxi-gui.pro true lxi-gui.pro false /home/lundmar/projects/lxi/lxi-tools/src/gui/build-lxi-gui-Desktop_Qt_5_10_0_GCC_64bit-Debug 3768 false true false false true 1 ProjectExplorer.Project.TargetCount 1 ProjectExplorer.Project.Updater.FileVersion 18 Version 18 lxi-tools-1.21/src/gui/lxi-gui/lxi-gui.qrc000066400000000000000000000002201332035063200203740ustar00rootroot00000000000000 lxi-tools_256x256.png photo-camera.png lxi-tools-1.21/src/gui/lxi-gui/lxi-tools_256x256.png000066400000000000000000000257001332035063200220020ustar00rootroot00000000000000PNG  IHDR\rfsBIT|d pHYs%%HtEXtSoftwarewww.inkscape.org< IDATxy@TUǿwmTDP4mq-\2w2ly_jٞmViZ) (,0/*Ü9w^s{<9#@"h'B QO{P@ 'DQ< bl*Mfsn `<J`FQ2(L LE ~Q3vA8KׯAĘG`28@'vpr׀[`XlO +** =^`4 W%B2Ǖa]#+888 AJY3 @iii kQ'B |g zfscAv^~P Aa65uPRRRWn; eqJ!!!5p8(((0h4'T_EQ qM Bt08F0O8Z>-An4`XlC}~0k6AAAYx fqld2 ĭJ_8 ((Ӽ' (^[_4@ODhg2U5aoC7k \JAj.Bc^F񢗟 ԁnag2 콗@ < BMTX,Hߩ'uU#N#%"Bs(IAG1A>!Za ni $^ A>! BŐ! C@* T AP1$bHBŐ! C@* T AP1$bHBŐ! C@* T AP1$bHBŐ! C@* {3ˮ\m?tzoFxqd76a l>nu̐\a9n c#s -X 7{# tIIc\v{whX6]a[z6`ʭ6v‚ivgj7ȭ/uLf8p8hѨ.> r)C\HN>ʬÈhwxS:N_&8tf(\֏4` b,Rxb@ զ}pLFX]CtX84f)D\mzb@cmݑ9Cr/^bBQ^R{~Z,xm$jY=h4p w wu†Ŋo,&&UO'?qkhVvdd3V:6m }nǣ-cg9WtǸ>)f+DM9)'drt`u0,Z9ܟ4o:ƱG葌Wtn#PH202SfOyQ7<ͯ݋ E%._ђz׾@̛^#du4y(6=EO  3CjR^OtmwsOG#_v?4mO"D~à ;q5{QXldj'>\^lT(VP} WEYrCq "R0} z0Zي]NeaԛGX84o&_U)?@8W&ΞY /& a|gV/]5aȴ3VX4m5@3 Z@<1#W%2|jwˬx p%_L)Muo?K/\8b`ZGs)7!:ϖޔn˰uo?S _ N Lu O" :۹ڼj*×7l߰j*^ѕDL @z[x#Su(5>[ tt>1g@vk\U_)jg;:0}sYTbWkWXu8}L3C/?û࿿5%a)79kzu썱},hD~@@a3,灑u0#r22tX1sbp o`b\O zF*SjW?hr)p@Eril$tmn\cw|O/e Hn"L1WtM ~w4g4%ԯD&O#f>0RӢQ]zy_yH6KUux+2OKT%ԏ3 8(g}USr!WJ-IV 6>'V9>믉'*IƁ/[N]?DaSCQo`E ѽwї߼ lkdwиPYpjB{&/{d_ ks)ʀE@Z& % 7K~vD9 yasbTRsNqɻ~ұV$?# zO4*a){8x4փْ#%T`<"8E ?q3u9[PśJ~wg #.*M)H00RW K6[v~&YL _@RFj??Z=6|[JGs T,S`)3\m*e`hN>FX2+ߪ=,h+wξk4i2 #jF*_*io-HR&4SY@eRj.L~O-^]z8?Tuڔ Y 1Ѥ~WJZa7q/xOu7!E/OYWDE<3g56?\㝅[|@͎ç/r$?lZa՞Rx{{q=mp,{Zs%嚔-^[ Cl9w -.HRlfPgҳ1arY9ܪ@vixG,.M)Vf 4KnR_6 YmOyLzÓsW1~aСSˆ\mߛ)l]T}IYvCY*?"su-Lծ(`of򊮕7煒L \!Je\m!r1eAIRyGrdw@y~&Y 4[[7>Q8'E`{EFuڔy Zlզܘ9${v<`-9{ f ,߃_iPZ6Fy`ZG+n`\-프ۋ#qn;ڬ QT,Sv "ҳԗ]g~n7%1?:ɽ'Cʅ"sG\%f6PN_+|80,Lj 7WdK6YMG= 9+!Uݒ:VT7&!m_*j&xGj5Nx+VA[ @ CXH$1,R^OrY?4-Յ>ȟMo ;=0I 90(%>7Mp!]Ze<݋b#W7;TL?_wFxOͬ6LvW^E^Q앒{1g[h.7\Y'VŢGKga2Y@R|~?7{R|dH8 gVne<`aԛ$b`#*,Kwp w>If#fEQ,ho^+`;<[PpSWD6 9(&zf| W瑾SDxJ# ~'I~>1gg{`A|g狰%2d/ԣJ<d"WMe>ݐgfOo׬>r_*& !ڏ{ =+-~d4y-^fN8 Bdn{PǠÓ;qyLVqz*6s32[{8*:Wxç/٠Ċծ+LiRWyoV?L# c)< B谛~T!b4i]!2TGvj3ߛֵRW:[|жe˭6zcԕ{#E~W:p'=밒F㭽&!HBŐ! C@*gIAmv;L~B]`ﱳ5k_ݞ dWch6>7-BŐ! 3@Xp&7 B6 C,AZ! C@* T Am k F uj\2kN QlR^@?u\Fvnq)u7W7#z$|]Cqdu覃OA~#>&ŢgJSHi]wlA1v:Ԍ\8[KȻTrS:hPZ4@;#A~d/ŠS~8ޗSz8NpR kP4 GFh֍cРIay?Y_w`!w iX-rޡ@=Ehwv:lЮmOܾ6/(eJLW1LivL}9[p-6Z;>_~Z siwo+,F/./~e?й%f>ǭպRN;mLI~Z ~1Mb]{xzL,N# Ym_v\1F.{RX`ypAhh8S7r͙ڝpPn΂-L6& Xv7c8[֯  Mk~,IQ Np1ܾ'Sڽ>S3X_ E5>duzܿkkCTY7. /ì'Y׿/o )Qqd4'{9?GZPv(aCO 0b[6ng:h#UPXpdL+f-َıˤy9g\v:_ykSQr9qacnl?-qB(Esq95Z46ƃ\("-3iyxgC8tKN@;>1Nq>E l۟Ǚڍ{TZ< }_-<=Rp9XTn߱s<`L.|vX6p#e f 5#.܂z=q3$_%{{_+<|j ?tWfc띭/G\!!FM9-1SZ#.E[ף|vk*zvN1mkVy}Vh~3H[4pOQtjo,B0: z~JP{y/r ]'CV^7RH`:{ef=I9 "o[cƚ8"S"Ps өeC9!~rĉod{88[Pڃ7Gg0028@ & mVB!@$59 k̫w#:=593 u#BQ Fq9#QÂMPSwjxݿ~]BtNy k_;$tb _z|CHj2&?0iGlK,.x]o 7ďo 1IDAT9?aAo۱r[֜j9@fqxXnŽC9؞4tKGJ= ˃aСQc3XFba&F};%/c++Fli;Aj=lja8E0#9X8:}'1gZJ=v(PGNY3\)e׆R%1}aSlv)9+?Ɣ֍:6!rA@1h8/솽ⅹk_,a\1"!hCW^9eLNͤQVCaL=.rԂYK3}G+4[آVǴMiIQ=uET:=MV/w0}A0=W}giWw, ﬑RMA`e%"җ(L/^.?3ݺ.`k8{ԜBѠUh{g<0T9l.U؅\C< aэ9(FQ`i' ]nk7?sU/a^!LX գw9-3Ksl!F\̴վ) yxH=pƽ/|Spň$i=co(`yp8A> ,J?/ng,V<2]wֵO1Gp ik\]l=SfZg7c$#,8u_܍6+_wd2<E?Ƅ/F1F N_p^״qл+[2 oa ~i6,~o| QHo5n LڊJҷxB?Kv*f?lHch|PۯF HvMnJ{vZŊO'v⎖ 1w;Hit)"6WZcN2Xӹ{SkrՂ9/Gao&&7mQ/͎b#e_Ħ'qP|̵zO)yČ'>\~܌$6] LeVdaב\e 0.I/Y8ӳ=. 7-=G\K1;Z5DBDt Q=~Ӳ\*6m|/nQQ֚=q2 #=jpVqڸxj[Pq?ov3 _)/G㾎I݋O),q]?|bByIq< "_tn)AjO+c3_) jԔՌ0I|P0|9$pj&vKK8_ L-)CGDb'1o|v]gƔa1W[c#^ jiloئ>CFvASV<`&X-+fՠo$L7Z{+(t&M5K#L+oGÔk۬>%7Cݓv鋘! 0*,]㾎Iҵ5B< g4I >' P`gifb\\tmvmM1O{\tEEdd_SqԌKF\.@@Ѹ^$ԫђp3vقb\.1r+EFIlFځYyE:_ 2+i5 BxZG#i= Ir T"AH AP1$bHBŐ! C@* T AP1$bHBŐ! C@* T AP1$bHBŐ! C@* T AP1$b4A>\׽ 'z)^5 u/> ׈x!x\`;BOH/7Ax (׽!«y*pOBWEq`6lPh0A z t\AWAH z_ŏ|% (Yh{DHAoCAxiU/?p]",&6-k"BjLN;]7me^&50LEQ+iQ Yo@ɴ SXӏ#A(lALNePll ȷoAH Arkk4_l;; /0E1h4~%B |  t:݄ڦa>'BiHH( A`^.w's=A\Yh&tlW/tK0 b  (L~ FA i TJ(h4 zY^ @u zv E9(BKuoP VT@8`2խ[D?!v]6bIENDB`lxi-tools-1.21/src/gui/lxi-gui/main.cpp000066400000000000000000000022311332035063200177430ustar00rootroot00000000000000#include "mainwindow.h" #include #include #include static MainWindow *main_window; static lxi_info_t lxi_info; void benchmark_progress(void) { main_window->update_progressbar(); } void broadcast(__attribute__((unused)) const char *address, const char *interface) { std::string broadcast_message = "Broadcasting on interface " + std::string(interface); main_window->update_statusbar(broadcast_message.c_str()); } void device(const char *address, const char *id) { main_window->add_instrument(id, address); } void lxi_setup(void) { // Initialize LXI library lxi_init(); // Setup search information callbacks lxi_info.broadcast = &broadcast; lxi_info.device = &device; } void lxi_discover_(int timeout, lxi_discover_t type) { // Search for LXI devices lxi_discover(&lxi_info, timeout, type); } int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; // Setup lxi library lxi_setup(); main_window = &w; w.show(); // Make sure things are resized correctly at startup w.resize(); return a.exec(); } lxi-tools-1.21/src/gui/lxi-gui/mainwindow.cpp000066400000000000000000000536521332035063200212100ustar00rootroot00000000000000#include "mainwindow.h" #include "ui_mainwindow.h" #include "workerthread.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../include/config.h" #include "../../include/scpi.h" #include "../../include/benchmark.h" #include "../../include/screenshot.h" extern void lxi_discover_(int timeout, lxi_discover_t type); extern void benchmark_progress(void); QT_CHARTS_USE_NAMESPACE MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // Create message box for error messages etc. messageBox = new QMessageBox(this); // Setup instrument table widget ui->tableWidget_InstrumentList->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->tableWidget_InstrumentList->verticalHeader()->setVisible(false); ui->tableWidget_InstrumentList->setShowGrid(false); ui->tableWidget_InstrumentList->setContextMenuPolicy(Qt::ActionsContextMenu); QAction* copyIDAction = new QAction("Copy ID", this); QAction* copyIPAction = new QAction("Copy IP", this); QAction* openBrowserAction = new QAction("Open in browser", this); connect(copyIDAction, SIGNAL(triggered()), this, SLOT(InstrumentList_copyID())); connect(copyIPAction, SIGNAL(triggered()), this, SLOT(InstrumentList_copyIP())); connect(openBrowserAction, SIGNAL(triggered()), this, SLOT(InstrumentList_openBrowser())); ui->tableWidget_InstrumentList->addAction(copyIDAction); ui->tableWidget_InstrumentList->addAction(copyIPAction); ui->tableWidget_InstrumentList->addAction(openBrowserAction); // Set up SCPI send action for line edit box lineEdit = ui->comboBox_SCPI_Command->lineEdit(); connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(SCPIsendCommand())); // Setup Screenshot page ui->graphicsView_Screenshot->setStyleSheet("background: transparent"); q_pixmap = new QPixmap(":/images/photo-camera.png"); scene = new QGraphicsScene(); ui->graphicsView_Screenshot->setScene(scene); scene->addPixmap(*q_pixmap); ui->graphicsView_Screenshot->show(); live_view_active = false; // Set up About page labels ui->label_10->setText("Website"); ui->label_10->setTextFormat(Qt::RichText); ui->label_10->setTextInteractionFlags(Qt::TextBrowserInteraction); ui->label_10->setOpenExternalLinks(true); QString string_version; string_version.sprintf("%s (BETA)", VERSION); ui->label_11->setText(string_version); // Set search button icon //ui->pushButton->setIcon(ui->pushButton->style()->standardIcon(QStyle::SP_DialogApplyButton)); // Setup Data Recorder page datarecorder_chart = new QChart(); datarecorder_chart->legend()->hide(); datarecorder_chart->setBackgroundVisible(false); datarecorder_chart->setPlotAreaBackgroundVisible(true); datarecorder_chart->setContentsMargins(0, 0, 0, 0); datarecorder_chart->setBackgroundRoundness(0); QBrush brush; brush.setStyle(Qt::SolidPattern); brush.setColor(Qt::white); datarecorder_chart->setPlotAreaBackgroundBrush(brush); line_series0 = new QLineSeries(); line_series1 = new QLineSeries(); line_series0->setName("Series 0"); line_series1->setName("Series 1"); datarecorder_chart->addSeries(line_series0); datarecorder_chart->addSeries(line_series1); axisX = new QValueAxis(); axisX->setTitleText("Time [s]"); datarecorder_chart->setAxisX(axisX, line_series0); datarecorder_chart->setAxisX(axisX, line_series1); axisX->setRange(0, 1); axisY = new QValueAxis(); //axisY->setTitleText("Value"); datarecorder_chart->setAxisY(axisY, line_series0); datarecorder_chart->setAxisY(axisY, line_series1); axisY->setRange(0, 1); //datarecorder_chart->setTitle("Data recorder chart"); ui->chartView->setChart(datarecorder_chart); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(DataRecorder_Update())); data_recorder_active = false; data_recorder_sample_counter = 0; ui->chartView->setRenderHint(QPainter::Antialiasing); ui->chartView->setRubberBand(QChartView::HorizontalRubberBand); ui->chartView->setContextMenuPolicy(Qt::ActionsContextMenu); QPalette pal; pal.setColor(QPalette::Window, QColor(0,0,0,0)); ui->chartView->setPalette(pal); ui->chartView->setBackgroundRole(QPalette::Window); QAction* zoomInAction = new QAction("Zoom In", this); QAction* zoomOutAction = new QAction("Zoom Out", this); QAction* zoomResetAction = new QAction("Zoom Reset", this); connect(zoomInAction, SIGNAL(triggered()), this, SLOT(DataRecorder_zoomIn())); connect(zoomOutAction, SIGNAL(triggered()), this, SLOT(DataRecorder_zoomOut())); connect(zoomResetAction, SIGNAL(triggered()), this, SLOT(DataRecorder_zoomReset())); ui->chartView->addAction(zoomInAction); ui->chartView->addAction(zoomOutAction); ui->chartView->addAction(zoomResetAction); ui->pushButton_DataRecorder_Save->setEnabled(false); // Register screenshot plugins screenshot_register_plugins(); } // Handle resize events void MainWindow::resizeEvent(QResizeEvent *event) { resize(); QMainWindow::resizeEvent(event); } // Handle resize void MainWindow::resize() { ui->tableWidget_InstrumentList->setColumnWidth(0, ui->tableWidget_InstrumentList->width()*4/5); ui->tableWidget_InstrumentList->setColumnWidth(1, ui->tableWidget_InstrumentList->width()/5-1); } // Connect int MainWindow::LXI_connect() { int timeout = ui->spinBox_SCPITimeout->value() * 1000; char *ip = (char *) IP.toUtf8().data(); // Connect lxi_device = lxi_connect(ip, 0, NULL, timeout, VXI11); if (lxi_device == LXI_ERROR) { messageBox->critical(this, "Error", "Failed to connect!"); return -1; } return 0; } // Send command int MainWindow::LXI_send_receive(QString *command, QString *response, int timeout) { char response_buffer[10000] = ""; char command_buffer[10000] = ""; int length; if (command->size() > 0) { // Prepare SCPI command string strcpy(command_buffer, command->toUtf8().constData()); strip_trailing_space(command_buffer); // Send command lxi_send(lxi_device, command_buffer, strlen(command_buffer), timeout); // If command is a question then receive response if (question(command_buffer)) { length = lxi_receive(lxi_device, response_buffer, 10000, timeout); if (length < 0) { messageBox->critical(this, "Error", "Failed to receive message!"); lxi_disconnect(lxi_device); return -1; } // Return response *response = QString::fromStdString(response_buffer); } } return 0; } // Disconnect int MainWindow::LXI_disconnect() { if (lxi_disconnect(lxi_device) != LXI_OK) return -1; else return 0; } void MainWindow::SCPIsendCommand(QString *command) { int timeout = ui->spinBox_SCPITimeout->value() * 1000; QString request, response; int status; if (IP.isEmpty()) { messageBox->warning(this, "Warning", "Please select instrument!"); return; } LXI_connect(); // Print request request = ""; request.append(*command); request.append("
"); ui->textBrowser_SCPI_Response->insertHtml(request); ui->textBrowser_SCPI_Response->ensureCursorVisible(); status = LXI_send_receive(command, &response, timeout); if (status == 0) { if (response.size() > 0) { // Print response response.insert(0, ""); response.append("
"); ui->textBrowser_SCPI_Response->insertHtml(response); ui->textBrowser_SCPI_Response->ensureCursorVisible(); } LXI_disconnect(); } } void MainWindow::SCPIsendCommand() { if (IP.isEmpty()) { messageBox->warning(this, "Warning", "Please select instrument!"); return; } QString command(ui->comboBox_SCPI_Command->currentText()); SCPIsendCommand(&command); } void MainWindow::SCPIsendCommand(const char *command) { if (IP.isEmpty()) { messageBox->warning(this, "Warning", "Please select instrument!"); return; } QString *command_ = new QString(command); SCPIsendCommand(command_); } void MainWindow::InstrumentList_copyID() { QClipboard *clipboard = QApplication::clipboard(); QTableWidgetItem *item = ui->tableWidget_InstrumentList->item(ui->tableWidget_InstrumentList->currentRow(), 0); clipboard->setText(item->text()); } void MainWindow::InstrumentList_copyIP() { QClipboard *clipboard = QApplication::clipboard(); QTableWidgetItem *item = ui->tableWidget_InstrumentList->item(ui->tableWidget_InstrumentList->currentRow(), 1); clipboard->setText(item->text()); } void MainWindow::InstrumentList_openBrowser() { QTableWidgetItem *item = ui->tableWidget_InstrumentList->item(ui->tableWidget_InstrumentList->currentRow(), 1); QString URL = "http://" + item->text(); QDesktopServices::openUrl(QUrl(URL)); } MainWindow::~MainWindow() { delete ui; } // Search button void MainWindow::on_pushButton_Search_clicked() { int timeout = ui->spinBox_SearchTimeout->value() * 1000; ui->tableWidget_InstrumentList->clearContents(); ui->tableWidget_InstrumentList->setRowCount(0); ui->pushButton_Search->setText("Searching"); ui->pushButton_Search->repaint(); lxi_discover_(timeout, ui->checkBox_mDNS->isChecked() ? DISCOVER_MDNS : DISCOVER_VXI11); ui->statusBar->clearMessage(); ui->pushButton_Search->setText("Search"); IP.clear(); } void MainWindow::add_instrument(const char *id, const char *address) { QString instrument_id = QString::fromStdString(id); QString instrument_address = QString::fromStdString(address); ui->tableWidget_InstrumentList->insertRow(0); ui->tableWidget_InstrumentList->setItem(0,0, new QTableWidgetItem(instrument_id)); ui->tableWidget_InstrumentList->setItem(0,1, new QTableWidgetItem(instrument_address)); ui->tableWidget_InstrumentList->item(0,1)->setTextAlignment(Qt::AlignCenter); } void MainWindow::update_statusbar(const char *message) { QString status_message = QString::fromStdString(message); ui->statusBar->showMessage(status_message); } // SCPI Send button void MainWindow::on_pushButton_SCPI_Send_clicked() { SCPIsendCommand(); } void MainWindow::on_tableWidget_InstrumentList_cellClicked(__attribute__((unused)) int row, __attribute__((unused)) int column) { QTableWidgetItem *item = ui->tableWidget_InstrumentList->item(ui->tableWidget_InstrumentList->currentRow(), 1); // Update IP IP = item->text(); } void MainWindow::update_progressbar() { ui->progressBar->setValue(ui->progressBar->value() + 1); } // Benchmark start void MainWindow::on_pushButton_Benchmark_Start_clicked() { double result; QString q_result; QMessageBox messageBox(this); if (IP.isEmpty()) { messageBox.warning(this, "Warning", "Please select instrument!"); return; } // Reset ui->label_6->clear(); ui->progressBar->setValue(0); ui->progressBar->setMaximum(ui->spinBox_BenchmarkRequests->value()); // Run benchmark ui->pushButton_Benchmark_Start->setText("Testing"); ui->pushButton_Benchmark_Start->repaint(); benchmark(IP.toUtf8().data(), 0, 1000, VXI11, ui->spinBox_BenchmarkRequests->value(), false, &result, benchmark_progress); ui->pushButton_Benchmark_Start->setText("Start"); ui->pushButton_Benchmark_Start->repaint(); // Print result q_result = QString::number(result, 'f', 1); ui->label_6->setText(q_result + " requests/second"); } void MainWindow::Screenshot_UpdateUI(QPixmap pixmap, QString image_format, QString image_filename) { // Update UI // TODO: Refactor common screenshot code int width = ui->graphicsView_Screenshot->width(); int height = ui->graphicsView_Screenshot->height() - 2; screenshotImageFormat.clear(); screenshotImageFormat.append(image_format); screenshotImageFilename.clear(); screenshotImageFilename.append(image_filename); pixmap = pixmap.scaled(QSize(std::min(width, pixmap.width()), std::min(height, pixmap.height())), Qt::KeepAspectRatio, Qt::SmoothTransformation); *q_pixmap = pixmap; if (!pixmapItem) pixmapItem = scene->addPixmap(pixmap); else pixmapItem->setPixmap(pixmap); ui->graphicsView_Screenshot->show(); } // Live View void MainWindow::on_pushButton_Screenshot_LiveView_clicked() { static WorkerThread *workerthread; QMessageBox messageBox(this); if (IP.isEmpty()) { messageBox.warning(this, "Warning", "Please select instrument!"); return; } if (live_view_active) { // Stop live view // Wait for worker thread to finish workerthread->live = false; workerthread->wait(); delete workerthread; ui->pushButton_Screenshot_LiveView->setText("Live View"); ui->pushButton_Screenshot_LiveView->repaint(); // Enable buttons ui->pushButton_Screenshot_Save->setEnabled(true); ui->pushButton_Screenshot_TakeScreenshot->setEnabled(true); live_view_active = false; } else { // Start live view ui->pushButton_Screenshot_LiveView->setText("Stop"); ui->pushButton_Screenshot_LiveView->repaint(); // Disable buttons ui->pushButton_Screenshot_Save->setEnabled(false); ui->pushButton_Screenshot_TakeScreenshot->setEnabled(false); // Start worker thread int timeout = ui->spinBox_ScreenshotTimeout->value() * 1000; workerthread = new WorkerThread; connect(workerthread, SIGNAL(requestUpdateUI(QPixmap, QString, QString)), this, SLOT(Screenshot_UpdateUI(QPixmap, QString, QString))); workerthread->startLiveUpdate(IP, timeout); live_view_active = true; } } // Take screenshot void MainWindow::on_pushButton_Screenshot_TakeScreenshot_clicked() { char image_buffer[0x200000]; int image_size = 0; char image_format[10]; char image_filename[1000]; int timeout = ui->spinBox_ScreenshotTimeout->value() * 1000; QMessageBox messageBox(this); if (IP.isEmpty()) { messageBox.warning(this, "Warning", "Please select instrument!"); return; } // Capture screenshot screenshot(IP.toUtf8().data(), "", "", timeout, false, image_buffer, &image_size, image_format, image_filename); screenshotImageFormat.clear(); screenshotImageFormat.append(image_format); screenshotImageFilename.clear(); screenshotImageFilename.append(image_filename); int width = ui->graphicsView_Screenshot->width(); int height = ui->graphicsView_Screenshot->height() - 2; q_pixmap->loadFromData((const uchar*) image_buffer, image_size, "", Qt::AutoColor); *q_pixmap = q_pixmap->scaled(QSize(std::min(width, q_pixmap->width()), std::min(height, q_pixmap->height())), Qt::KeepAspectRatio, Qt::SmoothTransformation); if (!pixmapItem) pixmapItem = scene->addPixmap(*q_pixmap); else pixmapItem->setPixmap(*q_pixmap); ui->graphicsView_Screenshot->show(); // Enable buttons ui->pushButton_Screenshot_Save->setEnabled(true); } // Save screenshot void MainWindow::on_pushButton_Screenshot_Save_clicked() { QString filename = QFileDialog::getSaveFileName(this, "Save file", screenshotImageFilename, "." + screenshotImageFormat); QFile file(filename); file.open(QIODevice::WriteOnly); q_pixmap->save(&file, screenshotImageFormat.toUtf8().constData()); file.close(); } // *CLS void MainWindow::on_pushButton_SCPI_CLS_clicked() { SCPIsendCommand("*CLS"); } // *ESE void MainWindow::on_pushButton_SCPI_ESE_clicked() { QString command; bool ok; int i = QInputDialog::getInt(this, "*ESE ", "Value:", 0, 0, 255, 1, &ok); if (ok) { command = tr("*ESE %1").arg(i); SCPIsendCommand(&command); } } // *ESE? void MainWindow::on_pushButton_SCPI_ESEQuestion_clicked() { SCPIsendCommand("*ESE?"); } // *ESR? void MainWindow::on_pushButton_SCPI_ESRQuestion_clicked() { SCPIsendCommand("*ESR?"); } // *IDN? void MainWindow::on_pushButton_SCPI_IDNQuestion_clicked() { SCPIsendCommand("*IDN?"); } // *OPC void MainWindow::on_pushButton_SCPI_OPC_clicked() { SCPIsendCommand("*OPC"); } // *OPC? void MainWindow::on_pushButton_SCPI_OPCQuestion_clicked() { SCPIsendCommand("*OPC?"); } // Blank button void MainWindow::on_pushButton_SCPI_Blank_clicked() { } // *RST void MainWindow::on_pushButton_SCPI_RST_clicked() { SCPIsendCommand("*RST"); } // *SRE void MainWindow::on_pushButton_SCPI_SRE_clicked() { QString command; bool ok; int i = QInputDialog::getInt(this, "*SRE ", "Value:", 0, 0, 255, 1, &ok); if (ok) { command = tr("*SRE %1").arg(i); SCPIsendCommand(&command); } } // *SRE? void MainWindow::on_pushButton_SCPI_SREQuestion_clicked() { SCPIsendCommand("*SRE?"); } // *STB? void MainWindow::on_pushButton_SCPI_STBQuestion_clicked() { SCPIsendCommand("*STB?"); } // *TST? void MainWindow::on_pushButton_SCPI_TSTQuestion_clicked() { SCPIsendCommand("*TST?"); } // *WAI void MainWindow::on_pushButton_SCPI_WAI_clicked() { SCPIsendCommand("*WAI"); } void MainWindow::on_pushButton_SCPI_SystemVersionQuery_clicked() { SCPIsendCommand(":SYSTem:VERSion?"); } void MainWindow::on_pushButton_SCPI_SystemErrorQuery_clicked() { SCPIsendCommand(":SYSTem:ERRor?"); } void MainWindow::on_pushButton_SCPI_SystemErrorNextQuery_clicked() { SCPIsendCommand(":SYSTem:ERRor:NEXT?"); } void MainWindow::on_pushButton_SCPIP_StatusOperationQuery_clicked() { SCPIsendCommand(":STATus:OPERation?"); } void MainWindow::on_pushButton_SCPI_StatusPreset_clicked() { SCPIsendCommand(":SYSTem:PRESet"); } // Data recorder start void MainWindow::on_pushButton_DataRecorder_Start_clicked() { if (IP.isEmpty()) { messageBox->warning(this, "Warning", "Please select instrument!"); return; } if (data_recorder_active) { // Stop recording timer->stop(); ui->pushButton_DataRecorder_Start->setText("Start"); LXI_disconnect(); // Enable inputs ui->lineEdit->setEnabled(true); ui->lineEdit_2->setEnabled(true); if (line_series0->count() || line_series1->count()) ui->pushButton_DataRecorder_Save->setEnabled(true); else ui->pushButton_DataRecorder_Save->setEnabled(false); ui->spinBox_DataRecorderRate->setEnabled(true); } else { // Reset line_series0->clear(); line_series1->clear(); axisX->setRange(0, 1); axisY->setRange(0, 1); data_recorder_first_sample = true; data_recorder_sample_counter = 0; // Start recording time.start(); data_recorder_time_slice = 1000 / ui->spinBox_DataRecorderRate->value(); timer->start(data_recorder_time_slice); ui->pushButton_DataRecorder_Start->setText("Stop"); LXI_connect(); // Disable inputs ui->lineEdit->setEnabled(false); ui->lineEdit_2->setEnabled(false); ui->pushButton_DataRecorder_Save->setEnabled(false); ui->spinBox_DataRecorderRate->setEnabled(false); } ui->pushButton_DataRecorder_Start->repaint(); data_recorder_active = !data_recorder_active; } void MainWindow::DataRecorder_Update() { QString response; double elapsed_time = 0; double sample_max, sample0 = 0, sample1 = 0; if (!ui->lineEdit->text().isEmpty()) { // Retrieve sample 1 QString command = ui->lineEdit->text(); LXI_send_receive(&command, &response, 1000); elapsed_time = ((double) time.elapsed()) / 1000; sample0 = response.toDouble(); line_series0->append(elapsed_time, sample0); } if (!ui->lineEdit_2->text().isEmpty()) { // Retrieve sample 2 QString command = ui->lineEdit_2->text(); LXI_send_receive(&command, &response, 1000); elapsed_time = ((double) time.elapsed()) / 1000; sample1 = response.toDouble(); line_series1->append(elapsed_time, sample1); } // Update chart sample_max = sample0 < sample1 ? sample1 : sample0; if (data_recorder_first_sample) { axisY->setMax(sample_max); data_recorder_first_sample = false; } if (sample_max > axisY->max()) { axisY->setMax(sample_max); axisY->applyNiceNumbers(); } axisX->setMax(elapsed_time); data_recorder_sample_counter++; } // Data recorder save void MainWindow::on_pushButton_DataRecorder_Save_clicked() { int i; QString filename = QFileDialog::getSaveFileName(this, "Save file", "data.csv", "Text CSV (.csv)"); QFile file(filename); file.open(QIODevice::WriteOnly|QFile::Truncate); QTextStream stream(&file); for (i=0;iat(i).x(); if (line_series0->count()) stream << "," << line_series0->at(i).y(); if (line_series1->count()) stream << "," << line_series1->at(i).y(); stream << "\n"; } file.close(); } void MainWindow::DataRecorder_zoomOut() { ui->chartView->chart()->zoomOut(); } void MainWindow::DataRecorder_zoomIn() { ui->chartView->chart()->zoomIn(); } void MainWindow::DataRecorder_zoomReset() { ui->chartView->chart()->zoomReset(); } void MainWindow::on_pushButton_SCPI_Clear_clicked() { ui->comboBox_SCPI_Command->lineEdit()->clear(); } lxi-tools-1.21/src/gui/lxi-gui/mainwindow.h000066400000000000000000000065441332035063200206530ustar00rootroot00000000000000#ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include #include #include #include #include #include #include #include QT_CHARTS_USE_NAMESPACE namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); void add_instrument(const char *id, const char *address); void update_statusbar(const char *message); void update_progressbar(); void pushButton_reset(); void resizeEvent(QResizeEvent *event); void resize(); private slots: void on_tableWidget_InstrumentList_cellClicked(int row, int column); void InstrumentList_copyID(); void InstrumentList_copyIP(); void InstrumentList_openBrowser(); int LXI_connect(); int LXI_send_receive(QString *command, QString *response, int timeout); int LXI_disconnect(); void DataRecorder_zoomOut(); void DataRecorder_zoomIn(); void DataRecorder_zoomReset(); void DataRecorder_Update(); void on_pushButton_Search_clicked(); void on_pushButton_Benchmark_Start_clicked(); void on_pushButton_Screenshot_TakeScreenshot_clicked(); void on_pushButton_Screenshot_Save_clicked(); void on_pushButton_Screenshot_LiveView_clicked(); void Screenshot_UpdateUI(QPixmap pixmap, QString image_format, QString image_filename); void SCPIsendCommand(const char *command); void SCPIsendCommand(QString *command); void SCPIsendCommand(); void on_pushButton_SCPI_Send_clicked(); void on_pushButton_SCPI_Clear_clicked(); void on_pushButton_SCPI_CLS_clicked(); void on_pushButton_SCPI_ESE_clicked(); void on_pushButton_SCPI_ESEQuestion_clicked(); void on_pushButton_SCPI_ESRQuestion_clicked(); void on_pushButton_SCPI_IDNQuestion_clicked(); void on_pushButton_SCPI_OPC_clicked(); void on_pushButton_SCPI_OPCQuestion_clicked(); void on_pushButton_SCPI_Blank_clicked(); void on_pushButton_SCPI_RST_clicked(); void on_pushButton_SCPI_SRE_clicked(); void on_pushButton_SCPI_SREQuestion_clicked(); void on_pushButton_SCPI_STBQuestion_clicked(); void on_pushButton_SCPI_TSTQuestion_clicked(); void on_pushButton_SCPI_WAI_clicked(); void on_pushButton_DataRecorder_Start_clicked(); void on_pushButton_DataRecorder_Save_clicked(); void on_pushButton_SCPI_SystemVersionQuery_clicked(); void on_pushButton_SCPI_SystemErrorQuery_clicked(); void on_pushButton_SCPI_SystemErrorNextQuery_clicked(); void on_pushButton_SCPIP_StatusOperationQuery_clicked(); void on_pushButton_SCPI_StatusPreset_clicked(); private: Ui::MainWindow *ui; QMessageBox *messageBox; QLineEdit *lineEdit; QString IP; QPixmap *q_pixmap; QString screenshotImageFormat; QString screenshotImageFilename; QLineSeries *line_series0; QLineSeries *line_series1; QChart *datarecorder_chart; int lxi_device; QTimer *timer; QTime time; bool live_view_active; bool data_recorder_active; int data_recorder_sample_counter; double data_recorder_time_slice; bool data_recorder_first_sample = true; QGraphicsScene* scene; QGraphicsPixmapItem* pixmapItem = NULL; QValueAxis *axisX; QValueAxis *axisY; }; #endif // MAINWINDOW_H lxi-tools-1.21/src/gui/lxi-gui/mainwindow.ui000066400000000000000000001324721332035063200210410ustar00rootroot00000000000000 MainWindow 0 0 880 800 0 0 700 600 LXI Tools - lxi-gui QLayout::SetNoConstraint :/images/lxi-tools_256x256.png true Qt::Horizontal QSizePolicy::Fixed 4 20 0 0 Qt::ScrollBarAlwaysOff false QAbstractItemView::SingleSelection QAbstractItemView::SelectRows false true 542 false 57 false true false Instrument ID AlignCenter IP Address AlignCenter Qt::Horizontal 350 20 Search Qt::Horizontal QSizePolicy::Fixed 10 20 false mDNS false Qt::Horizontal QSizePolicy::Minimum 40 20 Qt::Vertical QSizePolicy::Fixed 20 4 0 0 0 SCPI SCPI Command: 0 0 0 0 true true Clear Send Request/Response: IBeamCursor Qt::TextBrowserInteraction IEEE 488.2 Common Commands: <html><head/><body><p><span style=" font-size:large; font-weight:600;">Clear Status Command</span></p><p>Clears the instrument status byte by emptying the error queue and clearing all event registers. Also cancels any preceding *OPC command or query.</p></body></html> *CLS <html><head/><body><p><span style=" font-size:large; font-weight:600;">Event Status Enable Command</span></p><p>Sets bits in the standard event status enable register. The integer &lt;value&gt; represents the sum of the bits that will be enabled.</p></body></html> *ESE <value> <html><head/><body><p><span style=" font-size:large; font-weight:600;">Event Status Enable Query</span></p><p>Reads the value of the standard event enable register.</p></body></html> *ESE? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Event Status Register Query</span></p><p>Reads and clears the event status register.</p></body></html> *ESR? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Identification Query</span></p><p>Returns a string that uniquely identifies the instrument. The string is of the form &quot;&lt;manufacturer name&gt;,&lt;model number&gt;,&lt;serial number&gt;,&lt;software revision&gt;&quot;.</p></body></html> *IDN? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Operation Complete Command</span></p><p>Generates the OPC message in the standard event status register when all pending overlapped operations have been completed.</p></body></html> *OPC <html><head/><body><p><span style=" font-size:large; font-weight:600;">Operation Complete Query</span></p><p>Returns &quot;1&quot; when all pending overlapped operations have been completed.</p></body></html> *OPC? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Reset Command</span></p><p>Executes a device reset and cancels any pending *OPC command or query.</p></body></html> *RST <html><head/><body><p><span style=" font-size:large; font-weight:600;">Service Request Enable Command</span></p><p>Sets bits in the service request enable register. The integer &lt;value&gt; represents the sum of the bits that will be enabled.</p></body></html> *SRE <value> <html><head/><body><p><span style=" font-size:large; font-weight:600;">Service Request Enable Query</span></p><p>Reads the value of the service request enable register.</p></body></html> *SRE? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Status Byte Query</span></p><p>Reads the value of the instrument status byte.</p></body></html> *STB? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Self-Test Query</span></p><p>This query initiates the internal self-test and returns the result. A &quot;0&quot; indicates all tests passed. Any other value indicates one or more tests failed.</p></body></html> *TST? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Wait-to-Continue Command</span></p><p>Prohibits the instrument from executing any new commands until all pending overlapped commands have been completed.</p></body></html> *WAI false SCPI 1999.0 Commands: <html><head/><body><p><span style=" font-size:large; font-weight:600;">System Version Query</span></p><p>Returns supported SCPI version.</p></body></html> :SYSTem:VERSion? <html><head/><body><p><span style=" font-size:large; font-weight:600;">System Error Query</span></p><p>Returns and clears the latest error message from the instrument.</p></body></html> :SYSTem:ERRor? <html><head/><body><p><span style=" font-size:large; font-weight:600;">System Error Next Query</span></p><p><br/></p></body></html> :SYSTem:ERRor:NEXT? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Status Operation Query</span></p><p><br/></p></body></html> :STATus:OPERation? <html><head/><body><p><span style=" font-size:large; font-weight:600;">Status Preset Command</span></p><p><br/></p></body></html> :STATus:PRESet Screenshot Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff QGraphicsView::ScrollHandDrag Qt::Horizontal 40 20 Live View Take screenshot false Save Qt::Horizontal 40 20 Benchmark Abyssinica SIL 18 50 false Qt::AlignCenter Qt::Horizontal QSizePolicy::Preferred 40 20 0 0 1000 0 false Qt::Horizontal QSizePolicy::Preferred 40 20 true Send Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Qt::AlignCenter 1 9999 1000 requests Qt::Horizontal 40 20 Start Qt::Horizontal 40 20 Data Recorder 0 0 Data 1: SCPI Command Data 2: true SCPI Command Qt::Horizontal 40 20 1 100 10 samples/second Qt::Horizontal 40 20 Qt::Horizontal 40 20 Start Save Qt::Horizontal 40 20 Settings Qt::Vertical 20 164 Qt::Horizontal 273 20 Search Timeout: Qt::AlignCenter 999 1 second(s) SCPI Timeout: Qt::AlignCenter 999 3 second(s) Screenshot Timeout: Qt::AlignCenter 999 15 second(s) Search mDNS service: _lxi._tcp _hislip._tcp _http._tcp _scpi-raw._tcp _scpi-telnet._tcp _vxi-11._tcp Qt::Horizontal 272 20 Qt::Vertical 20 163 About Qt::Vertical 20 40 Qt::Horizontal 40 20 QLayout::SetFixedSize 11 75 true lxi-gui Qt::AlignCenter VERSION Qt::AlignCenter Qt::Vertical 20 40 Tools for managing LXI compatible instruments Qt::AlignCenter Qt::Vertical 20 40 lxi-tools.github.io Qt::AlignCenter Qt::Horizontal 40 20 Qt::Vertical 20 40 false QtCharts::QChartView QWidget
QtCharts/QChartView
1
lxi-tools-1.21/src/gui/lxi-gui/photo-camera.png000066400000000000000000000120211332035063200213760ustar00rootroot00000000000000PNG  IHDRI#sBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATx{t}ǿ][eGBZަ`'MedŊS|ߟ`>3_p՚ u`ŤI̟?TWfϞ=8ahdH$N0n k xrxxx'oqR̬0e0Wnu"ZJ\? = s#lS^΢Eӛh3=` \.׷7 vkk?piC|Z^烝qlټyIf0ăK:::j' 96$b{{y/1&a1!itmڴ鶏CҼ!4:MC`9 d```zV{L r@WDd!wИ]MD/1fm{Ge˖Qhkk`""ID2f8ʾҜh RiFhU(L7F*6|.V3!-#DfRt3ߊxX.fvkګv3И!acYaW\3T*a!|=Nl(A1G0_ x)G Ǩ\.SOM}de%յֲb)#j s EO۶ݽAfGlSϪ@D汾{eu$ +md2U LfcKKE=Ix?cYָ?ǘN ^,+ḵk͛;۶Rmoݺ~fttעu$ oXfGT ;L-"ZȘv]Gry̆N +2P![vUEa>pMeim&UV8A^1crYnmm}jCA I+WVFEY- ?hN\Np99N'#x>u5tAWUVYF  N_JuORVC#{vjedĖ9Acت aJ(yfRF숸uKd,KJ3պBP"Ot\R/fU ,Jf<Ǿ2})7D3qUA/B9̜W!4R 1A~+c 鶤|_t\."ߊ K=`3DǍ{y qSZ`Enj SB!'ETV}rJ 46;P0*NyqEd/BN8JB6]+2J1vqCwq.AYIq]?+2fL9mŊSq̈́6!"'>xͤZ&zA kY*i "4wl3[R8Vd )%A,s-1"&戾lOD+^c"΅21mۢOJc ]l C6n(z.8^z1pR61dAM&/G3BAh3< N6Ä1 N#3 'exqF1HPKDc@̷1H "Y.1^ l>DdvLfk _r~> @UAKz{{d̰͛'C\ߐ;hkJy/~Zm-a9ɇAp9ߓ_5:nV'om ݲ[yYZ]d&d؞4)ՓM6%e5d󼧙>e5F'/;#c[:":Bv;*ѽ}u]C\.eY,m۟rQTfRF{a#lvqX-t!xrd2oDѠwBpN8B몺E\qQR0 lDtU{us>IA^#r{Q7wp:)&e~d>}}}Um$ B6]DDʾma,{v,{>(6X 0 -W  3yE9U&Q"z.CK@(IM0R:&uDM `RR.b>iZ&,*W*ʠR `đTOR._lHA3mT/Ӄ (8I$ jsQ,G(JZ)g֢ɉ-J[<;2 gj-q#$?Z9l ㄈhooZXzoC21{6̼LQ.'ag̼aLDAwi x]!fjժ}F"!F|qяT 2B- {l6;> poww:`-m/^$!l=A2F" aW-Z^!iC]Q28cۿ&jzD`-[,wA<`tYb/J]Ҭy;͋dV {0^;3d{\]$A[6 qw=fVt^B}B1vC z}֞ٞJ2\{)`Pkwvvyo\OmY<z߇l5dd@#aUV Iߴ8pIENDB`lxi-tools-1.21/src/gui/lxi-gui/workerthread.cpp000066400000000000000000000016171332035063200215270ustar00rootroot00000000000000#include "mainwindow.h" #include "workerthread.h" #include #include #include #include "../../include/screenshot.h" void WorkerThread::run() { char image_buffer[0x200000]; int image_size = 0; char image_format[10]; char image_filename[1000]; QPixmap pixmap; while (live) { // Capture screenshot // TODO: Optmize so we avoid plugin autodetect overhead at every capture screenshot(IP.toUtf8().data(), "", "", timeout, false, image_buffer, &image_size, image_format, image_filename); pixmap.loadFromData((const uchar*) image_buffer, image_size, "", Qt::AutoColor); emit requestUpdateUI(pixmap, QString(image_format), QString(image_filename)); } } void WorkerThread::startLiveUpdate(const QString IP, int timeout) { this->IP = IP; this->timeout = timeout; this->live = true; this->start(); } lxi-tools-1.21/src/gui/lxi-gui/workerthread.h000066400000000000000000000006761332035063200212000ustar00rootroot00000000000000#ifndef WORKERTHREAD_H #define WORKERTHREAD_H #include #include #include #include class WorkerThread : public QThread { Q_OBJECT public: void run() override; void startLiveUpdate(const QString IP, int timeout); bool live = true; private: QString IP; int timeout; signals: void requestUpdateUI(QPixmap pixmap, QString format, QString filename); }; #endif // WORKERTHREAD_H lxi-tools-1.21/src/include/000077500000000000000000000000001332035063200155765ustar00rootroot00000000000000lxi-tools-1.21/src/include/benchmark.h000066400000000000000000000040421332035063200177010ustar00rootroot00000000000000/* * Copyright (c) 2016-2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef BENCHMARK_H #define BENCHMARK_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include "options.h" #include "error.h" #include int benchmark(char *ip, int port, int timeout, lxi_protocol_t protocol, int count, bool no_gui, double *result, void (*progress)(void)); #ifdef __cplusplus } #endif #endif lxi-tools-1.21/src/include/discover.h000066400000000000000000000032171332035063200175700ustar00rootroot00000000000000/* * Copyright (c) 2016-2018 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef DISCOVER_H #define DISCOVER_H #include int discover(bool mdns, int timeout); #endif lxi-tools-1.21/src/include/error.h000066400000000000000000000032371332035063200171050ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ERROR_H #define ERROR_H #define error_printf(format, args...) \ fprintf(stderr, "Error: " format, ## args) #endif lxi-tools-1.21/src/include/lxilua.h000066400000000000000000000001351332035063200172440ustar00rootroot00000000000000#include #include #include int luaopen_lxilua(lua_State *L); lxi-tools-1.21/src/include/options.h000066400000000000000000000042711332035063200174460ustar00rootroot00000000000000/* * Copyright (c) 2016 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef OPTIONS_H #define OPTIONS_H #include #include #include /* Options */ struct option_t { int command; int timeout; char ip[500]; char scpi_command[500]; bool hex; bool interactive; bool run_script; char *script_filename; char lua_script_filename[1000]; char *plugin_name; bool list; char screenshot_filename[1000]; lxi_protocol_t protocol; int port; bool mdns; int count; }; enum command_t { DISCOVER, SCPI, SCREENSHOT, BENCHMARK, RUN, NO_COMMAND }; extern struct option_t option; void parse_options(int argc, char *argv[]); #endif lxi-tools-1.21/src/include/run.h000066400000000000000000000035431332035063200165600ustar00rootroot00000000000000/* * Copyright (c) 2016-2018 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef RUN_H #define RUN_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include "options.h" #include "error.h" #include int run(char *filename, int timeout); #ifdef __cplusplus } #endif #endif lxi-tools-1.21/src/include/scpi.h000066400000000000000000000043261332035063200167120ustar00rootroot00000000000000/* * Copyright (c) 2016-2018 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SCPI_H #define SCPI_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include "options.h" #include "error.h" #include int scpi(char *ip, int port, int timeout, lxi_protocol_t protocol, char *command); int enter_interactive_mode(char *ip, int port, int timeout, lxi_protocol_t protocol); int run_script(char *ip, int port, int timeout, lxi_protocol_t protocol, char *filename); void strip_trailing_space(char *line); int question(char *string); #ifdef __cplusplus } #endif #endif lxi-tools-1.21/src/include/screenshot.h000066400000000000000000000043341332035063200201300ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SCREENSHOT_H #define SCREENSHOT_H #ifdef __cplusplus extern "C" { #endif #include void screenshot_register_plugins(void); void screenshot_list_plugins(void); int screenshot(char *address, char *plugin_name, char *filename, int timeout, bool no_gui, void *image_buffer, int *image_size, char *image_format, char *image_filename); // Screenshot helper function used by plugins to dump image file void screenshot_file_dump(void *data, int length, char *format); struct screenshot_plugin { const char *name; const char *description; const char *regex; int (*screenshot)(char *address, int timeout); }; #ifdef __cplusplus } #endif #endif lxi-tools-1.21/src/lxilua.c000066400000000000000000000120451332035063200156170ustar00rootroot00000000000000/* * Copyright (c) 2018, Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "options.h" #include "error.h" #define RESPONSE_LENGTH_MAX 0x400000 extern int question(const char *string); // lua: device = lxi_connect(address) static int connect(lua_State *L) { int device; const char *address = lua_tostring(L, 1); // Connect to LXI instrument using VXI11 device = lxi_connect(address, 0, NULL, option.timeout, VXI11); if (device == LXI_ERROR) error_printf("Failed to connect\n"); // Return status lua_pushinteger(L, device); return 1; } // lua: lxi_disconnect(device) static int disconnect(lua_State *L) { int status = 0; int device = lua_tointeger(L, 1); // Disconnect status = lxi_disconnect(device); // Return status lua_pushnumber(L, status); return 1; } // lua: scpi(device, command) static int scpi(lua_State *L) { char response[RESPONSE_LENGTH_MAX]; int status = 0, length; int device = lua_tointeger(L, 1); const char *command = lua_tostring(L, 2); // Send SCPI command length = lxi_send(device, command, strlen(command), option.timeout); if (length < 0) { error_printf("Failed to send message\n"); status = length; goto error; } // Only expect response in case we are firing a question command if (question(command)) { length = lxi_receive(device, response, RESPONSE_LENGTH_MAX, option.timeout); if (length < 0) { error_printf("Failed to receive message\n"); status = length; goto error; } } if (length > 0) { // Strip newline if (response[length-1] == '\n') response[--length] = 0; // Strip carriage return if (response[length-1] == '\r') response[--length] = 0; } lua_pushlstring(L, response, length); return 1; error: // Return status lua_pushnumber(L, status); return 1; } // lua: scpi_raw(device, command) static int scpi_raw(lua_State *L) { char response[RESPONSE_LENGTH_MAX]; int status = 0, length; int device = lua_tointeger(L, 1); const char *command = lua_tostring(L, 2); // Send SCPI command length = lxi_send(device, command, strlen(command), option.timeout); if (length < 0) { error_printf("Failed to send message\n"); status = length; goto error; } // Only expect response in case we are firing a question command if (question(command)) { length = lxi_receive(device, response, RESPONSE_LENGTH_MAX, option.timeout); if (length < 0) { error_printf("Failed to receive message\n"); status = length; goto error; } } lua_pushlstring(L, response, length); return 1; error: // Return status lua_pushnumber(L, status); return 1; } // lua: sleep(seconds) static int sleep_(lua_State *L) { long seconds = lua_tointeger(L, 1); sleep(seconds); return 0; } // lua: msleep(miliseconds) static int msleep(lua_State *L) { long mseconds = lua_tointeger(L, 1); long useconds = mseconds * 1000; usleep(useconds); return 0; } int luaopen_lxilua(lua_State *L) { lua_register(L, "connect", connect); lua_register(L, "disconnect", disconnect); lua_register(L, "scpi", scpi); lua_register(L, "scpi_raw", scpi_raw); lua_register(L, "sleep", sleep_); lua_register(L, "msleep", msleep); return 0; } lxi-tools-1.21/src/main.c000066400000000000000000000066201332035063200152470ustar00rootroot00000000000000/* * Copyright (c) 2016-2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "options.h" #include "error.h" #include "discover.h" #include "scpi.h" #include "screenshot.h" #include "benchmark.h" #include "run.h" #include int main(int argc, char* argv[]) { int status = EXIT_SUCCESS; double result; // Parse options parse_options(argc, argv); // Initialize LXI library lxi_init(); switch (option.command) { case DISCOVER: if (option.mdns) status = discover(true, option.timeout); else status = discover(false, option.timeout); break; case SCPI: if (option.interactive) status = enter_interactive_mode(option.ip, option.port, option.timeout, option.protocol); else if (option.run_script) status = run_script(option.ip, option.port, option.timeout, option.protocol, option.script_filename); else status = scpi(option.ip, option.port, option.timeout, option.protocol, option.scpi_command); break; case SCREENSHOT: screenshot_register_plugins(); if (option.list) { screenshot_list_plugins(); return 0; } status = screenshot(option.ip, option.plugin_name, option.screenshot_filename, option.timeout, true, NULL, NULL, NULL, NULL); break; case BENCHMARK: status = benchmark(option.ip, option.port, option.timeout, option.protocol, option.count, true, &result, NULL); break; case RUN: status = run(option.lua_script_filename, option.timeout); break; } return status; } lxi-tools-1.21/src/options.c000066400000000000000000000337721332035063200160260ustar00rootroot00000000000000/* * Copyright (c) 2016-2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "config.h" #include "options.h" #include "error.h" #include // Default timeouts in seconds #define TIMEOUT 3 #define TIMEOUT_SCREENSHOT 15 #define TIMEOUT_DISCOVER 1 #define TIMEOUT_DISCOVER_MDNS 6 #define PORT_VXI11 111 #define PORT_RAW 5025 struct option_t option = { NO_COMMAND, // Default command TIMEOUT, // Default timeout in seconds "", // Default IP address "", // Default SCPI command false, // Default no hexadecimal print false, // Default no interactive mode false, // Default no run script "", // Default script filename "", // Default lua script filename "", // Default screenshot plugin name false, // Default no list "", // Default screenshot filename VXI11, // Default protocol 0, // Default port (set later) false, // Default no mDNS discover 100, // Default number of requests in benchmark }; void print_help(char *argv[]) { printf("Usage: %s [--version] [--help] []\n", argv[0]); printf("\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); printf("Commands:\n"); printf(" discover [] Search for LXI devices\n"); printf(" scpi [] Send SCPI command\n"); printf(" screenshot [] [] Capture screenshot\n"); printf(" benchmark [] Benchmark\n"); printf(" run [] Run Lua script\n"); printf("\n"); printf("Discover options:\n"); printf(" -t, --timeout Timeout (default: Normal: %d, mDNS: %d)\n", TIMEOUT_DISCOVER, TIMEOUT_DISCOVER_MDNS); printf(" -m, --mdns Search via mDNS/DNS-SD\n"); printf("\n"); printf("Scpi options:\n"); printf(" -a, --address Device IP address\n"); printf(" -p, --port Use port (default: VXI11: %d, RAW: %d)\n", PORT_VXI11, PORT_RAW); printf(" -t, --timeout Timeout (default: %d)\n", option.timeout); printf(" -x, --hex Print response in hexadecimal\n"); printf(" -i, --interactive Enter interactive mode\n"); printf(" -s, --script Run script file\n"); printf(" -r, --raw Use raw/TCP\n"); printf("\n"); printf("Screenshot options:\n"); printf(" -a, --address Device IP address\n"); printf(" -t, --timeout Timeout (default: %d)\n", TIMEOUT_SCREENSHOT); printf(" -p, --plugin Use screenshot plugin by name\n"); printf(" -l, --list List available screenshot plugins\n"); printf("\n"); printf("Benchmark options:\n"); printf(" -a, --address Device IP address\n"); printf(" -p, --port Use port (default: VXI11: %d, RAW: %d)\n", PORT_VXI11, PORT_RAW); printf(" -t, --timeout Timeout (default: %d)\n", option.timeout); printf(" -c, --count Number of requests (default: %d)\n", option.count); printf(" -r, --raw Use raw/TCP\n"); printf("\n"); printf("Run options:\n"); printf(" -t, --timeout Timeout (default: %d)\n", option.timeout); printf("\n"); } void print_version(void) { printf("lxi v%s\n", VERSION); } void parse_options(int argc, char *argv[]) { int c; // Print help if no arguments provided if (argc == 1) { print_help(argv); exit(EXIT_SUCCESS); } // Convert default timeout to milliseconds option.timeout = option.timeout * 1000; // getopt_long stores the option index here int option_index = 0; // Skip ahead past command optind = 2; if (strcmp(argv[1], "discover") == 0) { option.command = DISCOVER; static struct option long_options[] = { {"timeout", required_argument, 0, 't'}, {"mdns", no_argument, 0, 'm'}, {0, 0, 0, 0 } }; static bool no_timeout_provided = true; /* Parse discover options */ c = getopt_long(argc, argv, "t:m", long_options, &option_index); while (c != -1) { switch (c) { case 't': option.timeout = atoi(optarg) * 1000; no_timeout_provided = false; break; case 'm': option.mdns = true; break; case '?': exit(EXIT_FAILURE); } c = getopt_long(argc, argv, "t:m", long_options, &option_index); } // Set discover timeout if none provided if (no_timeout_provided) { if (option.mdns) option.timeout = TIMEOUT_DISCOVER_MDNS * 1000; else option.timeout = TIMEOUT_DISCOVER * 1000; } } else if (strcmp(argv[1], "scpi") == 0) { option.command = SCPI; static struct option long_options[] = { {"address", required_argument, 0, 'a'}, {"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'}, {"hex", no_argument, 0, 'x'}, {"interactive", no_argument, 0, 'i'}, {"script", required_argument, 0, 's'}, {"raw", no_argument, 0, 'r'}, {0, 0, 0, 0 } }; do { /* Parse scpi options */ c = getopt_long(argc, argv, "a:p:t:xis:r", long_options, &option_index); switch (c) { case 'a': strncpy(option.ip, optarg, 500); break; case 'p': option.port = atoi(optarg); break; case 't': option.timeout = atoi(optarg) * 1000; break; case 'x': option.hex = true; break; case 'i': option.interactive = true; break; case 's': option.run_script = true; option.script_filename = optarg; break; case 'r': option.protocol = RAW; break; case '?': exit(EXIT_FAILURE); } } while (c != -1); } else if (strcmp(argv[1], "screenshot") == 0) { option.command = SCREENSHOT; // Set default timeout for screenshots option.timeout = TIMEOUT_SCREENSHOT * 1000; static struct option long_options[] = { {"address", required_argument, 0, 'a'}, {"timeout", required_argument, 0, 't'}, {"plugin", required_argument, 0, 'p'}, {"list", no_argument, 0, 'l'}, {0, 0, 0, 0 } }; do { /* Parse screenshot options */ c = getopt_long(argc, argv, "a:t:p:l", long_options, &option_index); switch (c) { case 'a': strncpy(option.ip, optarg, 500); break; case 't': option.timeout = atoi(optarg) * 1000; break; case 'p': option.plugin_name = optarg; break; case 'l': option.list = true; break; case '?': exit(EXIT_FAILURE); } } while (c != -1); } else if (strcmp(argv[1], "benchmark") == 0) { option.command = BENCHMARK; static struct option long_options[] = { {"address", required_argument, 0, 'a'}, {"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'}, {"count", required_argument, 0, 'c'}, {"raw", no_argument, 0, 'r'}, {0, 0, 0, 0 } }; do { /* Parse benchmark options */ c = getopt_long(argc, argv, "a:p:t:rc:", long_options, &option_index); switch (c) { case 'a': strncpy(option.ip, optarg, 500); break; case 'p': option.port = atoi(optarg); break; case 't': option.timeout = atoi(optarg) * 1000; break; case 'c': option.count = atoi(optarg); break; case 'r': option.protocol = RAW; break; case '?': exit(EXIT_FAILURE); } } while (c != -1); } else if (strcmp(argv[1], "run") == 0) { option.command = RUN; static struct option long_options[] = { {"timeout", required_argument, 0, 't'}, {0, 0, 0, 0 } }; do { /* Parse run options */ c = getopt_long(argc, argv, "t:", long_options, &option_index); switch (c) { case 't': option.timeout = atoi(optarg) * 1000; break; case '?': exit(EXIT_FAILURE); } } while (c != -1); } else { // No command provided so we restore index optind = 1; static struct option long_options[] = { {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0 } }; do { /* Parse options */ c = getopt_long(argc, argv, "vh", long_options, &option_index); switch (c) { case 'v': print_version(); exit(EXIT_SUCCESS); case 'h': print_help(argv); exit(EXIT_SUCCESS); case '?': exit(EXIT_FAILURE); } } while (c != -1); } if ((option.command == NO_COMMAND) && (optind != argc)) { error_printf("Unknown command\n"); exit(EXIT_FAILURE); } if (option.command == SCPI) { if (optind != argc) strncpy(option.scpi_command, argv[optind++], 500); if (strlen(option.ip) == 0) { error_printf("No IP address specified\n"); exit(EXIT_FAILURE); } if ((strlen(option.scpi_command) == 0) && (option.interactive == false)) { error_printf("No SCPI command specified\n"); exit(EXIT_FAILURE); } } if ((option.command == SCREENSHOT) && (optind != argc)) { strncpy(option.screenshot_filename, argv[optind++], 1000); } if ((option.command == RUN) && (optind != argc)) { strncpy(option.lua_script_filename, argv[optind++], 1000); } /* Print any unknown arguments */ if (optind < argc) { error_printf("Unknown arguments: "); while (optind < argc) fprintf(stderr, "%s ", argv[optind++]); fprintf(stderr, "\n"); exit(EXIT_FAILURE); } /* Configure port */ if (option.port == 0) { // See http://www.lxistandard.org/About/LXI-Protocols.aspx if (option.protocol == RAW) option.port = PORT_RAW; // Default TCP/RAW port else option.port = PORT_VXI11; // Default TCP/VXI11 port } } lxi-tools-1.21/src/plugins/000077500000000000000000000000001332035063200156345ustar00rootroot00000000000000lxi-tools-1.21/src/plugins/screenshot_keysight-dmm.c000066400000000000000000000061251332035063200226430ustar00rootroot00000000000000/* * Copyright (c) 2017-2018 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int keysight_dmm_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI commands to grab image command = "HCOP:SDUM:DATA:FORM BMP"; lxi_send(device, command, strlen(command), timeout); command = "HCOP:SDUM:DATA?"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip IEEE 488.2 Data Block header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Strip ending newline length--; // Dump remaining image data to file screenshot_file_dump(image, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin keysight_dmm = { .name = "keysight-dmm", .description = "Keysight Truevolt Digital Multimeter", .regex = "Agilent Keysight Technologies 34...A", .screenshot = keysight_dmm_screenshot }; lxi-tools-1.21/src/plugins/screenshot_keysight-ivx.c000066400000000000000000000061651332035063200227000ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int keysight_ivx_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI commands to grab image command = ":hardcopy:inksaver off"; lxi_send(device, command, strlen(command), timeout); command = ":display:data? BMP, color"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip IEEE 488.2 Data Block header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Strip ending newline length--; // Dump remaining image data to file screenshot_file_dump(image, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin keysight_ivx = { .name = "keysight-ivx", .description = "Keysight InfiniiVision 2000X/3000X series oscilloscope", .regex = "AGILENT KEYSIGHT TECHNOLOGIES [MD]SO-X.[23]...", .screenshot = keysight_ivx_screenshot }; lxi-tools-1.21/src/plugins/screenshot_rigol-1000z.c000066400000000000000000000057241332035063200221310ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int rigol_1000z_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab PNG image command = "display:data? on,0,png"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip TMC block header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Dump remaining PNG image data to file screenshot_file_dump(image, length, "png"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin rigol_1000z = { .name = "rigol-1000z", .description = "Rigol DS/MSO 1000Z series oscilloscope", .regex = "RIGOL TECHNOLOGIES Rigol Technologies DS1...Z MSO1...Z", .screenshot = rigol_1000z_screenshot }; lxi-tools-1.21/src/plugins/screenshot_rigol-2000.c000066400000000000000000000057601332035063200217400ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int rigol_2000_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = ":display:data?"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip TMC block header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Strip ending newline length--; // Dump remaining BMP image data to file screenshot_file_dump(image, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin rigol_2000 = { .name = "rigol-2000", .description = "Rigol DS/MSO 2000 series oscilloscope", .regex = "RIGOL TECHNOLOGIES Rigol Technologies DS2... MSO2...", .screenshot = rigol_2000_screenshot }; lxi-tools-1.21/src/plugins/screenshot_rigol-dg4000.c000066400000000000000000000060611332035063200222500ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int rigol_dg4000_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = "HCOPy:SDUMp:DATA:FORMat BMP"; lxi_send(device, command, strlen(command), timeout); command = ":HCOPy:SDUMp:DATA?"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip TMC block header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Dump remaining BMP image data to file screenshot_file_dump(image, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin rigol_dg4000 = { .name = "rigol-dg4000", .description = "Rigol DG 4000 series function generator", .regex = "RIGOL TECHNOLOGIES Rigol Technologies DG4...", .screenshot = rigol_dg4000_screenshot }; lxi-tools-1.21/src/plugins/screenshot_rigol-dm3068.c000066400000000000000000000056751332035063200223050ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int rigol_dm3068_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = ":DISP:DATA?"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip TMC block header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Dump remaining BMP image data to file screenshot_file_dump(image, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin rigol_dm3068 = { .name = "rigol-dm3068", .description = "Rigol DM 3068 digital multimeter", .regex = "RIGOL TECHNOLOGIES Rigol Technologies DM3068", .screenshot = rigol_dm3068_screenshot }; lxi-tools-1.21/src/plugins/screenshot_rigol-dp800.c000066400000000000000000000056771332035063200222210ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int rigol_dp800_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = ":SYSTem:PRINT? BMP"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip TMC block header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Dump remaining BMP image data to file screenshot_file_dump(image, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin rigol_dp800 = { .name = "rigol-dp800", .description = "Rigol DP 800 series power supply", .regex = "RIGOL TECHNOLOGIES Rigol Technologies DP8..", .screenshot = rigol_dp800_screenshot }; lxi-tools-1.21/src/plugins/screenshot_rigol-dsa.c000066400000000000000000000057021332035063200221220ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int rigol_dsa_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = ":PRIV:SNAP? BMP"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip TMC block header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Dump remaining BMP image data to file screenshot_file_dump(image, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin rigol_dsa = { .name = "rigol-dsa", .description = "Rigol DSA 700/800 series spectrum analyzer", .regex = "RIGOL TECHNOLOGIES Rigol Technologies DSA[78]..", .screenshot = rigol_dsa_screenshot }; lxi-tools-1.21/src/plugins/screenshot_rohde-schwarz-hmo-rtb.c000066400000000000000000000060341332035063200243660ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int rs_hmo_rtb_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command, *image; int device, length, n; char c; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI commands to grab image command = "HCOPy:FORMat BMP"; lxi_send(device, command, strlen(command), timeout); command = "HCOPy:DATA?"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Strip header c = response[1]; n = atoi(&c); image = &response[0]; image += n+2; length -= n+2; // Dump remaining image data to file screenshot_file_dump(image, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin rs_hmo_rtb = { .name = "rs-hmo-rtb", .description = "Rohde & Schwarz HMO 1000/2000/3000 / RTB 2000 series oscilloscope", .regex = "Rohde&Schwarz HAMEG HMO[123]... RTB2...", .screenshot = rs_hmo_rtb_screenshot }; lxi-tools-1.21/src/plugins/screenshot_siglent-sdg.c000066400000000000000000000054751332035063200224700ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int siglent_sdg_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX] = {}; char *command; int device, length; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = "scdp"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Dump received BMP image data to file screenshot_file_dump(response, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin siglent_sdg = { .name = "siglent-sdg", .description = "Siglent SDG 1000X/2000X/6000X series waveform generator", .regex = "SIGLENT TECHNOLOGIES Siglent Technologies SDG[126]...", .screenshot = siglent_sdg_screenshot }; lxi-tools-1.21/src/plugins/screenshot_siglent-sdm3000.c000066400000000000000000000055021332035063200227700ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int siglent_sdm3000_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX] = {}; char *command; int device, length; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = "scdp"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Dump received BMP image data to file screenshot_file_dump(response, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin siglent_sdm3000 = { .name = "siglent-sdm3000", .description = "Siglent SDM 3000/3000X series digital multimeter", .regex = "SIGLENT TECHNOLOGIES Siglent Technologies SDM3...", .screenshot = siglent_sdm3000_screenshot }; lxi-tools-1.21/src/plugins/screenshot_siglent-sds.c000066400000000000000000000054601332035063200224760ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int siglent_sds_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX] = {}; char *command; int device, length; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = "scdp"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Dump received BMP image data to file screenshot_file_dump(response, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin siglent_sds = { .name = "siglent-sds", .description = "Siglent SDS 1000X/2000X series oscilloscope", .regex = "SIGLENT TECHNOLOGIES Siglent Technologies SDS[12]...", .screenshot = siglent_sds_screenshot }; lxi-tools-1.21/src/plugins/screenshot_siglent-ssa3000x.c000066400000000000000000000055011332035063200231620ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int siglent_ssa3000x_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX] = {}; char *command; int device, length; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI command to grab BMP image command = "scdp"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Dump received BMP image data to file screenshot_file_dump(response, length, "bmp"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin siglent_ssa3000x = { .name = "siglent-ssa3000x", .description = "Siglent SSA 3000X series spectrum analyzer", .regex = "SIGLENT TECHNOLOGIES Siglent Technologies SSA3...X", .screenshot = siglent_ssa3000x_screenshot }; lxi-tools-1.21/src/plugins/screenshot_tektronix.c000066400000000000000000000057471332035063200223010ustar00rootroot00000000000000/* * Copyright (c) 2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "error.h" #include "screenshot.h" #define IMAGE_SIZE_MAX 0x400000 // 4 MB int tektronix_screenshot(char *address, int timeout) { char response[IMAGE_SIZE_MAX]; char *command; int device, length; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); return 1; } // Send SCPI commands to grab PNG image command = "save:image:fileformat PNG"; lxi_send(device, command, strlen(command), timeout); command = "hardcopy:inksaver off"; lxi_send(device, command, strlen(command), timeout); command = "hardcopy start"; lxi_send(device, command, strlen(command), timeout); length = lxi_receive(device, response, IMAGE_SIZE_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); return 1; } // Dump PNG image data to file screenshot_file_dump(response, length, "png"); // Disconnect lxi_disconnect(device); return 0; } // Screenshot plugin configuration struct screenshot_plugin tektronix_2000 = { .name = "tektronix-2000", .description = "Tektronix DPO/MSO 2000 series oscilloscope (experimental)", .regex = "TEKTRONIX DPO2... MSO2...", .screenshot = tektronix_screenshot }; lxi-tools-1.21/src/run.c000066400000000000000000000043241332035063200151260ustar00rootroot00000000000000/* * Copyright (c) 2016-2018 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "options.h" #include "error.h" #include "lxilua.h" #include #include #include #include int run(char *filename, int timeout) { lua_State *L; if (strlen(filename) == 0) { error_printf("Missing filename\n"); return 1; } L = luaL_newstate(); luaL_openlibs(L); // Add lxi functions luaopen_lxilua(L); if (luaL_dofile(L, filename)) { error_printf("%s\n", lua_tostring(L, -1)); lua_close(L); return 0; } lua_close(L); return 0; } lxi-tools-1.21/src/scpi.c000066400000000000000000000173641332035063200152700ustar00rootroot00000000000000/* * Copyright (c) 2016-2017 Martin Lund * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "options.h" #include "error.h" #include #define RESPONSE_LENGTH_MAX 0x500000 #define ID_LENGTH_MAX 65536 static void hex_print(void *data, int length) { char *bufferp; int i; bufferp = data; (void)bufferp; for (i=0; i= 0) { if ( isspace(line[i]) ) line[i] = '\0'; else break; i--; } } int question(const char *string) { int i; for (i = 0; string[i] != '\0'; i++) { if (string[i] == '?') return true; } return false; } int scpi(char *ip, int port, int timeout, lxi_protocol_t protocol, char *command) { char response[RESPONSE_LENGTH_MAX] = ""; char command_buffer[1000]; int device, length;; strip_trailing_space(command); if (protocol == RAW) { // Add newline to command string strcpy(command_buffer, command); command_buffer[strlen(command)] = '\n'; command_buffer[strlen(command)+1] = 0; command = command_buffer; } // Connect device = lxi_connect(ip, port, NULL, timeout, protocol); if (device != LXI_OK) { error_printf("Unable to connect to LXI device\n"); goto error_connect; } // Send SCPI command length = lxi_send(device, command, strlen(command), timeout); if (length < 0) { error_printf("Failed to send message\n"); goto error_send; } // Only expect response in case we are firing a question command if (question(command)) { length = lxi_receive(device, response, RESPONSE_LENGTH_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); goto error_receive; } // Print response if (option.hex) hex_print(response, length); else { int i; for (i=0; i "); if (input == NULL) break; add_history(input); strip_trailing_space(input); // Skip empty lines if (strlen(input) == 0) continue; // Send entered input as SCPI command length = lxi_send(device, input, strlen(input), timeout); if (length < 0) error_printf("Failed to send message\n"); // Only expect response in case we are firing a question command if (question(input)) { length = lxi_receive(device, response, RESPONSE_LENGTH_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); } else { // Make sure we terminate response string response[length] = 0; // Print response printf("%s", response); } } } printf("\n"); // Disconnect lxi_disconnect(device); return 0; error_connect: return 1; } int run_script(char *ip, int port, int timeout, lxi_protocol_t protocol, char *filename) { FILE *fp; char *line = NULL; size_t len = 0; ssize_t read; char response[RESPONSE_LENGTH_MAX] = ""; int device, length, i; // Open script file fp = fopen(filename, "r"); if (fp == NULL) { error_printf("Unable to open file %s\n", filename); goto error_fopen; } // Connect device = lxi_connect(ip, port, NULL, timeout, VXI11); if (device != LXI_OK) { error_printf("Unable to connect to LXI device\n"); goto error_connect; } printf("Connected to %s\n", ip); printf("Running script %s\n\n", filename); while ((read = getline(&line, &len, fp)) != -1) { printf("%s", line); strip_trailing_space(line); // Skip empty lines if (strlen(line) == 1) continue; // Send read line as SCPI command length = lxi_send(device, line, strlen(line), timeout); if (length < 0) error_printf("Failed to send message\n"); // Only expect response in case we are firing a question command if (line[strlen(line)-1] == '?') { length = lxi_receive(device, response, RESPONSE_LENGTH_MAX, timeout); if (length < 0) { error_printf("Failed to receive message\n"); } else { // Make sure we terminate response string response[length] = 0; // Print response for (i=0; i #include #include #include #include #include #include #include #include #include #include "screenshot.h" #include "error.h" #include #define PLUGIN_LIST_SIZE_MAX 50 #define ID_LENGTH_MAX 65536 extern struct screenshot_plugin keysight_dmm; extern struct screenshot_plugin keysight_ivx; extern struct screenshot_plugin rigol_1000z; extern struct screenshot_plugin rigol_2000; extern struct screenshot_plugin rigol_dg4000; extern struct screenshot_plugin rigol_dm3068; extern struct screenshot_plugin rigol_dp800; extern struct screenshot_plugin rigol_dsa; extern struct screenshot_plugin rs_hmo_rtb; extern struct screenshot_plugin siglent_sdm3000; extern struct screenshot_plugin siglent_sdg; extern struct screenshot_plugin siglent_sds; extern struct screenshot_plugin siglent_ssa3000x; extern struct screenshot_plugin tektronix_2000; static struct screenshot_plugin *plugin_list[PLUGIN_LIST_SIZE_MAX] = { }; static char *screenshot_filename = NULL; static char *screenshot_address = NULL; static bool screenshot_no_gui; static void *screenshot_image_buffer; static int *screenshot_image_size; static char *screenshot_image_format; static char *screenshot_image_filename; static int get_device_id(char *address, char *id, int timeout) { int device, bytes_sent, bytes_received; char *command; // Connect to LXI instrument device = lxi_connect(address, 0, NULL, timeout, VXI11); if (device == LXI_ERROR) { error_printf("Failed to connect\n"); goto error_connect; } // Get instrument ID command = "*IDN?"; bytes_sent = lxi_send(device, command, strlen(command), timeout); if (bytes_sent < 0) goto error_send; bytes_received = lxi_receive(device, id, ID_LENGTH_MAX, timeout); if (bytes_received < 0) { error_printf("Failed to receive message\n"); goto error_receive; } // Disconnect lxi_disconnect(device); // Remove trailing newline if (id[bytes_received-1] == '\n') id[bytes_received-1] = 0; return 0; error_receive: error_send: lxi_disconnect(device); error_connect: return 1; } static bool regex_match(const char *string, const char *pattern) { regex_t regex; int status; if (regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB) != 0) return false; // No match status = regexec(®ex, string, (size_t) 0, NULL, 0); regfree(®ex); if (status != 0) return false; // No match return true; // Match } static char *date_time(void) { static char date_time_string[50]; time_t t = time(NULL); struct tm tm = *localtime(&t); sprintf(date_time_string, "%d-%02d-%02d_%02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); return date_time_string; } void screenshot_file_dump(void *data, int length, char *format) { char automatic_filename[1000]; char *filename; char *image_data = data; int i = 0; FILE *fd; // Resolve screenshot output filename if (strlen(screenshot_filename) == 0) { // Automatically resolve screenshot filename if no filename is provided sprintf(automatic_filename, "screenshot_%s_%s.%s", screenshot_address, date_time(), format); filename = automatic_filename; } else { // Write image data to specified filename filename = screenshot_filename; } if (screenshot_no_gui) { if (strcmp(screenshot_filename, "-") == 0) { // Write image data to stdout in case filename is '-' for (i=0; i= PLUGIN_LIST_SIZE_MAX) { error_printf("Screenshot plugin list full\n"); exit(EXIT_FAILURE); } // Add plugin plugin_list[i] = plugin; } void screenshot_list_plugins(void) { int length, length_max = 0; int i = 0, j = 0; // Find length of longest plugin name while ((i < PLUGIN_LIST_SIZE_MAX) && (plugin_list[i] != NULL)) { length = strlen(plugin_list[i]->name); if (length_max < length) length_max = length; i++; } // Pretty print list of available plugins i=0; for (j=0; j<(length_max-4); j++) putchar(' '); printf("Name Description\n"); while ((i < PLUGIN_LIST_SIZE_MAX) && (plugin_list[i] != NULL)) { for (j=0; j<(length_max-strlen(plugin_list[i]->name)); j++) putchar(' '); printf("%s", plugin_list[i]->name); printf(" %s\n", plugin_list[i]->description); i++; } } void screenshot_register_plugins(void) { // Register screenshot plugins screenshot_plugin_register(&keysight_dmm); screenshot_plugin_register(&keysight_ivx); screenshot_plugin_register(&rigol_1000z); screenshot_plugin_register(&rigol_2000); screenshot_plugin_register(&rigol_dg4000); screenshot_plugin_register(&rigol_dm3068); screenshot_plugin_register(&rigol_dp800); screenshot_plugin_register(&rigol_dsa); screenshot_plugin_register(&rs_hmo_rtb); screenshot_plugin_register(&siglent_sdm3000); screenshot_plugin_register(&siglent_sdg); screenshot_plugin_register(&siglent_sds); screenshot_plugin_register(&siglent_ssa3000x); screenshot_plugin_register(&tektronix_2000); } int screenshot(char *address, char *plugin_name, char *filename, int timeout, bool no_gui, void *image_buffer, int *image_size, char *image_format, char *image_filename) { bool no_match = true; char id[ID_LENGTH_MAX]; bool token_found = true; char *token = NULL; int plugin_winner = -1; int match_count = 0; int match_count_max = 0; char *regex_buffer; int i = 0; // Check parameters if (strlen(address) == 0) { error_printf("Missing address\n"); exit(EXIT_FAILURE); } // Save variables screenshot_address = address; screenshot_filename = filename; screenshot_no_gui = no_gui; screenshot_image_buffer = image_buffer; screenshot_image_size = image_size; screenshot_image_format = image_format; screenshot_image_filename = image_filename; if (strlen(plugin_name) == 0) { // Get instrument ID if (get_device_id(address, id, timeout) != 0) { error_printf("Unable to retrieve instrument ID\n"); exit(EXIT_FAILURE); } // Find relevant screenshot plugin (match instrument ID to plugin) while ((i < PLUGIN_LIST_SIZE_MAX) && (plugin_list[i] != NULL)) { // Skip plugin if it has no .regex entry if (plugin_list[i]->regex == NULL) { i++; continue; } // Walk through space separated regular expressions in regex string regex_buffer = strdup(plugin_list[i]->regex); while (token_found == true) { if (token == NULL) token = strtok(regex_buffer, " "); else token = strtok(NULL, " "); if (token != NULL) { // Match regular expression against ID if (regex_match(id, token)) match_count++; // Successful match } else token_found = false; } free(regex_buffer); // Plugin with most matches wins if (match_count > match_count_max) { plugin_winner = i; match_count_max = match_count; } // Reset match_count = 0; token_found = true; i++; } if (plugin_winner == -1) { error_printf("Could not autodetect which screenshot plugin to use - please specify plugin name manually\n"); exit(EXIT_FAILURE); } if (isatty(fileno(stdout)) && screenshot_no_gui) printf("Loaded %s screenshot plugin\n", plugin_list[plugin_winner]->name); no_match = false; i = plugin_winner; } else { // Find relevant screenshot plugin (match specified plugin name to plugin) while ((i < PLUGIN_LIST_SIZE_MAX) && (plugin_list[i] != NULL)) { if (strcmp(plugin_list[i]->name, plugin_name) == 0) { no_match = false; break; } i++; } } if (no_match) { error_printf("Unknown plugin name\n"); exit(EXIT_FAILURE); } // Call capture screenshot function return plugin_list[i]->screenshot(address, timeout); } lxi-tools-1.21/test/000077500000000000000000000000001332035063200143435ustar00rootroot00000000000000lxi-tools-1.21/test/basic-tests.lua000066400000000000000000000033371332035063200172750ustar00rootroot00000000000000-- Basic Lua tests -- -- To run tests simply do: -- $ lxi run basic-tests.lua --[[ Why Lua? Lua provides a set of unique features that makes it distinct from other languages. These include: * Extensible * Simple * Efficient * Portable * Free and open Official Lua documentation: http://www.lua.org/docs.html Lua quick guide: https://www.tutorialspoint.com/lua/lua_quick_guide.htm Lua tutorial: https://www.tutorialspoint.com/lua/index.htm --]] -- Print -- a = 42 print("Hello lxi-tools") print("Value of a is " .. a) print("Running " .. _VERSION) print("Is " .. a .. " the answer?") -- Conditional -- a = true if (a) then print("a is true") else print("a is false") end -- Loop -- for i=10, 1, -1 -- for init, max/min value, increment do print("loop count " .. i) end -- Conditional loops i = 0 j = 5 while (i < j) do print("loop count " .. i) i = i + 1 end repeat print("loop count " .. i) i = i - 1 until (i == 0) -- Function -- function add(a, b) return (a + b) end print("10 + 10 = " .. add(10,10)) -- Array -- array = {"duck", 42, "dog"} print(array[1]) print(array[2]) print(array[3]) -- File I/O -- file = io.open("test.txt", "a") io.output(file) io.write("Hello lxi-tools\n") io.close(file) -- String to number conversion a_string = "+5.111E+02" a_number = tonumber(a_string) print("a_number = " .. a_number) print(type(a_string)) print(type(a_number)) -- Math print("2*2 = " .. 2 * 2) print("2^4 = " .. math.pow(2,4)) print("sin(2) = " .. math.sin(2)) print("sqrt(16) = " .. math.sqrt(16)) print("pi = " .. math.pi) print("a_number * 2 = " .. a_number * 2) -- OS functions os.execute ("echo 'Hello lxi-tools'") print(os.date("The time is %X")) print("TERM = " .. os.getenv("TERM")) lxi-tools-1.21/test/test.lua000066400000000000000000000015431332035063200160300ustar00rootroot00000000000000-- Connect to instruments dso = connect("192.168.1.112") psu = connect("192.168.1.144") -- Print instrument IDs dso_id = scpi(dso, "*IDN?") psu_id = scpi(psu, "*IDN?") print("Digital Storage Oscilloscope ID = " .. dso_id) print("Power Supply ID = " .. psu_id) -- Turn on power supply scpi(psu, "output on"); -- Wait for voltage to stabilize msleep(1000) -- Read out power supply voltage volt = scpi(psu, "voltage?") volt = tonumber(volt) print("volt = " .. volt) -- Read out DSO voltage RMS volt_rms = scpi(dso, ":MEASure:STATistic:ITEM? current,vrms") volt_rms = tonumber(volt_rms) print("volt_rms = " .. volt_rms) -- Do voltage comparison if (volt < volt_rms) then print("Power supply voltage is lower") else print("Power supply voltage is higher") end -- Turn off power supply scpi(psu, "output off"); -- Disconnect disconnect(psu) disconnect(dso) lxi-tools-1.21/test/test.scpi000066400000000000000000000003411332035063200162000ustar00rootroot00000000000000*IDN? :autoscale :stop :run :clear :tforce :single :CHANnel1:DISPlay on :CHANnel2:DISPlay on :CHANnel3:DISPlay on :CHANnel4:DISPlay on :CHANnel1:DISPlay off :CHANnel2:DISPlay off :CHANnel3:DISPlay off :CHANnel4:DISPlay off lxi-tools-1.21/test/test.sh000077500000000000000000000005761332035063200156710ustar00rootroot00000000000000IP=$1 SCPI="lxi scpi --address $IP" $SCPI "*IDN?" $SCPI ":autoscale" $SCPI ":stop" $SCPI ":run" $SCPI ":clear" $SCPI ":tforce" $SCPI ":single" $SCPI ":CHANnel1:DISPlay on" $SCPI ":CHANnel2:DISPlay on" $SCPI ":CHANnel3:DISPlay on" $SCPI ":CHANnel4:DISPlay on" $SCPI ":CHANnel1:DISPlay off" $SCPI ":CHANnel2:DISPlay off" $SCPI ":CHANnel3:DISPlay off" $SCPI ":CHANnel4:DISPlay off"