├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── graph.pde ├── scale.pde ├── screenshots ├── Averaging01.png ├── ChangingLowLimit.png ├── DefiningAreaWithCursors01.png ├── FilledGraph.png ├── MaxHoldScanVHF.png ├── MinMaxMedian.png ├── ZoomArea01.png ├── ZoomArea02.png ├── crop-off.png ├── crop-on.png ├── filledGraphNot.png ├── moving01.png ├── moving02.png ├── newUI01.png ├── referenceGraph.png ├── referenceOffset.png ├── screen1.png ├── screen2.png ├── screenShowSampleDots.png ├── screenVerticalCursor.png ├── upDownConverter.png └── zoomBox.png ├── sketch.properties └── spektrum.pde /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | application.*/** 2 | config.csv 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | if: tag IS present 2 | language: shell 3 | os: 4 | - windows 5 | - linux 6 | before_install: 7 | - |- 8 | case $TRAVIS_OS_NAME in 9 | windows) 10 | [[ ! -f C:/tools/msys64/msys2_shell.cmd ]] && rm -rf C:/tools/msys64 11 | choco uninstall -y mingw 12 | choco upgrade --no-progress -y msys2 13 | choco upgrade --no-progress -y jdk8 14 | choco upgrade --no-progress -y maven 15 | choco upgrade --no-progress -y processing 16 | export msys2='cmd //C RefreshEnv.cmd ' 17 | export msys2+='& set MSYS=winsymlinks:nativestrict ' 18 | export msys2+='& C:\\tools\\msys64\\msys2_shell.cmd -defterm -no-start' 19 | export mingw64="$msys2 -mingw64 -full-path -here -c "\"\$@"\" --" 20 | export msys2+=" -msys2 -c "\"\$@"\" --" 21 | $msys2 pacman --sync --noconfirm --needed mingw-w64-x86_64-toolchain 22 | $msys2 pacman --sync --noconfirm --needed mingw-w64-x86_64-libusb 23 | $msys2 pacman --sync --noconfirm --needed mingw-w64-x86_64-cmake 24 | $msys2 pacman --sync --noconfirm --needed wget zip unzip 25 | taskkill //IM gpg-agent.exe //F # https://travis-ci.community/t/4967 26 | export PATH=/C/tools/msys64/mingw64/bin:$PATH 27 | export MAKE=mingw32-make # so that Autotools can find it 28 | ;; 29 | linux) 30 | sudo apt-get update 31 | sudo apt-get -y install libusb-1.0-0-dev maven wget unzip 32 | cd /opt 33 | wget --no-check-certificate https://download.processing.org/processing-3.5.4-linux64.tgz 34 | tar -xf processing-3.5.4-linux64.tgz 35 | cd "$TRAVIS_BUILD_DIR" 36 | ;; 37 | esac 38 | 39 | before_cache: 40 | - |- 41 | case $TRAVIS_OS_NAME in 42 | windows) 43 | # https://unix.stackexchange.com/a/137322/107554 44 | $msys2 pacman --sync --clean --noconfirm 45 | ;; 46 | esac 47 | 48 | cache: 49 | directories: 50 | - $HOME/AppData/Local/Temp/chocolatey 51 | - /C/tools/msys64 52 | 53 | script: 54 | - |- 55 | case $TRAVIS_OS_NAME in 56 | windows) 57 | $mingw64 git clone https://github.com/pavels/rtl-sdr 58 | cd rtl-sdr 59 | $mingw64 cmake . -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" 60 | $mingw64 $MAKE 61 | cd .. 62 | mkdir -p $HOME/Documents/Processing/libraries/rtlspektrum/library 63 | cd "$HOME/Documents/Processing/libraries" 64 | $mingw64 wget https://github.com/sojamo/controlp5/releases/download/v2.2.5/controlP5-2.2.5.zip 65 | $mingw64 unzip -x controlP5-2.2.5.zip 66 | $mingw64 wget https://github.com/Viproz/controlp5/releases/download/v2.2.7b/controlP5.jar 67 | cp -f controlP5.jar controlP5/library 68 | cd "$TRAVIS_BUILD_DIR" 69 | $mingw64 git clone https://github.com/pavels/processing-rtlspektum-lib 70 | cd processing-rtlspektum-lib 71 | $mingw64 mvn package 72 | cp library.properties "$HOME/Documents/Processing/libraries/rtlspektrum/" 73 | cp target/rtlspektrum.jar "$HOME/Documents/Processing/libraries/rtlspektrum/library/" 74 | cp target/lib/bridj-0.7.0.jar "$HOME/Documents/Processing/libraries/rtlspektrum/library/bridj.jar" 75 | cd .. 76 | $mingw64 processing-java --sketch="$TRAVIS_BUILD_DIR" --output="$TRAVIS_BUILD_DIR\\spektrum" --force --export 77 | mkdir -p spektrum/data 78 | cp rtl-sdr/src/librtlpower.dll spektrum/lib/ 79 | cp rtl-sdr/src/librtlsdr.dll spektrum/lib/ 80 | cp C:/tools/msys64/mingw64/bin/libusb-1.0.dll spektrum/lib/ 81 | $mingw64 zip -r spektrum-win64.zip spektrum 82 | ;; 83 | linux) 84 | git clone https://github.com/pavels/rtl-sdr 85 | cd rtl-sdr 86 | cmake . 87 | make 88 | cd .. 89 | mkdir -p $HOME/sketchbook/libraries/rtlspektrum/library 90 | cd "$HOME/sketchbook/libraries" 91 | wget https://github.com/sojamo/controlp5/releases/download/v2.2.5/controlP5-2.2.5.zip 92 | unzip -x controlP5-2.2.5.zip 93 | wget https://github.com/Viproz/controlp5/releases/download/v2.2.7b/controlP5.jar 94 | cp -f controlP5.jar controlP5/library 95 | cd "$TRAVIS_BUILD_DIR" 96 | git clone https://github.com/pavels/processing-rtlspektum-lib 97 | cd processing-rtlspektum-lib 98 | mvn package 99 | cp library.properties "$HOME/sketchbook/libraries/rtlspektrum/" 100 | cp target/rtlspektrum.jar "$HOME/sketchbook/libraries/rtlspektrum/library/" 101 | cp target/lib/bridj-0.7.0.jar "$HOME/sketchbook/libraries/rtlspektrum/library/bridj.jar" 102 | cd .. 103 | /opt/processing-3.5.4/processing-java --sketch="$TRAVIS_BUILD_DIR" --output="$TRAVIS_BUILD_DIR/spektrum" --force --export 104 | mkdir -p spektrum/data 105 | cp -L rtl-sdr/src/librtlpower.so spektrum/lib/ 106 | cp -L rtl-sdr/src/librtlsdr.so spektrum/lib/ 107 | tar -czf spektrum-linux64.tar.gz spektrum 108 | ;; 109 | esac 110 | 111 | deploy: 112 | provider: releases 113 | api_key: 114 | secure: t579Arynmfj9PK1Pjpwwd374TBzZeP7ddETBzKsSw+uMbqOtpFBVF6VBmFZs0jG8XcUHMaqMt27Zs2uXuInU4/4sOqXV94JGIei6OwVBdmD3Qho6ssgYdQQH1HolJv3F5i0TmI0Zk7DIx2JmR9a6cqN3Y4AgeJdnMvN8c8bItwBrp1uLkisTd2PM++ovI0rmkFIKxsvyYguR2wT6zsvOFiokpMGjH9CtnEyp8dcxqJITMKpYOIpk85VUU8l9cBBGYDyasakVuF1VankWHbbqntZ0JUoorPbw3/7+gXot1LGe5+VVlgzKfdSKp6tqOX74g72etWnDzFhQfJdHU4jVPdWMCW+zlsK72tb+dymQ4nJJXHvQS1TUAp9c3PPGpehg7cdWlg7GrP2FJwzhqyLRdiJkXTH4X9I8bTbPzTrQTdz1HAIGRBuKCRDgYrK7V6TIGMh2fxXBTeoqP8wa9EqRfLwP65uh3+FkeoO5pQyKhsyvHiswRKKjaECbSWKVtW4SH7C0qi+HNqYLKz7DPc/aRxhue5flAA9JJAYTuEqk8c759DSnTiVmAABoZLqo0lBARUA0ZeY6MXvLY773h8ZeJuVs4SrBhrqefPczOCw0u5U78kDNPwpBL6txE0nSG4YpJKdJf0QsSH3Pw7anQ50zusheGgSL3RZnTX5iaUxE/5Y= 115 | file: 116 | - spektrum-win64.zip 117 | - spektrum-linux64.tar.gz 118 | skip_cleanup: true 119 | on: 120 | tags: true 121 | 122 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Pavel Šorejs 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of [project] nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spektrum 2 | 3 | [![Travis CI](https://api.travis-ci.com/pavels/spektrum.svg)](https://travis-ci.com/pavels/spektrum/builds) 4 | 5 | Spektrum is a spectrum analyzer software for use with [rtl-sdr](http://sdr.osmocom.org/trac/wiki/rtl-sdr). 6 | 7 | The biggest advantage is that it can do sweeps across a large frequency span. 8 | 9 | User interface part is written in [Processing](https://processing.org/) 10 | 11 | FM frequency band | 433 MHz antenna measurement 12 | :-------------------------:|:-------------------------: 13 | ![ FM frequency band ](https://raw.githubusercontent.com/pavels/spektrum/master/screenshots/screen1.png) | ![ 433MHz antenna measurement ](https://raw.githubusercontent.com/pavels/spektrum/master/screenshots/screen2.png) 14 | 15 | User interface with Tabs: | Area/Line option 16 | :-------------------------: | :-------------------------: 17 | ![ Latest UI ](https://github.com/pavels/spektrum/raw/master/screenshots/newUI01.png) |![Area graph option ](https://github.com/pavels/spektrum/raw/master/screenshots/FilledGraph.png) 18 | 19 | Mouse wheel zoom from middle of graph: | Mouse wheel close to graph edges adjusts limits 20 | :-------------------------: | :-------------------------: 21 | ![ Mouse wheel zoom ](https://github.com/pavels/spektrum/raw/master/screenshots/zoomBox.png) |![Double right-click or ZOOM button ](https://github.com/pavels/spektrum/raw/master/screenshots/ChangingLowLimit.png) 22 | 23 | The zoom area and measurements with cursors: | Zoomed in area 24 | :-------------------------: | :-------------------------: 25 | ![ Dual Cursor set ](https://github.com/pavels/spektrum/raw/master/screenshots/ZoomArea01.png) |![Double right-click or ZOOM button ](https://github.com/pavels/spektrum/raw/master/screenshots/ZoomArea02.png) 26 | 27 | Drag graph with middle mouse button: | Area of interest centered 28 | :-------------------------: | :-------------------------: 29 | ![ Graph is not centered ](https://github.com/pavels/spektrum/raw/master/screenshots/moving01.png) |![After drag ](https://github.com/pavels/spektrum/raw/master/screenshots/moving02.png) 30 | 31 | Reference save/display: | Averaging (video) 32 | :-------------------------: | :-------------------------: 33 | ![ Reference save/display ](https://github.com/pavels/spektrum/raw/master/screenshots/referenceGraph.png) | ![ Averaging ](https://github.com/pavels/spektrum/raw/master/screenshots/Averaging01.png) 34 | 35 | Min Max hold & Median: | VHF band scan with Max hold 36 | :-------------------------: | :-------------------------: 37 | ![ Min Max hold & Median: ](https://github.com/pavels/spektrum/raw/master/screenshots/MinMaxMedian.png) | ![ VHF band scan with Max hold ](https://github.com/pavels/spektrum/raw/master/screenshots/MaxHoldScanVHF.png) 38 | 39 | Basic support for IF. | Average stored as a reference and shifted up. 40 | :-------------------------: | :-------------------------: 41 | ![ Basic support for IF ](https://github.com/pavels/spektrum/raw/master/screenshots/upDownConverter.png) | ![ Average stored as a reference and shifted up. ](https://github.com/pavels/spektrum/raw/master/screenshots/referenceOffset.png) 42 | 43 | RTL Power croping : OFF. | RTL Power croping : ON. 44 | :-------------------------: | :-------------------------: 45 | ![ Croping off ](https://github.com/pavels/spektrum/raw/master/screenshots/crop-off.png) | ![ Croping on ](https://github.com/pavels/spektrum/raw/master/screenshots/crop-on.png) 46 | 47 | Quick Start 48 | ----------- 49 | 50 | Grab the latest [release](https://github.com/pavels/spektrum/releases) for your OS and unpack it somewhere. 51 | 52 | Connect and configure your rtl-sdr stick. 53 | 54 | ### Windows 55 | 56 | get Zadiag tool - [https://zadig.akeo.ie/](https://zadig.akeo.ie/) and install WinUSB driver for your SDR dongle 57 | 58 | ### Linux 59 | 60 | Get libusb-1.0 from your distribution repository 61 | 62 | To prevent loading wrong driver, create `/etc/modprobe.d/rtl-sdr.conf` file with following content 63 | ``` 64 | blacklist dvb_usb_rtl28xxu 65 | ``` 66 | 67 | Create udev rule `/etc/udev/rules.d/20.rtlsdr.rules` to access as non-root user: 68 | ``` 69 | SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", GROUP="adm", MODE="0666" 70 | ``` 71 | 72 | Launch the software. 73 | 74 | Usage 75 | ----- 76 | 77 | Usage is fairly simple 78 | 79 | * Tweak parameters like frequency range, scale, tuner gain or offset tuning to suit your needs. 80 | * Base display is average for each point, turn "min/max" to also see minimums and maximums for every displayed point. 81 | 82 | Relative mode 83 | ----- 84 | 85 | Relative mode allows you to "zero" the measurement and is useful for measurements with noise constant sources 86 | 87 | You can find an example here [http://www.rtl-sdr.com/rtl-sdr-tutorial-measuring-filter-characteristics-and-antenna-vswr-with-an-rtl-sdr-and-noise-source/](http://www.rtl-sdr.com/rtl-sdr-tutorial-measuring-filter-characteristics-and-antenna-vswr-with-an-rtl-sdr-and-noise-source/) 88 | 89 | * Connect your noise source 90 | * Set desired frequency range 91 | * Click "Relative mode" 92 | * Wait a couple of sweeps - it will do a running average of all collected data 93 | * Click "Set relative" to set the captured spectrum as reference. You should now see a fairly straight line around 0dB 94 | * Connect antenna or filter and tweak gain so you see what is desired 95 | 96 | SV Mod (version v0.19a) 97 | --- 98 | 99 | This project incorporates so-called SV MOD developed by SV8ARJ (George), SV1SGK (Nick) and others, which brought the following changes: 100 | 101 | - Added: 2 Cursors for Frequency axis. 102 | - Added: 2 Cursors for Amplitude axis. 103 | - Added: Absolute and differential measurements with cursors. 104 | - Added: Zoom functionality of the cursor's defined area (gain + frequency). 105 | - Added: _Mouse Wheel_ Gain limits adjustment on a graph (Top area for upper, Bottom area for lower limit). 106 | - Added: _Mouse Wheel_ Frequency limits adjustment on a graph (left area for lower frequency, right for upper). 107 | - Added: _Mouse Wheel_ in the center of the graph performs symmetric zoom in/out. 108 | - Added: View/settings store/recall (elementary "back" operation, nice for quick zoomed-in graph inspection). 109 | - Added: _Right click_ positions primary cursors. 110 | - Added: _Right Double Click_ positions primary cursors and moves secondary out of the way. 111 | - Added: _Right Click and Drag_ defines area using primary and secondary cursors, also interactive Delta measurements. 112 | - Added: _Left Double Click_ zooms area defined by cursors (Amplitude + frequency). 113 | - Added: _Left Mouse Click and Drag_ on a cursor moves the cursor. 114 | - Added: _Middle (mouse wheel) Double Click_ resets full scale for Amplitude and Frequency. 115 | - Added: _Middle (mouse wheel) Click and Drag_, moves the graph recalculating limits accordingly. 116 | - Added: Reset buttons to Min/Max range next to Start and Stop frequency text boxes. 117 | - Modified: Cursors on/off now operate on all 4 cursors. 118 | - Added: ZOOM and BACK buttons. 119 | - Added: Display of frequency, Amplitude, and differences for all cursors. 120 | - Modified: Button layout. 121 | - Fixed: Save/Reload settings on exit/start. 122 | - Added: Filled graph option (line or area). 123 | - Added: VSWR calculation display for the antenna tunning guys (delta dB from cursors to VSWR). 124 | - Added: Reference graph save / display. 125 | - Added: Video averaging, useful on fast refresh (zoomed in). 126 | - Added: Minimum, Maximum hold (persistent display). 127 | - Added: Median value display (middle value between Max and Min). 128 | - Added: IF frequency basic support (only Upper band displays left to right in ascending order). 129 | - Added: Average graph can be saved as reference (if active when "save reference" is clicked). 130 | - Added: Vertical offset for reference graph (controlled from knob). 131 | - Added: Quick help reference screen (mouse operation). 132 | - Modified: RF gain is now a rotary knob plus 3 buttons for 1/3, 1/2 and 2/3 presets. 133 | - Modified: Created a tabbed interface to make room for further development. 134 | - Added: 9+1 Presets plus controls to modify and recall. 135 | - Added: Graph smoothing using RTL crop. (rtlspektrum library wrapper recompiled to export the "crop" setting). 136 | - Modified: Behaviour of mouse and delete key in text fields from [here](https://github.com/Viproz/controlp5/releases/tag/v2.2.7) (controIP5 library fix by @Viproz, Thanks !). 137 | 138 | Big thanks to all participating on this massive extension to Spektrum. 139 | 140 | Background 141 | ---- 142 | 143 | Two libraries are needed to run the code 144 | 145 | * rtl-sdr rtlpower - special branch, where rtlpower is separated into library, so we don't need to run the binary rtlpower. 146 | * java bridge - processing library to interface with rtlpower 147 | 148 | The rtl-sdr branch is located here: [https://github.com/pavels/rtl-sdr](https://github.com/pavels/rtl-sdr) 149 | 150 | The processing library is here [https://github.com/pavels/processing-rtlspektum-lib](https://github.com/pavels/processing-rtlspektum-lib) 151 | 152 | Development 153 | ---- 154 | 155 | You need 156 | 157 | * Processing development environment [https://processing.org/](https://processing.org/) 158 | * rtlspektrum processing library [https://github.com/pavels/processing-rtlspektum-lib/releases](https://github.com/pavels/processing-rtlspektum-lib/releases) (unpack latest rtlspektrum.zip into your processing libraries folder) 159 | * controlP5 processing library [https://github.com/sojamo/controlp5](https://github.com/sojamo/controlp5) 160 | 161 | Contributing 162 | ----- 163 | 164 | * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet. 165 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it. 166 | * Fork the project. 167 | * Start a feature/bugfix branch. 168 | * Commit and push until you are happy with your contribution. 169 | 170 | Contributors 171 | ----- 172 | * [Contributors](https://github.com/pavels/spektrum/graphs/contributors) 173 | 174 | Copyright 175 | ----- 176 | 177 | Copyright (c) 2015 Pavel Šorejs & Contributors. See LICENSE for further details. 178 | -------------------------------------------------------------------------------- /graph.pde: -------------------------------------------------------------------------------- 1 | int graphWidth() { 2 | return width - 280; //return width - 255; 3 | } 4 | 5 | int graphX() { 6 | return 230; 7 | } 8 | 9 | int graphHeight() { 10 | return height - 50; 11 | } 12 | 13 | int graphY() { 14 | return 25; 15 | } 16 | 17 | int hzPerPixel() { 18 | return (stopFreq - startFreq)/graphWidth(); 19 | } 20 | 21 | int gainPerPixel() { 22 | return ( (scaleMax - scaleMin) * 1000) /graphHeight(); 23 | 24 | } 25 | 26 | void graphDrawLine(int x1, int y1, int x2, int y2, int lineColor, float alpha) { 27 | // this rtn draws the frequency trace on the screen =========== 28 | stroke(lineColor, alpha); 29 | if (drawSampleToggle) { 30 | ellipse(x2 + graphX(), graphHeight() - y2 + graphY(),1,1); 31 | } 32 | else { 33 | line(x1 + graphX(), graphHeight() - y1 + graphY(), x2 + graphX(), graphHeight() - y2 + graphY()); 34 | } 35 | } 36 | 37 | void graphDrawFill(int x1, int y1, int x2, int y2, int lineColor, float alpha) { 38 | // this rtn draws the frequency trace on the screen =========== 39 | stroke(lineColor, alpha); 40 | quad( x1 + graphX(), graphHeight() - y1 + graphY(), x2 + graphX(), graphHeight() - y2 + graphY() , 41 | x2 + graphX(), graphY() + graphHeight(), x1 + graphX(), graphY() + graphHeight() ); 42 | 43 | } 44 | 45 | 46 | void drawGraphMatt(double minValue, double maxValue, int minFreq, int maxFreq) { 47 | // This rtn draws the grid on the screen ====================== 48 | int pixelSpacing = 50; 49 | 50 | int verticals = (graphWidth() / pixelSpacing / 5) * 5; 51 | int horizontals = (graphHeight() / pixelSpacing / 5) * 5; 52 | 53 | verticals = verticals == 0 ? 1 : verticals; 54 | horizontals = horizontals == 0 ? 1 : horizontals; 55 | 56 | float verticalSpacing = graphWidth() / (float)verticals; 57 | float horizontalSpacing = graphHeight() / (float)horizontals; 58 | 59 | double xStep = (maxFreq - minFreq) / verticals / 10000.0; 60 | double xPos = minFreq / 10000.0; 61 | 62 | 63 | double yStep = (maxValue - minValue) / horizontals; 64 | double yPos = maxValue; 65 | 66 | stroke(#474747); 67 | fill(#A7A7A7); 68 | 69 | for (int i = 0; i<=verticals; i++) { 70 | line(graphX() + i * verticalSpacing, graphY(), graphX() + i * verticalSpacing, graphY() + graphHeight()); 71 | textAlign(CENTER); //TODO optimize for efficiency and speed 72 | text( round((float) ifCorrectedFreq( (int) (xPos * 10000) )/10000.0 ) / 100.0 + "", graphX() + i * verticalSpacing, graphY() + graphHeight() + 20); 73 | xPos += xStep; 74 | } 75 | 76 | for (int i = 0; i<=horizontals; i++) { 77 | line(graphX(), graphY() + i * horizontalSpacing, graphX() + graphWidth(), graphY() + i * horizontalSpacing); 78 | textAlign(RIGHT); 79 | text(round((float)yPos) + "", graphX() - 5, graphY() + i * horizontalSpacing + 4); 80 | yPos -= yStep; 81 | } 82 | 83 | } 84 | 85 | void sweep(int x, int lineColor, float alpha) { 86 | // show sweep 87 | 88 | // plot new marker 89 | stroke(lineColor, alpha); 90 | line(x + graphX(), graphY() ,x + graphX(), graphY()+graphHeight()); 91 | } 92 | 93 | void sweepVertical(int y, int lineColor, float alpha) { 94 | // show sweep 95 | 96 | // plot new marker 97 | stroke(lineColor, alpha); 98 | line(graphX(), y + graphY() , graphX() + graphWidth(), y + graphY()); 99 | } 100 | -------------------------------------------------------------------------------- /scale.pde: -------------------------------------------------------------------------------- 1 | DataPoint[] reduceBuffer(double[] buffer, int length) { 2 | DataPoint[] ret = new DataPoint[length]; 3 | float step = (float)length / (float)buffer.length; 4 | 5 | int oldIndex = 0; 6 | float position = 0; 7 | 8 | double minValue = Double.POSITIVE_INFINITY; 9 | double maxValue = Double.NEGATIVE_INFINITY; 10 | int count = 0; 11 | double sum = 0; 12 | 13 | int sourcePos = 0; 14 | 15 | while (position < length) { 16 | if (oldIndex != (int)position) { 17 | DataPoint dp = new DataPoint(); 18 | dp.x = (int)position - 1; 19 | dp.yMin = minValue; 20 | dp.yMax = maxValue; 21 | dp.yAvg = (double)sum / (double)count; 22 | ret[oldIndex] = dp; 23 | 24 | sum = 0; 25 | count = 0; 26 | minValue = Double.POSITIVE_INFINITY; 27 | maxValue = Double.NEGATIVE_INFINITY; 28 | 29 | oldIndex = (int)position; 30 | continue; 31 | } 32 | 33 | if (sourcePos < buffer.length) { 34 | if (buffer[sourcePos] < minValue && buffer[sourcePos] != Double.NEGATIVE_INFINITY) { 35 | minValue = buffer[sourcePos]; 36 | } 37 | if (buffer[sourcePos] > maxValue) { 38 | maxValue = buffer[sourcePos]; 39 | } 40 | 41 | if (buffer[sourcePos] > Double.NEGATIVE_INFINITY && buffer[sourcePos] != Double.POSITIVE_INFINITY) { 42 | sum += buffer[sourcePos]; 43 | count++; 44 | } 45 | } 46 | position += step; 47 | sourcePos++; 48 | } 49 | 50 | return ret; 51 | } 52 | 53 | DataPoint[] scaleBufferX(double[] buffer) { 54 | double[] xscale = new double[graphWidth()]; 55 | DataPoint[] ret; 56 | 57 | if(buffer == null) return new DataPoint[0]; 58 | 59 | if (graphWidth() < buffer.length) { 60 | ret = reduceBuffer(buffer, graphWidth()); 61 | } else { 62 | ret = new DataPoint[buffer.length]; 63 | float step = graphWidth() / (float)buffer.length; 64 | 65 | for (int i = 0; i < buffer.length; i++) { 66 | DataPoint dp = new DataPoint(); 67 | dp.x = (int)((float)i * step); 68 | dp.yMin = buffer[i]; 69 | dp.yMax = buffer[i]; 70 | dp.yAvg = buffer[i]; 71 | ret[i] = dp; 72 | } 73 | } 74 | 75 | return ret; 76 | } -------------------------------------------------------------------------------- /screenshots/Averaging01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/Averaging01.png -------------------------------------------------------------------------------- /screenshots/ChangingLowLimit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/ChangingLowLimit.png -------------------------------------------------------------------------------- /screenshots/DefiningAreaWithCursors01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/DefiningAreaWithCursors01.png -------------------------------------------------------------------------------- /screenshots/FilledGraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/FilledGraph.png -------------------------------------------------------------------------------- /screenshots/MaxHoldScanVHF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/MaxHoldScanVHF.png -------------------------------------------------------------------------------- /screenshots/MinMaxMedian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/MinMaxMedian.png -------------------------------------------------------------------------------- /screenshots/ZoomArea01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/ZoomArea01.png -------------------------------------------------------------------------------- /screenshots/ZoomArea02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/ZoomArea02.png -------------------------------------------------------------------------------- /screenshots/crop-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/crop-off.png -------------------------------------------------------------------------------- /screenshots/crop-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/crop-on.png -------------------------------------------------------------------------------- /screenshots/filledGraphNot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/filledGraphNot.png -------------------------------------------------------------------------------- /screenshots/moving01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/moving01.png -------------------------------------------------------------------------------- /screenshots/moving02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/moving02.png -------------------------------------------------------------------------------- /screenshots/newUI01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/newUI01.png -------------------------------------------------------------------------------- /screenshots/referenceGraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/referenceGraph.png -------------------------------------------------------------------------------- /screenshots/referenceOffset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/referenceOffset.png -------------------------------------------------------------------------------- /screenshots/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/screen1.png -------------------------------------------------------------------------------- /screenshots/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/screen2.png -------------------------------------------------------------------------------- /screenshots/screenShowSampleDots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/screenShowSampleDots.png -------------------------------------------------------------------------------- /screenshots/screenVerticalCursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/screenVerticalCursor.png -------------------------------------------------------------------------------- /screenshots/upDownConverter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/upDownConverter.png -------------------------------------------------------------------------------- /screenshots/zoomBox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavels/spektrum/5ac7b06566d1dde5f0d217db3de93f9cbbe86389/screenshots/zoomBox.png -------------------------------------------------------------------------------- /sketch.properties: -------------------------------------------------------------------------------- 1 | mode.id=processing.mode.java.JavaMode 2 | mode=Java 3 | -------------------------------------------------------------------------------- /spektrum.pde: -------------------------------------------------------------------------------- 1 | import controlP5.*; 2 | import rtlspektrum.Rtlspektrum; 3 | import java.io.FileWriter; 4 | import java.util.*; 5 | import processing.serial.*; 6 | 7 | Serial myPort; 8 | 9 | Rtlspektrum spektrumReader; 10 | ControlP5 cp5; 11 | DataPoint[] scaledBuffer; 12 | 13 | boolean startingupBypassSaveConfiguration = true; 14 | 15 | int reloadConfigurationAfterStartUp = 0;// This will be set at the end of the startup 16 | int CONFIG_RELOAD_DELAY = 30; // 0 is disabled 17 | 18 | interface CURSORS { 19 | int 20 | CUR_NONE = 0, 21 | CUR_X_LEFT = 1, 22 | CUR_X_RIGHT = 2, 23 | CUR_Y_TOP = 3, 24 | CUR_Y_BOTTOM = 4; 25 | } 26 | 27 | int movingCursor = CURSORS.CUR_NONE; 28 | 29 | String tmpMessage; 30 | String tmpMessage1; 31 | 32 | final int NONE = 0; 33 | // TABS 34 | // 35 | int tabActiveID = 1; 36 | String tabActiveName = "default"; 37 | final int TAB_HEIGHT = 25; 38 | final int TAB_HEIGHT_ACTIVE = 30; 39 | 40 | final int TAB_GENERAL = 1; 41 | final int TAB_MEASURE = 2; 42 | final int TAB_SETTINGS = 3; 43 | final int TAB_SARK100 = 4; 44 | 45 | String tabLabels[] = {"global", "SETUP", "MEASURE", "SETTINGS", "NOT YET", "WHO ARE YOU"}; 46 | 47 | final int ITEM_GAIN = 1; 48 | final int ITEM_FREQUENCY = 2; 49 | final int ITEM_ZOOM = 3; 50 | final int ITEM_RF_GAIN = 4; 51 | 52 | // interface IF_TYPES { 53 | // int 54 | final int IF_TYPE_NONE = 0; 55 | final int IF_TYPE_ABOVE = 1; 56 | final int IF_TYPE_BELOW = 2; 57 | // } 58 | 59 | // Configuration 60 | // 61 | final int nrOfConfigurations = 10; // First element is used for the Autosave functionality. 62 | 63 | final int PRESET_SAVE = 1; 64 | final int PRESET_LOAD = 2; 65 | int configurationOperation = 0 ; 66 | 67 | int CONFIG_SAVE_DELAY = 80; // 0 is disabled 68 | configurationClass[] configSet = new configurationClass[10]; 69 | int configurationActive=0; 70 | String configurationName; 71 | DropdownList configurationDropdown; 72 | int configurationSaveDelay = 0; 73 | 74 | // Maybe not needed -- TBD 75 | // 76 | public class configurationClass { 77 | public int startFreq; 78 | public int stopFreq; 79 | public int binStep; 80 | public int scaleMin; 81 | public int scaleMax; 82 | public int rfGain; 83 | public int fullRangeMin; 84 | public int fullRangeMax; 85 | public int ifOffset; 86 | public int ifType; 87 | public int activeConfig; 88 | public String configName; 89 | 90 | public configurationClass(int i) 91 | { 92 | configName = "Config" + i; 93 | } 94 | } 95 | 96 | 97 | int timeToSet = 0; // GRGNICK add 98 | int itemToSet = 0; // GRGNICK add -- 1 is Gain, 2 is Frequency 99 | int infoText1X = 0; 100 | int infoText1Y = 0; 101 | int infoColor = #00FF3F; 102 | int infoLineX = 0; 103 | int infoLineY = 0; 104 | int infoRectangle[] = {0, 0, 0, 0}; 105 | String infoText = ""; 106 | 107 | int lastWidth =0; 108 | 109 | int zoomBackFreqMin = 0; 110 | int zoomBackFreqMax = 0; 111 | int zoomBackScalMin = 0; 112 | int zoomBackScalMax = 0; 113 | 114 | int fullRangeMin = 24000000; 115 | int fullRangeMax = 1800000000; 116 | int fullScaleMin = -110; 117 | int fullScaleMax = 40; 118 | 119 | int startFreq = 88000000; 120 | int stopFreq = 108000000; 121 | int binStep = 1000; 122 | int binStepProtection = 200; 123 | int vertCursorFreq = 88000000; 124 | int tmpFreq = 0; 125 | int rfGain = 0; 126 | int ifOffset = 0; 127 | int ifType = 0; 128 | int cropPercent = 0; // RTL data chunks percentage to keep. Values 0 to 70 percent 129 | 130 | 131 | int scaleMin = -110; 132 | int scaleMax = 40; 133 | 134 | int uiNextLineIndex = 0; 135 | int[][] uiLines = new int[10][10]; 136 | 137 | final int GRAPH_DRAG_NONE = 0; 138 | final int GRAPH_DRAG_STARTED = 1; 139 | final int GRAPH_DRAG_ENDED = 0; 140 | int mouseDragGraph = GRAPH_DRAG_NONE; 141 | 142 | int dragGraphStartX; 143 | int dragGraphStartY; 144 | 145 | int cursorVerticalLeftX = -1; 146 | int cursorVerticalRightX = -1; 147 | int cursorHorizontalTopY = -1; 148 | int cursorHorizontalBottomY = -1; 149 | 150 | int cursorVerticalLeftX_Color = #3399ff; // Cyan 151 | int cursorHorizontalBottomY_Color = #3399ff; 152 | 153 | int cursorVerticalRightX_Color = #ff80d5; // Magenta 154 | int cursorHorizontalTopY_Color = #ff80d5; 155 | 156 | int cursorDeltaColor = #00E010; 157 | ListBox deviceDropdown; 158 | DropdownList gainDropdown; 159 | DropdownList serialDropdown; 160 | 161 | 162 | String[] devices; 163 | int[] gains; 164 | 165 | int relMode = 0; 166 | 167 | double minFrequency; 168 | double minValue; 169 | double minScaledValue; 170 | 171 | double maxFrequency; 172 | double maxValue; 173 | double maxScaledValue; 174 | 175 | boolean minmaxDisplay = false; 176 | boolean sweepDisplay = false; 177 | 178 | int showInfoScreen = 0; 179 | class DataPoint { 180 | public int x; 181 | public double yMin = 0; 182 | public double yMax = 0; 183 | public double yAvg = 0; 184 | } 185 | 186 | class infoScreen { 187 | public int topY = 0; 188 | public int leftX = 0; 189 | public int width = 0; 190 | public int height = 0; 191 | public String text = ""; 192 | color colorBack; 193 | } 194 | 195 | infoScreen infoHelp; 196 | 197 | 198 | //========= added by Dave N 199 | Table table; 200 | String fileName = "config.csv"; // config file used to save and load program setting like frequency etc. 201 | boolean setupDone = false; 202 | boolean frozen = true; 203 | boolean vertCursor = false; 204 | float minMaxTextX = 10; 205 | float minMaxTextY = 660; 206 | 207 | int deltaLabelsX; 208 | int deltaLabelsY; 209 | int deltaLabelsXWaiting; 210 | int deltaLabelsYWaiting; 211 | 212 | boolean overGraph = false; 213 | boolean mouseDragLock = false; 214 | int startDraggingThr = 5; 215 | int lastMouseX; 216 | color buttonColor = color(70, 70, 70); 217 | color buttonColorText = color(255, 255, 230); 218 | color setButtonColor = color(127, 0, 0); 219 | color clickMeButtonColor = color(20, 200, 20); 220 | color willSaveButtonColor = color(200, 20, 20); 221 | boolean drawSampleToggle=false; 222 | boolean vertCursorToggle=true; 223 | boolean drawFill=false; 224 | 225 | // Reference 226 | // 227 | boolean refShow = false; // If the reference graph is shown on screen 228 | boolean refStoreFlag = false; // Used to flag a save in draw() 229 | DataPoint[] refArray ; // Storage of reference graph 230 | boolean refArrayHasData = false; 231 | int refYoffset=0; 232 | 233 | 234 | // Average 235 | // 236 | DataPoint[] avgArray ; // Storage of reference graph 237 | boolean avgShow = false; 238 | boolean avgArrayHasData = false; 239 | int avgDepth = 10; 240 | int avgNewSampleWeight = 1; 241 | boolean avgSamples = false; 242 | 243 | // Persistent 244 | // 245 | DataPoint[] perArray ; // Storage of Minimum and Maximum persiastant data graph 246 | boolean perShowMax = false; 247 | boolean perShowMin = false; 248 | boolean perShowMed = false; 249 | boolean perArrayHasData = false; 250 | 251 | 252 | int lastScanPosition = 0; 253 | int scanPosition = 0; 254 | int completeCycles = 0; // How many times the scanner has finished the defined range 255 | 256 | color tabColorBachground = color(0, 70, 80); 257 | 258 | 259 | //========================= 260 | 261 | void MsgBox( String Msg, String Title ) { 262 | // Messages 263 | javax.swing.JOptionPane.showMessageDialog ( null, Msg, Title, javax.swing.JOptionPane.ERROR_MESSAGE ); 264 | } 265 | 266 | void setupStartControls() { 267 | int x, y; 268 | int width = 170; 269 | 270 | x = 15; 271 | y = 40; 272 | 273 | // frameRate(30); 274 | 275 | deviceDropdown = cp5.addListBox("deviceDropdown") 276 | .setBarHeight(20) 277 | .setItemHeight(20) 278 | .setPosition(x, y) 279 | .setSize(width, 20 + (devices.length * 30)); 280 | 281 | deviceDropdown.getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText("Select device"); 282 | 283 | for (int i=0; i>|") 365 | ; 366 | 367 | // -------------------------------------------------------------------- 368 | // 369 | y += 40; 370 | 371 | cp5.addTextfield("binStepText") 372 | .setPosition(x, y) 373 | .setSize(60, 20) 374 | .setText(str(binStep)) 375 | .setAutoClear(true) 376 | .getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText("Bin size [Hz]") 377 | ; 378 | 379 | 380 | cp5.addButton("setRangeButton") 381 | .setValue(0) 382 | .setPosition(95, y) 383 | .setSize(width/2, 20) 384 | .setColorBackground(buttonColor) 385 | .setColorLabel(buttonColorText) 386 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.CENTER).setText("Set range") 387 | ; 388 | 389 | y += 10; 390 | 391 | uiLines[uiNextLineIndex++][TAB_GENERAL] = y; 392 | 393 | // -------------------------------------------------------------------- IF offset 394 | // 395 | y+=35; 396 | 397 | cp5.addTextlabel("ifLabel") 398 | .setText("UP/DOWN CONVERTER:") 399 | .setPosition(x-13, y) 400 | .setColorValue(0xffffff00) 401 | .setFont(createFont("ARIAL", 10)) 402 | ; 403 | 404 | y+=30; 405 | 406 | cp5.addTextfield("ifOffset") 407 | .setPosition(x, y) 408 | .setSize(90, 20) 409 | .setText(str(binStep)) 410 | .setAutoClear(true) 411 | .getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText("IF Frequency") 412 | ; 413 | 414 | cp5.get(Textfield.class, "ifOffset").setText(str(ifOffset)); // Spaggeti because mouse events and code modification events have the same result on event code... 415 | 416 | // toggle vertical sursor on or off 417 | cp5.addToggle("ifPlusToggle") 418 | .setPosition(x + 100, y) 419 | .setSize(20, 20) 420 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.TOP_OUTSIDE).setText("Above") 421 | ; 422 | 423 | // toggle for how samples are shown - line / dots 424 | cp5.addToggle("ifMinusToggle") 425 | .setPosition(x + 140, y) 426 | .setSize(20, 20) 427 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.TOP_OUTSIDE).setText("Below") 428 | ; 429 | 430 | uiLines[uiNextLineIndex++][TAB_GENERAL] = y; 431 | 432 | // -------------------------------------------------------------------- 433 | // 434 | y+=35; 435 | 436 | cp5.addTextlabel("optionsLabel") 437 | .setText("VARIOUS OPTIONS:") 438 | .setPosition(x-13, y) 439 | .setColorValue(0xffffff00) 440 | .setFont(createFont("ARIAL", 10)) 441 | ; 442 | 443 | y+=35; 444 | 445 | // toggle vertical sursor on or off 446 | cp5.addToggle("vertCursorToggle") 447 | .setPosition(x, y) 448 | .setSize(20, 20) 449 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.TOP_OUTSIDE).setText("Cursors") 450 | ; 451 | 452 | // toggle for how samples are shown - line / dots 453 | cp5.addToggle("drawSampleToggle") 454 | .setPosition(x + 70, y) 455 | .setSize(20, 20) 456 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.TOP_OUTSIDE).setText("Line/Dots") 457 | ; 458 | 459 | // toggle for how samples are shown - line / dots 460 | cp5.addToggle("drawFill") 461 | .setPosition(x + 140, y) 462 | .setSize(20, 20) 463 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.TOP_OUTSIDE).setText("Filled Graph") 464 | ; 465 | 466 | // -------------------------------------------------------------------- 467 | // 468 | y += 40; 469 | 470 | cp5.addToggle("offsetToggle") 471 | .setPosition(x, y) 472 | .setSize(20, 20) 473 | .setValue(false) 474 | .getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText("Offset tunning") 475 | ; 476 | 477 | cp5.addToggle("minmaxToggle") 478 | .setPosition(x + 70, y) 479 | .setSize(20, 20) 480 | .setValue(false) 481 | .getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText("Min/Max") 482 | ; 483 | 484 | cp5.addToggle("sweepToggle") 485 | .setPosition(x + 140, y) 486 | .setSize(20, 20) 487 | .setValue(false) 488 | .getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText("Sweep") 489 | ; 490 | 491 | y += 40; 492 | 493 | cp5.addTextfield("cropPrcntTxt") 494 | .setPosition(x, y) 495 | .setSize(60, 20) 496 | .setText(str(cropPercent)) 497 | .setAutoClear(false) 498 | .getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText("Crop percent (0-70%)") 499 | ; 500 | 501 | uiLines[uiNextLineIndex++][TAB_GENERAL] = y; 502 | 503 | // ---------------------------- Configurations 504 | // 505 | y+=35; 506 | 507 | cp5.addTextlabel("configLabel") 508 | .setText("CONFIGURATION PRESETS:") 509 | .setPosition(x-13, y) 510 | .setColorValue(0xffffff00) 511 | .setFont(createFont("ARIAL", 10)) 512 | ; 513 | 514 | // Quick n dirty. Load from file and populate list. 515 | // 516 | Table tmpTable = loadTable(fileName, "header"); 517 | 518 | y+=35; 519 | 520 | cp5.addTextfield("presetName") 521 | .setPosition(x, y) 522 | .setSize(100, 20) 523 | .setText(tmpTable.getString(0, "configName")) 524 | .setAutoClear(false) 525 | .getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText("Current preset") 526 | ; 527 | 528 | configurationDropdown = cp5.addDropdownList("configurationList") 529 | .setBarHeight(20) 530 | .setItemHeight(20) 531 | .setPosition(x, y) 532 | .setSize(100, 80) 533 | .hide(); 534 | configurationDropdown.getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setText(configurationName); 535 | 536 | cp5.addButton("selectPreset") 537 | .setPosition(x+105, y) 538 | .setSize(20, 20) 539 | .setColorBackground(buttonColor) 540 | .setColorLabel(buttonColorText) 541 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.CENTER).setText("...") 542 | ; 543 | 544 | cp5.addButton("savePreset") 545 | .setPosition(x+135, y) 546 | .setSize(40, 20) 547 | .setColorBackground(buttonColor) 548 | .setColorLabel(buttonColorText) 549 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.CENTER).setText("Save to") 550 | ; 551 | 552 | // Populate presets dropdwon list 553 | // 554 | for (int i=0; i 0) { 1124 | cp5.get(Toggle.class, "ifMinusToggle").setValue(0); 1125 | ifType = IF_TYPE_ABOVE; 1126 | ifOffset = parseInt(cp5.get(Textfield.class, "ifOffset").getText()); 1127 | } else { 1128 | ifType = IF_TYPE_NONE; 1129 | } 1130 | 1131 | configurationSaveDelay = CONFIG_SAVE_DELAY; 1132 | } 1133 | } 1134 | public void ifMinusToggle(int theValue) { 1135 | if (setupDone) { 1136 | if (theValue > 0) { 1137 | cp5.get(Toggle.class, "ifPlusToggle").setValue(0); 1138 | ifType = IF_TYPE_BELOW; 1139 | ifOffset = parseInt(cp5.get(Textfield.class, "ifOffset").getText()); 1140 | } else { 1141 | ifType = IF_TYPE_NONE; 1142 | } 1143 | } 1144 | 1145 | configurationSaveDelay = CONFIG_SAVE_DELAY; 1146 | } 1147 | 1148 | // Min/Max UI 1149 | // 1150 | public void offsetToggle(int theValue) { 1151 | if (setupDone) { 1152 | if (theValue > 0) { 1153 | spektrumReader.setOffsetTunning(true); 1154 | } else { 1155 | spektrumReader.setOffsetTunning(false); 1156 | } 1157 | } 1158 | } 1159 | 1160 | public void minmaxToggle(int theValue) { 1161 | if (setupDone) { 1162 | if (theValue > 0) { 1163 | minmaxDisplay = true; 1164 | } else { 1165 | minmaxDisplay = false; 1166 | } 1167 | } 1168 | } 1169 | 1170 | public void sweepToggle(int theValue) { 1171 | if (setupDone) { 1172 | if (theValue > 0) { 1173 | sweepDisplay = true; 1174 | } else { 1175 | sweepDisplay = false; 1176 | } 1177 | } 1178 | } 1179 | 1180 | public void perShowMaxToggle(int theValue) { 1181 | if (setupDone) { 1182 | if (theValue > 0) { 1183 | perShowMax = true; 1184 | } else { 1185 | perShowMax = false; 1186 | } 1187 | } 1188 | } 1189 | 1190 | public void perShowMinToggle(int theValue) { 1191 | if (setupDone) { 1192 | if (theValue > 0) { 1193 | perShowMin = true; 1194 | } else { 1195 | perShowMin = false; 1196 | } 1197 | } 1198 | } 1199 | 1200 | public void perShowMedToggle(int theValue) { 1201 | if (setupDone) { 1202 | if (theValue > 0) { 1203 | perShowMed = true; 1204 | } else { 1205 | perShowMed = false; 1206 | } 1207 | } 1208 | } 1209 | 1210 | public int ifCorrectedFreq( int inFreq ) { 1211 | int tmpFreq=inFreq; 1212 | 1213 | if (ifType == IF_TYPE_ABOVE) tmpFreq -= ifOffset; 1214 | else if (ifType == IF_TYPE_BELOW) tmpFreq = ifOffset - tmpFreq; 1215 | 1216 | return tmpFreq; 1217 | } 1218 | 1219 | public void setRangeButton() { 1220 | setRange(); 1221 | } 1222 | 1223 | public void setRange() { 1224 | // Button color indicating change 1225 | cp5.get(Button.class, "setRangeButton").setColorBackground( buttonColor ); 1226 | 1227 | cursorVerticalLeftX = -1; 1228 | cursorVerticalRightX = -1; 1229 | 1230 | try { 1231 | startFreq = parseInt(cp5.get(Textfield.class, "startFreqText").getText()); 1232 | stopFreq = parseInt(cp5.get(Textfield.class, "stopFreqText").getText()); 1233 | binStep = parseInt(cp5.get(Textfield.class, "binStepText").getText()); 1234 | cropPercent = parseInt(cp5.get(Textfield.class, "cropPrcntTxt").getText()); 1235 | } 1236 | catch(Exception e) { 1237 | println("setRange exception."); 1238 | } 1239 | 1240 | if (startFreq == 0 || stopFreq <= startFreq || binStep < 1) return; 1241 | 1242 | configurationSaveDelay = CONFIG_SAVE_DELAY; 1243 | 1244 | double tmpCrop = (double) ( max( min(70, cropPercent ), 0 ) / 100.0); 1245 | relMode = 0; 1246 | spektrumReader.clearFrequencyRange(); 1247 | spektrumReader.setFrequencyRange(startFreq, stopFreq, binStep, tmpCrop); 1248 | spektrumReader.startAutoScan(); 1249 | println("setRange: CROP set to " + tmpCrop); 1250 | } 1251 | 1252 | public void setScale() { 1253 | // Button color indicating change 1254 | cp5.get(Button.class, "setScale").setColorBackground( buttonColor ); 1255 | 1256 | cursorHorizontalTopY = -1; 1257 | cursorHorizontalBottomY = -1; 1258 | 1259 | try { 1260 | scaleMin = parseInt(cp5.get(Textfield.class, "scaleMinText").getText()); 1261 | scaleMax = parseInt(cp5.get(Textfield.class, "scaleMaxText").getText()); 1262 | } 1263 | catch(Exception e) { 1264 | return; 1265 | } 1266 | 1267 | configurationSaveDelay = CONFIG_SAVE_DELAY; 1268 | } 1269 | 1270 | 1271 | public void resetScale() { 1272 | scaleMin = fullScaleMin; 1273 | scaleMax = fullScaleMax; 1274 | cp5.get(Textfield.class, "scaleMinText").setText(str(scaleMin)); 1275 | cp5.get(Textfield.class, "scaleMaxText").setText(str(scaleMax)); 1276 | } 1277 | 1278 | public void autoScale() { 1279 | if (setupDone) { 1280 | if (minmaxDisplay) { 1281 | scaleMin = (int)(minValue - abs((float)minValue*0.1)); 1282 | scaleMax = (int)(maxValue + abs((float)maxValue*0.1)); 1283 | } else { 1284 | scaleMin = (int)(minScaledValue - abs((float)minScaledValue*0.1)); 1285 | scaleMax = (int)(maxScaledValue + abs((float)maxScaledValue*0.1)); 1286 | } 1287 | cp5.get(Textfield.class, "scaleMinText").setText(str(scaleMin)); 1288 | cp5.get(Textfield.class, "scaleMaxText").setText(str(scaleMax)); 1289 | } 1290 | } 1291 | 1292 | void refSave( ) { 1293 | println("Flaging for graph storage"); 1294 | refStoreFlag = true; 1295 | } 1296 | 1297 | void perReset( ) { 1298 | perArrayHasData = false; 1299 | } 1300 | 1301 | // On set scale (V or H) fix the cursors involved so the primaries are always on the lower side (swap them is needed). 1302 | void swapCursors() { 1303 | int tmpInt; 1304 | 1305 | if (cursorVerticalLeftX > cursorVerticalRightX) { 1306 | tmpInt = cursorVerticalLeftX; 1307 | cursorVerticalLeftX = cursorVerticalRightX; 1308 | cursorVerticalRightX = tmpInt; 1309 | } 1310 | 1311 | if (cursorHorizontalTopY > cursorHorizontalBottomY) { 1312 | tmpInt = cursorHorizontalTopY; 1313 | cursorHorizontalTopY = cursorHorizontalBottomY; 1314 | cursorHorizontalBottomY = tmpInt; 1315 | } 1316 | } 1317 | 1318 | void zoomBack() { 1319 | 1320 | swapCursors();//Fix order 1321 | 1322 | cp5.get(Textfield.class, "startFreqText").setText( str(zoomBackFreqMin) ); 1323 | cp5.get(Textfield.class, "stopFreqText").setText( str(zoomBackFreqMax) ); 1324 | cp5.get(Textfield.class, "scaleMinText").setText( str(zoomBackScalMin) ); 1325 | cp5.get(Textfield.class, "scaleMaxText").setText( str(zoomBackScalMax) ); 1326 | 1327 | zoomBackFreqMin = startFreq; 1328 | zoomBackFreqMax = stopFreq; 1329 | zoomBackScalMin = scaleMin; 1330 | zoomBackScalMax = scaleMax; 1331 | 1332 | setScale(); 1333 | setRange(); 1334 | } 1335 | 1336 | void zoomIn() { 1337 | 1338 | swapCursors();//Fix order 1339 | 1340 | zoomBackFreqMin = startFreq; 1341 | zoomBackFreqMax = stopFreq; 1342 | zoomBackScalMin = scaleMin; 1343 | zoomBackScalMax = scaleMax; 1344 | 1345 | cp5.get(Textfield.class, "startFreqText").setText( str(startFreq + hzPerPixel() * (cursorVerticalLeftX - graphX())) ); 1346 | cp5.get(Textfield.class, "stopFreqText").setText( str(startFreq + hzPerPixel() * (cursorVerticalRightX - graphX())) ); 1347 | cp5.get(Textfield.class, "scaleMinText").setText( str(scaleMax - ( ( (cursorHorizontalBottomY - graphY()) * gainPerPixel() ) / 1000 )) ); 1348 | cp5.get(Textfield.class, "scaleMaxText").setText( str(scaleMax - ( ( (cursorHorizontalTopY - graphY()) * gainPerPixel() ) / 1000 )) ); 1349 | 1350 | setScale(); 1351 | setRange(); 1352 | } 1353 | 1354 | public void toggleRelMode(int theValue) { 1355 | if (setupDone) { 1356 | relMode++; 1357 | if (relMode > 2) { 1358 | relMode = 0; 1359 | } 1360 | } 1361 | } 1362 | 1363 | public void deviceDropdown(int theValue) { 1364 | deviceDropdown.hide(); 1365 | spektrumReader = new Rtlspektrum(theValue); 1366 | int status = spektrumReader.openDevice(); 1367 | 1368 | // Initialiaze configuration class array 1369 | // 1370 | for (int i=0; i buffer[i] && buffer[i] != Double.NEGATIVE_INFINITY) { 1458 | minFrequency = startFreq + i * binStep; 1459 | minValue = buffer[i]; 1460 | } 1461 | 1462 | if (maxValue < buffer[i] && buffer[i] != Double.POSITIVE_INFINITY) { 1463 | maxFrequency = startFreq + i * binStep; 1464 | maxValue = buffer[i]; 1465 | } 1466 | } 1467 | 1468 | scaledBuffer = scaleBufferX(buffer); 1469 | 1470 | // Reference graph 1471 | // 1472 | if ( !refArrayHasData && refShow ) { 1473 | refArray = new DataPoint[scaledBuffer.length]; 1474 | refShow = false; 1475 | cp5.get(Toggle.class, "refShow").setValue(0); 1476 | } 1477 | if ( refShow && refArray.length != scaledBuffer.length ) { 1478 | refStoreFlag = true; 1479 | refShow = false; 1480 | } 1481 | if ( refStoreFlag ) { 1482 | 1483 | // println("STORE size: " + refArray.length ); 1484 | 1485 | if (avgShow && avgArrayHasData ) { 1486 | refArray = new DataPoint[avgArray.length]; 1487 | arrayCopy( avgArray, refArray ); 1488 | cp5.get(Toggle.class, "avgShow").setValue(0); 1489 | } else { 1490 | refArray = new DataPoint[scaledBuffer.length]; 1491 | arrayCopy( scaledBuffer, refArray ); 1492 | } 1493 | refArrayHasData = true; 1494 | refStoreFlag = false; 1495 | refShow = true; 1496 | cp5.get(Toggle.class, "refShow").setValue(1); 1497 | cp5.get(Knob.class, "refYoffset").setValue(0); 1498 | } 1499 | 1500 | // Average graph 1501 | // 1502 | if ( !avgArrayHasData && avgShow ) { 1503 | avgArray = new DataPoint[scaledBuffer.length]; 1504 | } 1505 | if ( avgShow && avgArray.length != scaledBuffer.length ) { 1506 | avgArray = new DataPoint[scaledBuffer.length]; 1507 | } 1508 | 1509 | // Persistent data graph 1510 | // 1511 | if ( !perArrayHasData && (perShowMin || perShowMax || perShowMed) ) { 1512 | perArray = new DataPoint[scaledBuffer.length]; 1513 | } 1514 | if ( (perShowMin || perShowMax || perShowMed) && perArray.length != scaledBuffer.length ) { 1515 | perArray = new DataPoint[scaledBuffer.length]; 1516 | } 1517 | 1518 | // Data processing per screen point 1519 | // 1520 | for (int i = 0; i scaledBuffer[i].yAvg) { 1524 | minScaledValue = scaledBuffer[i].yAvg; 1525 | } 1526 | 1527 | if (maxScaledValue < scaledBuffer[i].yAvg) { 1528 | maxScaledValue = scaledBuffer[i].yAvg; 1529 | } 1530 | } 1531 | 1532 | 1533 | drawGraphMatt(scaleMin, scaleMax, startFreq, stopFreq); 1534 | 1535 | double scaleFactor = (double)graphHeight() / (scaleMax - scaleMin); 1536 | DataPoint lastPoint = null; 1537 | DataPoint refLastPoint = null; 1538 | DataPoint avgLastPoint = null; 1539 | DataPoint perLastPoint = null; 1540 | 1541 | DataPoint point = null; 1542 | DataPoint refPoint = null; 1543 | DataPoint avgPoint = null; 1544 | DataPoint perPoint = null; 1545 | 1546 | color tmpColorGraph = color( 200, 200, 40 ); 1547 | color tmpColorAvg = color( 10, 200, 40 ); 1548 | color tmpColorRef = color( 51, 51, 255 ); 1549 | color tmpColorPerMax = color( 180, 180, 180 ); 1550 | color tmpColorPerMin = color( 160, 160, 160 ); 1551 | color tmpColorPerMed = color( 51, 204, 255 ); 1552 | color tmpColorFill = color ( 102, 102, 0 ); 1553 | 1554 | 1555 | int tmpAlpha = 255; 1556 | if (avgShow || perShowMed) tmpAlpha = 70; 1557 | else tmpAlpha = 255; 1558 | 1559 | // Main point per point loop 1560 | // 1561 | for (int i = 0; i < scaledBuffer.length; i++) { 1562 | point = scaledBuffer[i]; 1563 | refPoint = null; 1564 | avgPoint = scaledBuffer[i]; 1565 | perPoint = scaledBuffer[i]; 1566 | 1567 | if (refShow && refArrayHasData ) { 1568 | refPoint = refArray[i]; 1569 | } 1570 | if (avgShow && avgArrayHasData ) { 1571 | avgPoint = avgArray[i]; 1572 | } 1573 | if ((perShowMin || perShowMax || perShowMed ) && perArrayHasData ) { 1574 | perPoint = perArray[i]; 1575 | } 1576 | 1577 | if (point == null ) continue; 1578 | if (avgPoint == null) avgArrayHasData = false; 1579 | if (perPoint == null) perArrayHasData = false; 1580 | 1581 | if (lastPoint != null) { 1582 | 1583 | // MAIN graph 1584 | // 1585 | if ( drawFill ) { 1586 | graphDrawFill(lastPoint.x, (int)((lastPoint.yAvg - scaleMin) * scaleFactor), point.x, (int)((point.yAvg - scaleMin) * scaleFactor), tmpColorFill, 255); // #fcf400 1587 | } 1588 | 1589 | graphDrawLine(lastPoint.x, (int)((lastPoint.yAvg - scaleMin) * scaleFactor), point.x, (int)((point.yAvg - scaleMin) * scaleFactor), tmpColorGraph, tmpAlpha); 1590 | 1591 | if (minmaxDisplay) { 1592 | graphDrawLine(lastPoint.x, (int)((lastPoint.yMin - scaleMin) * scaleFactor), point.x, (int)((point.yMin - scaleMin) * scaleFactor), #C23B22, 255); 1593 | graphDrawLine(lastPoint.x, (int)((lastPoint.yMax - scaleMin) * scaleFactor), point.x, (int)((point.yMax - scaleMin) * scaleFactor), #03C03C, 255); 1594 | } 1595 | 1596 | // Reference graph 1597 | // 1598 | if (refShow) { 1599 | graphDrawLine(refLastPoint.x, ((int)((refLastPoint.yAvg - scaleMin) * scaleFactor) ) - refYoffset, 1600 | refPoint.x, ( (int)((refPoint.yAvg - scaleMin) * scaleFactor)) - refYoffset, tmpColorRef, 255); 1601 | } 1602 | 1603 | // Average graph 1604 | // 1605 | if (avgShow) { 1606 | if ( !avgArrayHasData ) { // Initialize array 1607 | println("STORING Average"); 1608 | avgArray = new DataPoint[scaledBuffer.length]; 1609 | arrayCopy( scaledBuffer, avgArray); 1610 | avgArrayHasData = true; 1611 | } else // Update and show 1612 | { 1613 | if ( !avgSamples ) 1614 | { 1615 | // if (scaledBuffer[i].yAvg > 1000) println(scaledBuffer[i].yAvg); 1616 | if (scaledBuffer[i].yAvg < 1000) 1617 | avgArray[i].yAvg = avgArray[i].yAvg - (avgArray[i].yAvg / avgDepth ) + (scaledBuffer[i].yAvg / (float)avgDepth); 1618 | } else if ( completeCycles > 0) { 1619 | avgArray[i].yAvg = avgArray[i].yAvg - (avgArray[i].yAvg / avgDepth ) + (scaledBuffer[i].yAvg / (float)avgDepth); 1620 | completeCycles = 0; 1621 | // println("UPDATED"); 1622 | } 1623 | 1624 | if (avgLastPoint!= null) { 1625 | graphDrawLine(avgLastPoint.x, (int)((avgLastPoint.yAvg - scaleMin) * scaleFactor), avgPoint.x, (int)((avgPoint.yAvg - scaleMin) * scaleFactor), tmpColorAvg, 255); 1626 | } 1627 | } 1628 | } 1629 | 1630 | // Persistent graph 1631 | // 1632 | if (perShowMin || perShowMax || perShowMed) { 1633 | if ( !perArrayHasData ) { // Initialize array 1634 | println("STORING Persistant"); 1635 | perArray = new DataPoint[scaledBuffer.length]; 1636 | arrayCopy( scaledBuffer, perArray); 1637 | for ( int jj=0; jj< scaledBuffer.length-1; jj++) { 1638 | perArray[jj].yMax = perArray[jj].yAvg ; 1639 | perArray[jj].yMin = perArray[jj].yAvg ; 1640 | } 1641 | 1642 | perArrayHasData = true; 1643 | } else // Update and show 1644 | { 1645 | if ( scaledBuffer[i].yAvg> perArray[i].yMax ) perArray[i].yMax = scaledBuffer[i].yAvg; 1646 | if ( scaledBuffer[i].yAvg< perArray[i].yMin ) perArray[i].yMin = scaledBuffer[i].yAvg; 1647 | perArray[i].yAvg = perArray[i].yMin + ( perArray[i].yMax - perArray[i].yMin ) /2; 1648 | 1649 | if (perLastPoint!= null) { 1650 | if (perShowMax) 1651 | graphDrawLine(perLastPoint.x, (int)((perLastPoint.yMax - scaleMin) * scaleFactor), perPoint.x, (int)((perPoint.yMax - scaleMin) * scaleFactor), tmpColorPerMax, 200); 1652 | if (perShowMin) 1653 | graphDrawLine(perLastPoint.x, (int)((perLastPoint.yMin - scaleMin) * scaleFactor), perPoint.x, (int)((perPoint.yMin - scaleMin) * scaleFactor), tmpColorPerMin, 200); 1654 | if (perShowMed) 1655 | graphDrawLine(perLastPoint.x, (int)((perLastPoint.yAvg - scaleMin) * scaleFactor), perPoint.x, (int)((perPoint.yAvg - scaleMin) * scaleFactor), tmpColorPerMed, 255); 1656 | } 1657 | } 1658 | } 1659 | } 1660 | 1661 | lastPoint = point; 1662 | refLastPoint = refPoint; 1663 | avgLastPoint = avgPoint; 1664 | perLastPoint = perPoint; 1665 | } 1666 | 1667 | fill(#222324); 1668 | stroke(#D5921F); 1669 | 1670 | // Original Min/Max 1671 | // 1672 | textAlign(LEFT); 1673 | fill(#C23B22); 1674 | text("Min: " + String.format("%.2f", minFrequency / 1000) + "kHz " + String.format("%.2f", minValue) + "dB", minMaxTextX +5, minMaxTextY+20); 1675 | fill(#03C03C); 1676 | text("Max: " + String.format("%.2f", maxFrequency / 1000) + "kHz " + String.format("%.2f", maxValue) + "dB", minMaxTextX +5, minMaxTextY+40); 1677 | 1678 | // Cursors and measurements 1679 | // 1680 | if (vertCursorToggle) { 1681 | drawVertCursor(); 1682 | } 1683 | 1684 | // UI seperator lines 1685 | // 1686 | for ( uiNextLineIndex = 0; uiLines[uiNextLineIndex][tabActiveID] != 0; uiNextLineIndex++ ) 1687 | line( 5, uiLines[uiNextLineIndex][tabActiveID] + 30, 195, uiLines[uiNextLineIndex][tabActiveID] + 30); 1688 | 1689 | 1690 | // Frequency scan detection (complete cycles through spectrum range) 1691 | // 1692 | scanPosition = spektrumReader.getScanPos(); 1693 | 1694 | if ( lastScanPosition != scanPosition ) { 1695 | if (scanPosition - lastScanPosition <= 0) completeCycles++; 1696 | lastScanPosition = scanPosition ; 1697 | // println("RECYCLE !!!" + lastScanPosition); 1698 | } 1699 | 1700 | // Sweep indicator position (vertical line) 1701 | // 1702 | if (sweepDisplay) { 1703 | int scanPos = (int)(((float)graphWidth() / (float)buffer.length) * (float)scanPosition); 1704 | sweep(scanPos, #FFFFFF, 64); 1705 | } 1706 | 1707 | if (cursorVerticalLeftX < 0) cursorVerticalLeftX = graphX(); 1708 | if (cursorVerticalRightX < 0) cursorVerticalRightX = graphX() + graphWidth(); 1709 | if (cursorHorizontalTopY < 0) cursorHorizontalTopY = graphY(); 1710 | if (cursorHorizontalBottomY < 0) cursorHorizontalBottomY = graphY() + graphHeight(); 1711 | 1712 | if ( timeToSet > 1 ) { 1713 | timeToSet--; 1714 | 1715 | if ( infoText1X != 0) { // Do we need any infomative text ? 1716 | fill( infoColor ); 1717 | textSize(40); 1718 | text( infoText, infoText1X, infoText1Y ); 1719 | textSize(12); 1720 | stroke(#FFFFFF); 1721 | if (itemToSet == ITEM_FREQUENCY) line(infoLineX, graphY(), infoLineX, graphY() + graphHeight()); 1722 | if (itemToSet == ITEM_GAIN) line(graphX(), infoLineY, graphX() + graphWidth(), infoLineY); 1723 | if (itemToSet == ITEM_ZOOM) { 1724 | noFill(); 1725 | rect( infoRectangle[0], infoRectangle[1], infoRectangle[2], infoRectangle[3] ); 1726 | } 1727 | } 1728 | } else if ( timeToSet == 1 ) { 1729 | timeToSet = 0; 1730 | if (itemToSet == ITEM_FREQUENCY) setRange(); 1731 | if (itemToSet == ITEM_GAIN) setScale(); 1732 | if (itemToSet == ITEM_ZOOM) { 1733 | setScale(); 1734 | setRange(); 1735 | } 1736 | 1737 | infoText1X = 0; 1738 | } 1739 | 1740 | // Delayed saving 1741 | // 1742 | if (configurationSaveDelay > 1) { 1743 | configurationSaveDelay--; 1744 | } else if (configurationSaveDelay == 1) { 1745 | configurationSaveDelay = 0; 1746 | saveConfig(); 1747 | println("TMR: Config saved (after delay)."); 1748 | } 1749 | } 1750 | // 1751 | // end of draw routine ============================================= 1752 | 1753 | 1754 | // Help Button 1755 | // 1756 | void helpShow ( ) { 1757 | Textarea tmpTA=cp5.get(Textarea.class, "textArea01"); 1758 | PFont pfont = createFont("Arial", 15, true); // use true/false for smooth/no-smooth 1759 | ControlFont font = new ControlFont(pfont, 15, 50); 1760 | 1761 | tmpTA.moveTo("global"); 1762 | 1763 | tmpMessage = "SPEKTRUM - Quick reference.\n"; 1764 | tmpMessage+= "\n"; 1765 | tmpMessage+= "Mouse operation :- \n" ; 1766 | tmpMessage+= "\n"; 1767 | tmpMessage+= "Left Mouse Button : \n" ; 1768 | tmpMessage+= "- Click and Drag on Cursor : Move cursor \n" ; 1769 | tmpMessage+= "- Double Click : Zoom in defined area (by cursors) \n" ; 1770 | tmpMessage+= "\n"; 1771 | tmpMessage+= "Right Mouse Button : \n" ; 1772 | tmpMessage+= "- Click : Move primary cursors to mouse pointer \n" ; 1773 | tmpMessage+= "- Double click : Move primary cursors to pointer, store away secondary cursors. \n" ; 1774 | tmpMessage+= "- Click and Drag : Define an area with primary and secondary cursors. Diff. measurements. \n" ; 1775 | tmpMessage+= "\n"; 1776 | tmpMessage+= "Mouse wheel : \n" ; 1777 | tmpMessage+= "- Double click : Reset full ranges (Amplitude and Frequency) \n" ; 1778 | tmpMessage+= "- Click and Drag : Move graph in X/Y preserving X/Y delta ranges (Pan graph) \n" ; 1779 | tmpMessage+= "- Rotate on top/bottom of graph to change corresponding Amplitude limit \n" ; 1780 | tmpMessage+= "- Rotate on left/right of graph to change corresponding frequency limit \n" ; 1781 | tmpMessage+= "- Rotate in middle of graph to change zoom level (X and Y) \n" ; 1782 | tmpMessage+= "\n\n"; 1783 | tmpMessage+= "\n"; 1784 | 1785 | tmpMessage1 = "Tips\n\n"; 1786 | tmpMessage1+= "- On rotary knobs (eg RF gain) left click and drag up/down for fast adjustment. \n"; 1787 | tmpMessage1+= "- An average graph may also be saved as reference if it is active when the 'SAVE REFERENCE'\n"; 1788 | tmpMessage1+= " Button is clicked \n"; 1789 | tmpMessage1+= "- Crop (percent) will make the graph smoother but slower. Enter a value between 0 and 70 and\n"; 1790 | tmpMessage1+= " press [ENTER]\n"; 1791 | tmpMessage1+= "\n\n\n\n\n\n\n\n\n\n\n\n\n"; 1792 | tmpMessage1+= "\n"; 1793 | 1794 | if ( showInfoScreen == 0) { 1795 | tmpTA.setPosition( graphX() + 10, graphY() + 10 ); 1796 | tmpTA.setSize(graphWidth() - 20, graphHeight() - 20); 1797 | tmpTA.setColorBackground( #808080); 1798 | tmpTA.setText(tmpMessage + tmpMessage1); 1799 | tmpTA.setFont(font); 1800 | 1801 | // Close button 1802 | // 1803 | cp5.addButton("closeHelp") 1804 | .setPosition(graphX() + graphWidth() - 60, graphY() + 15) 1805 | .setSize(40, 20) 1806 | .setColorBackground(buttonColor) 1807 | .setColorLabel(buttonColorText) 1808 | .getCaptionLabel().align(ControlP5.CENTER, ControlP5.CENTER).setText("CLOSE") 1809 | ; 1810 | 1811 | showInfoScreen = 1; 1812 | } else { 1813 | showInfoScreen = 0; 1814 | tmpTA.clear(); 1815 | tmpTA.setPosition( 0, -20 ); 1816 | tmpTA.setSize(10, 10); 1817 | cp5.get(Button.class, "closeHelp").remove(); 1818 | } 1819 | } 1820 | 1821 | void closeHelp () { 1822 | helpShow ( ); 1823 | } 1824 | 1825 | // Average waveform check box 1826 | // 1827 | void avgShow( int value) 1828 | { 1829 | if (value == 1) { 1830 | avgShow = true; 1831 | avgArrayHasData = false; 1832 | avgDepth = max( parseInt(cp5.get(Textfield.class, "avgDepthTxt").getText()), 2); 1833 | } else { 1834 | avgShow = false; 1835 | avgArrayHasData = false; 1836 | } 1837 | } 1838 | 1839 | void freezeDisplay() { 1840 | //================ added by DJN 26 Aug 2017 1841 | if (frozen) { 1842 | frozen = false; 1843 | cp5.get(Button.class, "freezeDisplay").getCaptionLabel().setText("Pause"); 1844 | loop(); 1845 | println("Display unfrozen."); 1846 | } else { 1847 | frozen = true; 1848 | cp5.get(Button.class, "freezeDisplay").getCaptionLabel().setText("Run"); 1849 | noLoop(); 1850 | println("Display frozen."); 1851 | } 1852 | } 1853 | 1854 | void exitProgram() { 1855 | println("Exit program rtn."); 1856 | if (setupDone) exit(); 1857 | } 1858 | 1859 | public void resetMin() { 1860 | //Set the start freq at full range 1861 | 1862 | cp5.get(Textfield.class, "startFreqText").setText( str(fullRangeMin) ); 1863 | setRange(); 1864 | } 1865 | 1866 | 1867 | void resetMax() { 1868 | //Set the stop freq full range 1869 | 1870 | cp5.get(Textfield.class, "stopFreqText").setText( str(fullRangeMax) ); 1871 | setRange(); 1872 | } 1873 | 1874 | void loadConfigPostCreation() 1875 | { 1876 | cp5.get(Textfield.class, "startFreqText").setText( str(startFreq) ); 1877 | cp5.get(Textfield.class, "stopFreqText").setText( str(stopFreq) ); 1878 | cp5.get(Textfield.class, "scaleMinText").setText( str(scaleMin) ); 1879 | cp5.get(Textfield.class, "scaleMaxText").setText( str(scaleMax) ); 1880 | 1881 | if (ifType == IF_TYPE_ABOVE) { 1882 | cp5.get(Toggle.class, "ifMinusToggle").setValue(0); 1883 | cp5.get(Toggle.class, "ifPlusToggle").setValue(1); 1884 | } else if (ifType == IF_TYPE_BELOW ) { 1885 | cp5.get(Toggle.class, "ifMinusToggle").setValue(1); 1886 | cp5.get(Toggle.class, "ifPlusToggle").setValue(0); 1887 | } else { 1888 | cp5.get(Toggle.class, "ifMinusToggle").setValue(0); 1889 | cp5.get(Toggle.class, "ifPlusToggle").setValue(0); 1890 | } 1891 | 1892 | cp5.get(Textfield.class, "ifOffset").setText(str(ifOffset)); 1893 | cp5.get(Textfield.class, "cropPrcntTxt").setText(str(cropPercent)); 1894 | 1895 | setScale(); 1896 | setRange(); 1897 | 1898 | configurationSaveDelay = 0; 1899 | } 1900 | 1901 | void loadConfig() { 1902 | table = loadTable(fileName, "header"); 1903 | 1904 | startFreq = table.getInt(configurationActive, "startFreq"); 1905 | stopFreq = table.getInt(configurationActive, "stopFreq"); 1906 | if (startFreq >= stopFreq) stopFreq = startFreq +100000; 1907 | binStep = table.getInt(configurationActive, "binStep"); 1908 | scaleMin = table.getInt(configurationActive, "scaleMin"); 1909 | scaleMax = table.getInt(configurationActive, "scaleMax"); 1910 | fullRangeMin = table.getInt(configurationActive, "minFreq"); 1911 | fullRangeMax = table.getInt(configurationActive, "maxFreq"); 1912 | 1913 | ifOffset = table.getInt(configurationActive, "ifOffset"); 1914 | ifType = table.getInt(configurationActive, "ifType"); 1915 | cropPercent = table.getInt(configurationActive, "cropPrcnt"); 1916 | 1917 | configurationName = table.getString(configurationActive, "configName"); 1918 | 1919 | //Protection 1920 | if (binStep < binStepProtection) binStep = binStepProtection; 1921 | cropPercent = max( min(70, cropPercent ), 0 ); // Just in case.... 1922 | 1923 | // Init zoom back 1924 | zoomBackFreqMin = startFreq; 1925 | zoomBackFreqMax = stopFreq; 1926 | zoomBackScalMin = scaleMin; 1927 | zoomBackScalMax = scaleMax; 1928 | 1929 | println("loadConfig: Config table " + fileName + " loaded."); 1930 | println("startFreq = " + startFreq + " stopFreq = " + stopFreq + " binStep = " + binStep + " scaleMin = " + 1931 | scaleMin + " scaleMax = ", scaleMax + " rfGain = " + rfGain + " fullRangeMin = " + fullRangeMin + " fullRangeMax = " + fullRangeMax + 1932 | " ifOffset = " + ifOffset + " ifType = " + ifType); 1933 | 1934 | try { 1935 | cp5.get(Textfield.class, "ifOffset").setText(str(ifOffset)); // Spaghetti because mouse events and code modification events have the same result on event code... 1936 | } 1937 | catch (Exception e) { 1938 | } 1939 | } 1940 | 1941 | void saveConfig() { 1942 | saveConfigToIndx( 0 ); 1943 | } 1944 | 1945 | void saveConfigToIndx( int configIndx ) { 1946 | //================ Function added by DJN 24 Aug 2017 1947 | // Note: saveTable fails if file is being backed up at time saveTable is run! 1948 | int i; 1949 | if (startingupBypassSaveConfiguration == false) { 1950 | 1951 | println("saveConfig: Active Configuration " + configurationActive + " with name " + configurationName); 1952 | 1953 | table.setInt(0, "activeConfig", configurationActive); 1954 | 1955 | table.setInt(configIndx, "startFreq", startFreq); 1956 | table.setInt(configIndx, "stopFreq", stopFreq); 1957 | table.setInt(configIndx, "binStep", binStep); 1958 | table.setInt(configIndx, "scaleMin", scaleMin); 1959 | table.setInt(configIndx, "scaleMax", scaleMax); 1960 | table.setInt(configIndx, "rfGain", rfGain); 1961 | table.setInt(configIndx, "minFreq", fullRangeMin); 1962 | table.setInt(configIndx, "maxFreq", fullRangeMax); 1963 | table.setInt(configIndx, "ifOffset", ifOffset); 1964 | table.setInt(configIndx, "ifType", ifType); 1965 | table.setInt(configIndx, "cropPrcnt", cropPercent); 1966 | 1967 | saveTable(table, fileName, "csv"); 1968 | 1969 | println("STORE TO " + configIndx + " : startFreq = " + startFreq + " stopFreq = " + stopFreq + " binStep = " + binStep + " scaleMin = " + 1970 | scaleMin + " scaleMax = ", scaleMax + " rfGain = " + rfGain + " fullRangeMin = " + fullRangeMin + " fullRangeMax = " + fullRangeMax + 1971 | " ifOffset = " + ifOffset + " ifType = " + ifType); 1972 | println("Config table " + fileName + " saved."); 1973 | } 1974 | } 1975 | 1976 | void makeConfig() { 1977 | 1978 | FileWriter fw= null; 1979 | File file =null; 1980 | println("File " + fileName); 1981 | 1982 | try { 1983 | file=new File(fileName); 1984 | println( file.getAbsolutePath()); 1985 | if (file.exists()) { 1986 | println("File " + fileName + " exists."); 1987 | } else { 1988 | // Recreate missing config file 1989 | file.createNewFile(); 1990 | fw = new FileWriter(file); 1991 | 1992 | fw.write("startFreq,stopFreq,binStep,scaleMin,scaleMax,rfGain,minFreq,maxFreq,ifOffset,ifType,cropPrcnt,activeConfig,configName\n"); 1993 | 1994 | fw.write("24000000,1800000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,AutoSave\n"); 1995 | fw.write("88000000,108000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,FM Band\n"); 1996 | fw.write("118000000,178000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,VHF Band+\n"); 1997 | fw.write("380000000,450000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,UHF Band+\n"); 1998 | fw.write("120000000,170000000,2000,-110,40,0,24000000,1800000000,120000000,1,0,0,Spyverter\n"); 1999 | fw.write("24000000,1800000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,Config A\n"); 2000 | fw.write("24000000,1800000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,Config B\n"); 2001 | fw.write("24000000,1800000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,Config C\n"); 2002 | fw.write("24000000,1800000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,Config D\n"); 2003 | fw.write("24000000,1800000000,2000,-110,40,0,24000000,1800000000,0,0,0,0,Config E\n"); 2004 | 2005 | fw.flush(); 2006 | fw.close(); 2007 | println(fileName + " created succesfully"); 2008 | } 2009 | } 2010 | catch(IOException e) { 2011 | e.printStackTrace(); 2012 | } 2013 | 2014 | 2015 | println("Reached end of makeconfig"); 2016 | } 2017 | 2018 | //============================================== 2019 | void drawVertCursor() { 2020 | float xBand; 2021 | float xCur; 2022 | float xPlot; 2023 | xBand = (stopFreq - startFreq); 2024 | 2025 | int tmpInt; 2026 | int freqLeft; 2027 | int freqRight; 2028 | freqLeft = startFreq + hzPerPixel() * (cursorVerticalLeftX - graphX()); 2029 | freqRight = startFreq + hzPerPixel() * (cursorVerticalRightX - graphX()); 2030 | float scaleBottom; 2031 | float scaleTop; 2032 | scaleBottom = scaleMax - ( ( (cursorHorizontalBottomY - graphY()) * gainPerPixel() ) / 1000.0 ); 2033 | scaleTop = scaleMax - ( ( (cursorHorizontalTopY - graphY()) * gainPerPixel() ) / 1000.0 ); 2034 | textSize(16); 2035 | 2036 | // LEFT 2037 | stroke(cursorVerticalLeftX_Color); 2038 | fill(cursorVerticalLeftX_Color); 2039 | line(cursorVerticalLeftX, graphY(), cursorVerticalLeftX, graphY()+graphHeight()); 2040 | textAlign(CENTER); 2041 | text(numToStr(ifCorrectedFreq(freqLeft) /1000) + " kHz", cursorVerticalLeftX-10, graphY() - 5); 2042 | 2043 | // RIGHT 2044 | stroke(cursorVerticalRightX_Color); 2045 | fill(cursorVerticalRightX_Color); 2046 | line(cursorVerticalRightX, graphY(), cursorVerticalRightX, graphY()+graphHeight()); 2047 | textAlign(CENTER); 2048 | text(numToStr(ifCorrectedFreq(freqRight)/1000) + " kHz", cursorVerticalRightX-10, graphY() - 5); 2049 | 2050 | // BOTTOM 2051 | stroke(cursorHorizontalBottomY_Color); 2052 | fill(cursorHorizontalBottomY_Color); 2053 | line(graphX(), cursorHorizontalBottomY, graphX()+graphWidth(), cursorHorizontalBottomY); 2054 | textAlign(CENTER); 2055 | text( String.format("%.1f", scaleBottom) + " db", graphX()+graphWidth()+20, cursorHorizontalBottomY+4); 2056 | 2057 | // TOP 2058 | stroke(cursorHorizontalTopY_Color); 2059 | fill(cursorHorizontalTopY_Color); 2060 | line(graphX(), cursorHorizontalTopY, graphX()+graphWidth(), cursorHorizontalTopY); 2061 | textAlign(CENTER); 2062 | text(String.format("%.1f", scaleTop) + " db", graphX()+graphWidth()+20, cursorHorizontalTopY+4); 2063 | 2064 | // DELTA - FREQ / SCALE 2065 | // 2066 | float tmpVSWR = 1; 2067 | float tmpDdb = 0; 2068 | 2069 | tmpDdb = abs(scaleBottom - scaleTop); 2070 | tmpVSWR = (pow(10, (tmpDdb / 20 )) +1 ) / ( pow( 10, (tmpDdb / 20)) - 1 ) ; 2071 | 2072 | int labelXOffset = 0; 2073 | int labelYOffset = 0; 2074 | if ( deltaLabelsX > graphX() -40 ) { 2075 | if ( deltaLabelsX > graphWidth() / 2 ) labelXOffset = -140; 2076 | else labelXOffset = 50; 2077 | if ( deltaLabelsY > graphHeight() / 2 ) labelYOffset = -30; 2078 | else labelYOffset = 60; 2079 | } 2080 | textAlign(LEFT); 2081 | fill(cursorDeltaColor); 2082 | text("Δx : " + numToStr((freqRight - freqLeft)/1000) + " kHz", deltaLabelsX + labelXOffset, deltaLabelsY + labelYOffset ); 2083 | text("Δy : " + String.format("%.1f", scaleBottom - scaleTop) + " db", deltaLabelsX + labelXOffset, deltaLabelsY + 20 + labelYOffset ); 2084 | textSize(12); 2085 | text("VSWR: 1 : " + String.format("%.3f", tmpVSWR), deltaLabelsX + labelXOffset, deltaLabelsY + 38 + labelYOffset ); 2086 | 2087 | textSize(12); 2088 | noFill(); 2089 | stroke(#808080); 2090 | rect( deltaLabelsX - 10 + labelXOffset, deltaLabelsY - 20 + labelYOffset, 170, 65); 2091 | } 2092 | 2093 | // ==================================================================== 2094 | 2095 | String numToStr(int inNum) { 2096 | // Convert number to string with commas 2097 | String outStr = nfc(inNum); 2098 | return outStr; 2099 | } 2100 | 2101 | int getGraphXfromFreq( int frequency ) { 2102 | return max(graphX() -10, min( graphX() + graphWidth() + 10, graphX() + graphWidth() * (frequency/1000 - startFreq/1000) / (stopFreq/1000 - startFreq/1000))); 2103 | } 2104 | 2105 | int getGraphYfromDb( int db ) { 2106 | return min(graphY() + graphHeight() + 10, max( graphY() - 10, graphHeight() +graphY() - graphHeight() * (db - scaleMin) / (scaleMax - scaleMin) )); 2107 | } 2108 | 2109 | //============== Move the red vertical cursor=============================================== 2110 | 2111 | void mousePressed(MouseEvent evnt) { 2112 | int thisMouseX = mouseX; 2113 | int thisMouseY = mouseY; 2114 | 2115 | boolean CLICK_ABOVE; 2116 | boolean CLICK_LEFT; 2117 | boolean DOUBLE_CLICK; 2118 | 2119 | CLICK_ABOVE = false; 2120 | CLICK_LEFT = false; 2121 | DOUBLE_CLICK = false; 2122 | 2123 | // Only alow clicks in the graph 2124 | // 2125 | if ( mouseX < graphX() ) return; 2126 | 2127 | // Help open ? Just close it 2128 | // 2129 | if (showInfoScreen >0) { 2130 | closeHelp(); 2131 | return; 2132 | } 2133 | 2134 | if (evnt.getCount() == 2) { 2135 | DOUBLE_CLICK = true; 2136 | if (mouseButton == RIGHT) { 2137 | cursorVerticalRightX = graphWidth() + graphX(); 2138 | cursorHorizontalTopY = graphY(); 2139 | } // TAG01 RIGHT->LEFT was LEFT 2140 | if (mouseButton == CENTER) { 2141 | resetMin(); 2142 | resetMax(); 2143 | resetScale(); 2144 | }; 2145 | if (mouseButton == LEFT) zoomIn() ; // TAG01 RIGHT->LEFT was RIGHT 2146 | 2147 | 2148 | println("DOUBLE CLICK DETECTED"); 2149 | return; // ATTENTION !!! RETURN !!!! BAD BAD HABIT. TODO properly. -GRG 2150 | } 2151 | 2152 | 2153 | //Protecion 2154 | if (thisMouseX < graphX() || thisMouseX > graphWidth() + graphX() +1) return; 2155 | if (thisMouseY < graphY() || thisMouseY > graphHeight() + graphY() +1) return; 2156 | 2157 | //Calculate center 2158 | if ( (thisMouseX - graphX()) < (graphWidth()/2) ) { 2159 | CLICK_LEFT = true; 2160 | } 2161 | 2162 | if ( (thisMouseY - graphY() < graphHeight()/2) ) { 2163 | CLICK_ABOVE = true; 2164 | } 2165 | 2166 | 2167 | int clickFreq = startFreq + hzPerPixel() * (thisMouseX - graphX()); 2168 | int clickScale; 2169 | clickScale = ( (thisMouseY - graphY()) * gainPerPixel() ) / 1000; 2170 | clickScale = scaleMax - clickScale; 2171 | 2172 | if (mouseButton == RIGHT ) // TAG01 RIGHT<->LEFT was LEFT 2173 | { 2174 | // Test if the mouse over graph 2175 | if (thisMouseX >= graphX() && thisMouseX <= graphWidth() + graphX() +1) { 2176 | mouseDragLock = true; 2177 | 2178 | vertCursorFreq = clickFreq; 2179 | lastMouseX = mouseX; 2180 | println("clickFreq = " + clickFreq); 2181 | } 2182 | 2183 | 2184 | cursorVerticalLeftX = mouseX; 2185 | cursorHorizontalBottomY = mouseY; 2186 | 2187 | println("clickFreq: " + clickFreq + ", clickScale: " + clickScale); 2188 | } else if (mouseButton == CENTER) { 2189 | 2190 | mouseDragGraph = GRAPH_DRAG_STARTED; 2191 | 2192 | dragGraphStartX = mouseX; 2193 | dragGraphStartY = mouseY; 2194 | } else if (mouseButton == LEFT) { // TAG01 RIGHT->LEFT was RIGHT 2195 | int SELECT_THR = 20; 2196 | // Drag cursors 2197 | // 2198 | // TOP 2199 | if ( abs(mouseY-cursorHorizontalTopY) <= SELECT_THR ) { 2200 | println("TOP LINE"); 2201 | println("clickScale: " + clickScale); 2202 | cp5.get(Textfield.class, "scaleMaxText").setText(str(clickScale)); 2203 | sweepVertical( mouseY - graphY(), #fcd420, 255); 2204 | cursorHorizontalTopY = mouseY; 2205 | movingCursor = CURSORS.CUR_Y_TOP; 2206 | 2207 | // Button color indicating change 2208 | cp5.get(Button.class, "setScale").setColorBackground( clickMeButtonColor ); 2209 | } 2210 | // BOTTOM 2211 | else if ( abs(mouseY-cursorHorizontalBottomY) <= SELECT_THR ) { 2212 | println("BOTTOM LINE"); 2213 | println("clickScale: " + clickScale); 2214 | cp5.get(Textfield.class, "scaleMinText").setText(str(clickScale)); 2215 | sweepVertical( mouseY - graphY(), #fcd420, 255); 2216 | cursorHorizontalBottomY = mouseY; 2217 | movingCursor = CURSORS.CUR_Y_BOTTOM; 2218 | 2219 | // Button color indicating change 2220 | cp5.get(Button.class, "setScale").setColorBackground( clickMeButtonColor ); 2221 | } 2222 | // LEFT 2223 | else if ( abs(mouseX-cursorVerticalLeftX) <= SELECT_THR ) { 2224 | println("LEFT LINE"); 2225 | println("clickFreq: " + clickFreq); 2226 | cp5.get(Textfield.class, "startFreqText").setText(str(clickScale)); 2227 | sweep( mouseX - graphX(), #fcd420, 255); 2228 | cursorVerticalLeftX = mouseX; 2229 | movingCursor = CURSORS.CUR_X_LEFT; 2230 | 2231 | // Button color indicating change 2232 | cp5.get(Button.class, "setRangeButton").setColorBackground( clickMeButtonColor ); 2233 | } 2234 | // RIGHT 2235 | else if ( abs(mouseX-cursorVerticalRightX) <= SELECT_THR ) { 2236 | println("RIGHT LINE"); 2237 | println("clickFreq: " + clickFreq); 2238 | cp5.get(Textfield.class, "stopFreqText").setText(str(clickScale)); 2239 | sweep( mouseX - graphX(), #fcd420, 255); 2240 | cursorVerticalRightX = mouseX; 2241 | movingCursor = CURSORS.CUR_X_RIGHT; 2242 | 2243 | // Button color indicating change 2244 | cp5.get(Button.class, "setRangeButton").setColorBackground( clickMeButtonColor ); 2245 | } 2246 | } 2247 | } 2248 | 2249 | void mouseDragged() { 2250 | int thisMouseX = mouseX; 2251 | int thisMouseY = mouseY; 2252 | 2253 | //Protecion 2254 | if (thisMouseX < graphX() || thisMouseX > graphWidth() + graphX() +1) return; 2255 | if (thisMouseY < graphY() || thisMouseY > graphHeight() + graphY() +1) return; 2256 | 2257 | // Dragging Red cursor 2258 | if (mouseDragLock) { 2259 | if ( ( abs(cursorVerticalLeftX - mouseX) > startDraggingThr ) || ( abs(cursorHorizontalBottomY - mouseY) > startDraggingThr ) ) { 2260 | cursorVerticalRightX = mouseX; 2261 | cursorHorizontalTopY = mouseY; 2262 | 2263 | deltaLabelsX = mouseX-30; 2264 | deltaLabelsY = mouseY-29; 2265 | } 2266 | } 2267 | 2268 | if (movingCursor == CURSORS.CUR_X_LEFT) { 2269 | cursorVerticalLeftX = thisMouseX; 2270 | int clickFreq = startFreq + hzPerPixel() * (thisMouseX - graphX()); 2271 | cp5.get(Textfield.class, "startFreqText").setText( str(clickFreq) ); 2272 | } else if (movingCursor == CURSORS.CUR_X_RIGHT) { 2273 | cursorVerticalRightX = thisMouseX; 2274 | int clickFreq = startFreq + hzPerPixel() * (thisMouseX - graphX()); 2275 | cp5.get(Textfield.class, "stopFreqText").setText( str(clickFreq) ); 2276 | } else if (movingCursor == CURSORS.CUR_Y_TOP) { 2277 | cursorHorizontalTopY = thisMouseY; 2278 | int clickScale = scaleMax - ( ( (thisMouseY - graphY()) * gainPerPixel() ) / 1000 ) ; 2279 | cp5.get(Textfield.class, "scaleMaxText").setText(str(clickScale)); 2280 | } else if (movingCursor == CURSORS.CUR_Y_BOTTOM) { 2281 | cursorHorizontalBottomY = thisMouseY; 2282 | int clickScale = scaleMax - ( ( (thisMouseY - graphY()) * gainPerPixel() ) / 1000 ) ; 2283 | cp5.get(Textfield.class, "scaleMinText").setText(str(clickScale)); 2284 | } 2285 | 2286 | if (mouseButton == RIGHT) { // TAG01 RIGHT->LEFT was LEFT 2287 | stroke(#606060); 2288 | line( cursorVerticalLeftX, cursorHorizontalBottomY, mouseX, mouseY) ; 2289 | } else if (mouseButton == CENTER) { 2290 | stroke(#606060); 2291 | line( dragGraphStartX, dragGraphStartY, mouseX, mouseY) ; 2292 | } 2293 | } 2294 | 2295 | void mouseReleased() { 2296 | mouseDragLock = false; 2297 | lastMouseX = 0; 2298 | 2299 | movingCursor = CURSORS.CUR_NONE; 2300 | 2301 | deltaLabelsX = deltaLabelsXWaiting; 2302 | deltaLabelsY = deltaLabelsYWaiting; 2303 | 2304 | // Move graph 2305 | if (mouseDragGraph == GRAPH_DRAG_STARTED) { 2306 | mouseDragGraph = GRAPH_DRAG_NONE; 2307 | 2308 | int deltaF; 2309 | int deltaDB; 2310 | int freqLeft; 2311 | int freqRight; 2312 | freqLeft = startFreq + hzPerPixel() * (dragGraphStartX - graphX()); 2313 | freqRight = startFreq + hzPerPixel() * (mouseX - graphX()); 2314 | int scaleBottom; 2315 | int scaleTop; 2316 | scaleBottom = scaleMax - ( ( (dragGraphStartY - graphY()) * gainPerPixel() ) / 1000 ); 2317 | scaleTop = scaleMax - ( ( (mouseY - graphY()) * gainPerPixel() ) / 1000 ); 2318 | 2319 | deltaF = freqRight - freqLeft ; 2320 | deltaDB = scaleBottom - scaleTop; 2321 | 2322 | // Move graph up/down 2323 | if (deltaDB != 0) { 2324 | scaleMin += deltaDB; 2325 | scaleMax += deltaDB; 2326 | 2327 | // Protections 2328 | if (scaleMin < fullScaleMin) { 2329 | scaleMin = fullScaleMin; 2330 | } 2331 | if (scaleMin > fullScaleMax) { 2332 | scaleMin = fullScaleMin; 2333 | } 2334 | if (scaleMax < fullScaleMin) { 2335 | scaleMax = fullScaleMin; 2336 | } 2337 | if (scaleMax > fullScaleMax) { 2338 | scaleMax = fullScaleMax; 2339 | } 2340 | 2341 | // Set new scales 2342 | cp5.get(Textfield.class, "scaleMinText").setText( str(scaleMin) ); 2343 | cp5.get(Textfield.class, "scaleMaxText").setText( str(scaleMax) ); 2344 | 2345 | setScale(); 2346 | println("deltaDB: " + numToStr(deltaDB) + ", -New Scale: \n" + " LOWER:" + numToStr(scaleMin) + ", UPPER:" + numToStr(scaleMax) ); 2347 | } 2348 | 2349 | // Move graph right/left 2350 | if (abs(deltaF) > 10) { 2351 | startFreq -= deltaF; 2352 | stopFreq -= deltaF; 2353 | 2354 | // Protections 2355 | if (startFreq < fullRangeMin) { 2356 | startFreq = fullRangeMin; 2357 | } 2358 | if (startFreq > fullRangeMax) { 2359 | startFreq = fullRangeMax; 2360 | } 2361 | if (stopFreq < fullRangeMin) { 2362 | stopFreq = fullRangeMin; 2363 | } 2364 | if (stopFreq > fullRangeMax) { 2365 | stopFreq = fullRangeMax; 2366 | } 2367 | 2368 | // Set new scales 2369 | cp5.get(Textfield.class, "startFreqText").setText( str(startFreq) ); 2370 | cp5.get(Textfield.class, "stopFreqText").setText( str(stopFreq) ); 2371 | 2372 | println("deltaF: " + numToStr(deltaF) + ", -New Freq: \n" + " START:" + numToStr(startFreq) + ", STOP:" + numToStr(stopFreq) ); 2373 | 2374 | setRange(); 2375 | } 2376 | } 2377 | } 2378 | 2379 | 2380 | void mouseWheel(MouseEvent event) { 2381 | final int NOTHING = 0; 2382 | final int GAIN_HIGH = 1; 2383 | final int GAIN_LOW = 2; 2384 | final int FREQ_LEFT = 4; 2385 | final int FREQ_RIGHT = 8; 2386 | final int GRAPH_ZOOM = 16; 2387 | final int TIME_UNTIL_SET = 25; 2388 | final int TIME_UNTIL_SET_FAST = 10; 2389 | 2390 | int tmpFreq; 2391 | int tmpFreq2; 2392 | int tmpGain; 2393 | int tmpGain2; 2394 | 2395 | int toModify; 2396 | int gMouseX; 2397 | int gMouseY; 2398 | int freqStep = 0; 2399 | 2400 | int scaleFreqOverDb = 0; 2401 | 2402 | gMouseX = mouseX - graphX(); 2403 | gMouseY = mouseY - graphY(); 2404 | 2405 | toModify = NOTHING; 2406 | 2407 | // Centre of graph horizontal is for incr/decr GAIN top/bottom 2408 | // 2409 | if ( abs( gMouseX - graphWidth()/2 ) < graphWidth()/4 ) { 2410 | // println ("Middle COLUMN"); 2411 | 2412 | // Top or bottom ? 2413 | // 2414 | if ( gMouseY < graphHeight()/4 ) { 2415 | toModify = GAIN_HIGH; 2416 | } else if ( graphHeight() - gMouseY < graphHeight()/4 ) { 2417 | toModify = GAIN_LOW; 2418 | } 2419 | } 2420 | 2421 | // Middle of graph's vertical is for incr/decr frequency max/min 2422 | // 2423 | if ( abs( gMouseY - graphHeight()/2 ) < graphHeight()/4 ) { 2424 | // println ("Middle ROW"); 2425 | 2426 | // Left or Right ? 2427 | // 2428 | if ( gMouseX < graphWidth()/4 && gMouseX > 0 ) { 2429 | toModify = FREQ_LEFT; 2430 | } else if ( graphWidth() - gMouseX < graphWidth()/4 ) { 2431 | toModify = FREQ_RIGHT; 2432 | } 2433 | } 2434 | 2435 | // Middle of graph on X and Y is for zoom 2436 | // 2437 | if ( abs( gMouseX - graphWidth()/2 ) < graphWidth()/4 && abs( gMouseY - graphHeight()/2 ) < graphHeight()/4 ) 2438 | toModify = GRAPH_ZOOM ; 2439 | 2440 | 2441 | tmpFreq = 0; 2442 | if (toModify > 0 ) { 2443 | infoText1X = min( max( graphX() +90, mouseX), graphWidth() + 140 ) ; 2444 | infoText1Y = max( graphY() +40, mouseY ); 2445 | } 2446 | if ( stopFreq - startFreq > 50000000 ) freqStep = 10000000; 2447 | else freqStep = 1000000; 2448 | 2449 | switch ( toModify ) { 2450 | 2451 | // GAIN ==================== 2452 | // 2453 | case GAIN_LOW: 2454 | tmpGain = (( parseInt(cp5.get(Textfield.class, "scaleMinText").getText()) ) - event.getCount()) ; 2455 | if (tmpGain < fullScaleMin ) tmpGain = fullScaleMin; 2456 | if (tmpGain > fullScaleMax ) tmpGain = fullScaleMax-1; 2457 | if (tmpGain >= scaleMax ) tmpGain = scaleMax - 1; 2458 | cp5.get(Textfield.class, "scaleMinText").setText(str(tmpGain)); 2459 | infoText = str(tmpGain) + " db" ; 2460 | itemToSet = ITEM_GAIN; 2461 | infoLineY = getGraphYfromDb( tmpGain ); 2462 | 2463 | timeToSet = TIME_UNTIL_SET; 2464 | break; 2465 | 2466 | case GAIN_HIGH: 2467 | tmpGain = (( parseInt(cp5.get(Textfield.class, "scaleMaxText").getText()) ) - event.getCount()) ; 2468 | if (tmpGain < fullScaleMin ) tmpGain = fullScaleMin + 1; 2469 | if (tmpGain > fullScaleMax ) tmpGain = fullScaleMax; 2470 | if (tmpGain <= scaleMin ) tmpGain = scaleMin + 1; 2471 | cp5.get(Textfield.class, "scaleMaxText").setText(str(tmpGain)); 2472 | itemToSet = ITEM_GAIN; 2473 | infoText = str(tmpGain) + " db" ; 2474 | infoLineY = getGraphYfromDb( tmpGain ); 2475 | 2476 | timeToSet = TIME_UNTIL_SET; 2477 | break; 2478 | 2479 | // FREQUENCY =================== 2480 | // 2481 | case FREQ_LEFT: 2482 | tmpFreq = (( parseInt(cp5.get(Textfield.class, "startFreqText").getText()) /freqStep ) - event.getCount() ) * freqStep ; 2483 | if (tmpFreq < fullRangeMin ) tmpFreq = fullRangeMin; 2484 | if (tmpFreq > fullRangeMax ) tmpFreq = fullRangeMax; 2485 | if (tmpFreq >= stopFreq ) tmpFreq = stopFreq - 1000000; 2486 | cp5.get(Textfield.class, "startFreqText").setText(str(tmpFreq)); 2487 | itemToSet = ITEM_FREQUENCY; 2488 | infoText = str( ifCorrectedFreq(tmpFreq) / 1000000 ) + " MHz" ; 2489 | infoLineX = getGraphXfromFreq( tmpFreq ); 2490 | timeToSet = TIME_UNTIL_SET; 2491 | break; 2492 | 2493 | case FREQ_RIGHT: 2494 | tmpFreq = (( parseInt(cp5.get(Textfield.class, "stopFreqText").getText()) / freqStep) - event.getCount()) * freqStep; 2495 | if (tmpFreq < fullRangeMin ) tmpFreq = fullRangeMin; 2496 | if (tmpFreq > fullRangeMax ) tmpFreq = fullRangeMax; 2497 | if (tmpFreq <= startFreq ) tmpFreq = startFreq + 1000000; 2498 | cp5.get(Textfield.class, "stopFreqText").setText(str(tmpFreq)); 2499 | itemToSet = ITEM_FREQUENCY; 2500 | infoText = str( ifCorrectedFreq( tmpFreq )/ 1000000 ) + " MHz"; 2501 | infoLineX = getGraphXfromFreq( tmpFreq ); 2502 | timeToSet = TIME_UNTIL_SET; 2503 | break; 2504 | 2505 | case GRAPH_ZOOM: 2506 | scaleFreqOverDb = (stopFreq - startFreq) / (scaleMax - scaleMin) ; // How many Hz for each db 2507 | tmpGain = min( max( (( parseInt(cp5.get(Textfield.class, "scaleMinText").getText()) ) - event.getCount()), fullScaleMin), fullScaleMax) ; 2508 | tmpGain2 = max( min( (( parseInt(cp5.get(Textfield.class, "scaleMaxText").getText()) ) + event.getCount()), fullScaleMax), fullScaleMin) ; 2509 | if ( tmpGain2 <= tmpGain ) tmpGain2 = tmpGain + 2; 2510 | if ( tmpGain == fullScaleMax ) { 2511 | tmpGain = fullScaleMax-1; 2512 | tmpGain2=fullScaleMax; 2513 | } 2514 | cp5.get(Textfield.class, "scaleMinText").setText(str(tmpGain)); 2515 | cp5.get(Textfield.class, "scaleMaxText").setText(str(tmpGain2)); 2516 | 2517 | tmpFreq = min( max( parseInt(cp5.get(Textfield.class, "startFreqText").getText()) - scaleFreqOverDb * event.getCount(), fullRangeMin ), fullRangeMax ) ; 2518 | tmpFreq2 = max( min( parseInt(cp5.get(Textfield.class, "stopFreqText").getText()) + scaleFreqOverDb * event.getCount(), fullRangeMax ), fullRangeMin ) ; 2519 | 2520 | if (tmpFreq >= tmpFreq2) tmpFreq2 = tmpFreq + 10000000; 2521 | 2522 | cp5.get(Textfield.class, "startFreqText").setText(str(tmpFreq)); 2523 | cp5.get(Textfield.class, "stopFreqText").setText(str(tmpFreq2)); 2524 | 2525 | if ( event.getCount() >0 ) infoText = "ZOOM OUT"; 2526 | else infoText="ZOOM IN"; 2527 | infoRectangle[0]= getGraphXfromFreq( tmpFreq ); 2528 | infoRectangle[1]= getGraphYfromDb( tmpGain); 2529 | infoRectangle[2]= getGraphXfromFreq( tmpFreq2 ) - infoRectangle[0]; 2530 | infoRectangle[3]= ( getGraphYfromDb( tmpGain2) - infoRectangle[1]); 2531 | 2532 | itemToSet = ITEM_ZOOM; 2533 | timeToSet = TIME_UNTIL_SET; 2534 | break; 2535 | } 2536 | } 2537 | --------------------------------------------------------------------------------