119 Commits

Author SHA1 Message Date
052167962d fix the rtlsdr source on android again 2024-01-17 06:52:08 +01:00
6fc41a81a7 fix build on android due to new librtlsdr function name 2024-01-17 06:41:25 +01:00
e710b6c6dc Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2024-01-17 06:29:41 +01:00
a91434c5fe fix windows CI (hopefully) and rebuild with Android BlogV4 support 2024-01-17 06:28:16 +01:00
ead2ac6128 Merge pull request #1267 from clouserw/readme-fix
add additional library to debian install instructions
2024-01-05 00:20:29 +01:00
505cbb0ba2 add additional library to debian install instructions 2024-01-04 14:01:00 -08:00
b1030cbdfb Implement #1260 2023-12-27 03:53:43 +01:00
f3c5b2c31f Fixed #1259 2023-12-26 23:10:44 +01:00
794d6ff5ac clean up 2023-10-29 02:40:34 +01:00
4803271115 fix #1212 2023-10-29 02:27:19 +01:00
78daed7879 Implemented #1205 2023-10-11 21:50:23 +02:00
5f297b1a69 undid the oopsie 2023-10-10 15:21:55 +02:00
7c5d4226eb removed MacOS CI since it required payment (ew...) 2023-10-09 22:43:39 +02:00
ec086ebbdf attempt at fixing M1 ci 2023-10-09 22:42:07 +02:00
d10d420467 added MacOS M1 CI 2023-10-02 23:57:06 +02:00
5bf989f49d fixed android build 2023-09-29 15:35:48 +02:00
4b3b6976d6 removed rpi install script since it is not to be used 2023-09-29 14:52:30 +02:00
55ddd383d2 Fix #1199 2023-09-29 14:42:45 +02:00
a043ab2dd3 Merge pull request #1185 from daviderud/master
Netherlands bandplan and readme correction
2023-09-14 09:58:58 +02:00
d270e1c5e8 Merge branch 'master' of https://github.com/daviderud/SDRPlusPlus 2023-09-14 05:53:25 +02:00
e41f24a95e first version 2023-09-14 05:50:53 +02:00
766b3db363 added clarification and correction 2023-09-14 05:43:00 +02:00
0632342bb7 fixed MacOS packaging 2023-09-13 05:14:07 +02:00
27b07ed0e8 Merge pull request #1163 from theverygaming/master
fixed buffer overflow in file source
2023-09-13 03:43:45 +02:00
a824c83848 Update main.cpp 2023-09-13 03:16:12 +02:00
0e50ee0e67 Update main.cpp 2023-09-13 02:51:44 +02:00
a55d1d9c06 Merge pull request #1179 from isorochan/master
Fix typo in fastAmplitude()
2023-09-07 20:05:24 +02:00
97187b790f Fix typo in fastAmplitude() 2023-09-07 17:27:34 +03:00
9c1361a8a9 Added radio mode info in the recorder and fixed horrible code in the rigctl_server module 2023-09-06 05:38:00 +02:00
4d0d14856b added support for RTL-SDR v4 on MacOS 2023-09-06 04:12:04 +02:00
365ab2325e MacOS CI fix 2023-09-06 03:39:12 +02:00
4da7e686f3 Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2023-09-04 22:19:59 +02:00
c1d9ab64f8 fixed CI 2023-09-04 22:19:51 +02:00
ea33135bf1 Merge pull request #1174 from Bastiti/master
Add 8m - Amateur to the Belgian Bandplan
2023-09-04 15:20:54 +02:00
2081384905 Update belgium.json
https://www.ibpt.be/consommateurs/publication/decision-du-29-aout-2023-concernant-l-attribution-du-spectre-40.660-mhz---40.690-mhz-a-des-stations-de-radiocommunications-privees-dinstruction-individuelle-dintercommunication-technique-et-detudes-utilisees-par-des-radioamateurs
2023-09-04 14:47:13 +02:00
6b31134af2 do not play file when samplerate == 0 2023-08-27 13:28:21 +02:00
99d2786c25 Merge pull request #1169 from nmaster2042/master
Update france.json
2023-08-26 16:07:07 +02:00
320f4459ed Update france.json
Change France Band plan 11m band high limit to 27,410 Mhz, see https://www.legifrance.gouv.fr/loda/article_lc/LEGIARTI000006430657/2020-10-31
2023-08-26 13:21:30 +02:00
de5816f79f Merge pull request #1160 from themeadery/usa-bandplan
Update usa.json bandplan with MURS frequencies
2023-08-07 07:40:49 +02:00
7b9c01ec73 fixed buffer overflow in file source 2023-08-06 04:02:51 +02:00
f06eccd97c Merge pull request #1161 from Manawyrm/manawyrm-bandplan
Update name in bandplan and contributors list
2023-08-05 00:03:25 +02:00
88baa8a48e Update name in bandplan and contributors list 2023-08-04 22:50:12 +02:00
b436fd0745 Update usa.json
Added MURS frequencies
2023-08-03 10:01:48 -07:00
6fa4299136 fixed bookworm build 2023-07-24 00:54:17 +02:00
220dcbcc76 added debian bookworm nightly 2023-07-24 00:05:25 +02:00
15ad065feb Merge pull request #1131 from rmdlv/patch-1
Fixed russia.json
2023-07-11 01:14:04 +02:00
dddf84510e Fixed russia.json
Source - https://digital.gov.ru/ru/documents/3634/
2023-07-10 21:18:33 +08:00
acd9ad9781 Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2023-07-10 04:41:41 +02:00
168e28cc44 added SNR meter smoothing 2023-07-10 04:41:34 +02:00
3b9867c1d7 Merge pull request #1104 from jj1bdx/jj1bdx-rigctl-get_powerstat-fix
rigctl_server: Add answering code for rigctl \get_powerstat
2023-07-09 18:53:40 +02:00
ff7ef78b8f Merge pull request #1114 from bvernoux/master
Workaround for RtAudio Exception when no Input are connected on Input MIC on some PC
2023-07-02 18:00:46 +02:00
87add9ad83 Fix formatting 2023-07-02 17:03:52 +02:00
6cd09f9b60 Workaround for RtAudio Exception when no Input are connected on MIC IN on some computer
Issue reproduced with Windows11 Pro + ROG CROSSHAIR X670E HERO
Workaround for RtAudio just add try/catch and log the error but allow to start SDR++
2023-07-02 16:44:47 +02:00
8d05c1e181 bugfix 2023-06-30 01:36:28 +02:00
1c081cad78 added OQPSK mode to the meteor demodulator 2023-06-29 01:19:15 +02:00
aa929a1e79 Add answering code for rigctl \get_powerstat
* Answer "1" (Power is ON) to \get_powerstat inquiry
2023-06-25 13:51:51 +09:00
5acdab0d22 Instantiated the perseus source by default and perseus on raspberrypi 2023-06-10 21:48:02 +02:00
3e3846daa1 another fix 2023-06-10 21:17:17 +02:00
47617e1acd trying again, powershell belongs in the trash 2023-06-10 20:37:38 +02:00
1df51020aa hopefully last windows CI fix 2023-06-10 20:22:32 +02:00
aa1fd9e573 enabled perseus-sdr build on windows 2023-06-10 19:24:40 +02:00
21816fb438 maybe CI fix 2023-06-10 03:56:46 +02:00
3a06612ff5 fixed missing autoconf on macos 2023-05-24 03:09:35 +02:00
a53bf05ed3 and another one... 2023-05-24 02:55:37 +02:00
78c57db116 I hate autotools so much 2023-05-24 02:52:57 +02:00
c16281f68a hopefully last fix... 2023-05-24 02:49:22 +02:00
32c580ba57 another fix 2023-05-24 02:47:04 +02:00
664b5d85e2 fixed again 2023-05-24 02:43:37 +02:00
6bcb62bfb2 fixed ci 2023-05-24 02:40:23 +02:00
0e7c754b8b Enabled perseus build on all unix 2023-05-24 02:32:27 +02:00
71327cd695 fixed driver download link 2023-05-17 18:37:21 +02:00
f296730302 fixed recorder frequency bug with multivfo 2023-05-17 03:58:49 +02:00
b89fdba433 Update united-kingdom.json 2023-05-02 21:15:02 +02:00
2c3b522787 Update contributing.md 2023-04-24 03:49:30 +02:00
528763d10e disabled spectran http sampleFreq autodetection 2023-04-22 00:27:31 +02:00
582aeed640 Added samplerate and center frequency tracking to http spectran source 2023-04-20 08:32:31 +02:00
9c0b57a036 Work on the spectran HTTP source 2023-04-19 02:25:44 +02:00
604f95fd96 Added perseus source (still a bit buggy) 2023-04-14 22:12:13 +02:00
c892e51000 Added Archlinux disclaimer 2023-04-10 22:53:41 +02:00
3336ae4aa5 added rigctl client to the macos bundle 2023-04-03 20:50:11 +02:00
190cea8e4e added missing include 2023-04-03 19:44:47 +02:00
13a268a3e1 Update module.cpp 2023-04-01 19:10:47 +02:00
800a8b22c7 fixed fft smoothing not set on startup when enabled 2023-03-31 01:38:14 +02:00
2eb030dd83 Added stdexcept 2023-03-30 19:55:54 +02:00
365fe1930c Update smgui.h 2023-03-30 19:17:15 +02:00
342a677c3f Merge pull request #1001 from konung-yaropolk/master
Added new colormaps: Vivid, Smoke and Temper
2023-03-30 08:41:58 +02:00
b5c41bcb3a added missing argument to macos instructions 2023-03-29 22:04:51 +02:00
f578adceef more work 2023-03-27 17:26:37 +02:00
a9f882e5b1 Fixed halfed volume with HPF on 2023-03-25 21:22:40 +01:00
3420808f3a added high-pass filter in NFM mode 2023-03-25 19:09:27 +01:00
d3d245992d Merge pull request #1021 from sergeyvfx/fix_start_crash
Fix crash in LimeSDR start when the device is disconnected
2023-03-23 23:20:49 +01:00
9a3414b847 Made the code more useful to the user
maybe just yeeting out of the start function with no indication to the user is a bad idea?
2023-03-23 23:19:05 +01:00
90c26f8c1b Fix crash in LimeSDR start when the device is disconnected
This fixes crash when a device which was enumerated on the
application startup gets disconnected prior to the attempt
to start the radio.

The check is done after the existing work around for the
LimeSuite bug. The idea is to give it the best possible
chance for the radio to start.

Possibly, the check needs needs to happen in the second
LMS_Open() but ideally this needs to be verified against
the original workaround scenario.
2023-03-23 22:36:44 +01:00
ec4dc6cc9e fix 2023-03-22 20:47:05 +01:00
93b28d1495 M17 decoder fix 2023-03-22 18:38:15 +01:00
84291deaf6 Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2023-03-22 18:20:38 +01:00
21e0696917 readme update 2023-03-22 18:20:34 +01:00
19247ef4f2 Merge pull request #1013 from sergeyvfx/fix_empty_limesdr_crash
Fix crash starting LimeSDR source without device
2023-03-14 23:00:13 +01:00
1e5601e773 Better check
Need to check if a device is actually selected, not just if there is one to select
2023-03-14 22:17:47 +01:00
ae1fd87f02 Fix crash starting LimeSDR source without device
This change fixes crash when a LimeSDR source is started without
an actual device selected.
2023-03-14 21:37:22 +01:00
ab2aee316c Merge pull request #1012 from Bastiti/patch-1
Belgian bandplan
2023-03-14 11:33:44 +01:00
109374277e Belgian bandplan
Added the amateur radio band as specified in the 2019
https://www.ibpt.be/file/cc73d96153bbd5448a56f19d925d05b1379c7f21/821514ac30e14d85dfdbe6747d1b82af9fd4da4c/2019-05-24_ram-decision.pdf

Broadcast band added from BIPT/IBPT Bandplanning.

According to Belgian laws, you're not allowed to receive anything execpt Ham Radio, CB, PMR446 or Broadcasting.
2023-03-14 09:08:43 +01:00
692436f6e4 Merge pull request #1010 from hb9fxq/patch-3
Update readme.md
2023-03-13 18:13:17 +01:00
eccb715d0c Update readme.md 2023-03-13 17:55:44 +01:00
f6f074e0c7 added missing patrons 2023-03-13 17:29:37 +01:00
50a77a7e60 Added a PR template 2023-03-11 13:52:37 +01:00
a4f3c92a03 added missing include to legacy network lib 2023-03-10 17:59:13 +01:00
37920b6476 adjustments 2023-03-10 00:45:56 +01:00
314b8bf72d Const correctness fix for optionlist 2023-03-09 15:14:33 +01:00
5f0858bab2 Removed logging from the DSP lib 2023-03-09 08:56:48 +01:00
007761a027 made it a bit cleaner 2023-03-08 19:49:23 +01:00
801f1be6b2 Hopefully a fix for the rfspace SDR-IP 2023-03-08 19:46:14 +01:00
9cc793e328 bugfix 2023-03-08 19:39:15 +01:00
1e01313612 Update vivid.json 2023-03-08 00:26:26 -06:00
4283cacae6 fixed build for volk 1.x 2023-03-02 15:19:16 +01:00
6f9dacdd53 Added back 'ignore silence' option in the recorder 2023-03-02 14:47:02 +01:00
e64c343645 Update vivid.json 2023-03-01 02:24:08 -06:00
kek
b9effce7d6 Added new colormap: Temper Colors 2023-02-26 05:19:34 -06:00
kek
45a13227de new file: root/res/colormaps/smoke.json
new file:   root/res/colormaps/vivid.json
2023-02-26 04:35:55 -06:00
81 changed files with 5523 additions and 972 deletions

7
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,7 @@
# Important
Only minor bug fixes and bandplans are accepted.
Pull requests adding features or any bug fix that requires significant code changes will be automatically rejected.
Open an issue requesting a feature or discussing a possible bugfix instead.

View File

@ -34,10 +34,10 @@ jobs:
- name: Patch Pothos with earlier libusb version - name: Patch Pothos with earlier libusb version
working-directory: ${{runner.workspace}} working-directory: ${{runner.workspace}}
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
- name: Download SDRPlay API - name: Download SDRPlay API
run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu" -OutFile ${{runner.workspace}}/SDRPlay.zip run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu&confirm=t" -OutFile ${{runner.workspace}}/SDRPlay.zip
- name: Install SDRPlay API - name: Install SDRPlay API
run: 7z x ${{runner.workspace}}/SDRPlay.zip -o"C:/Program Files/" run: 7z x ${{runner.workspace}}/SDRPlay.zip -o"C:/Program Files/"
@ -58,14 +58,17 @@ jobs:
run: mkdir "C:/Program Files/codec2" ; mkdir "C:/Program Files/codec2/include" ; mkdir "C:/Program Files/codec2/include/codec2" ; mkdir "C:/Program Files/codec2/lib" ; cd "codec2" ; xcopy "src" "C:/Program Files/codec2/include" ; cd "build" ; xcopy "src" "C:/Program Files/codec2/lib" ; xcopy "codec2" "C:/Program Files/codec2/include/codec2" run: mkdir "C:/Program Files/codec2" ; mkdir "C:/Program Files/codec2/include" ; mkdir "C:/Program Files/codec2/include/codec2" ; mkdir "C:/Program Files/codec2/lib" ; cd "codec2" ; xcopy "src" "C:/Program Files/codec2/include" ; cd "build" ; xcopy "src" "C:/Program Files/codec2/lib" ; xcopy "codec2" "C:/Program Files/codec2/include/codec2"
- name: Install vcpkg dependencies - name: Install vcpkg dependencies
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows
- name: Install rtaudio - name: Install rtaudio
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install . run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
- name: Install libperseus-sdr
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
- name: Prepare CMake - name: Prepare CMake
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
- name: Build - name: Build
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
@ -93,11 +96,8 @@ jobs:
- name: Update brew repositories - name: Update brew repositories
run: brew update run: brew update
- name: Fix stuff
run: rm -f /usr/local/bin/2to3* /usr/local/bin/idle3* /usr/local/bin/pydoc3* /usr/local/bin/python3* /usr/local/bin/python3-config* && brew reinstall gettext
- name: Install dependencies - name: Install dependencies
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf rtl-sdr libbladerf codec2 zstd && pip3 install mako run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
- name: Install volk - name: Install volk
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
@ -106,7 +106,7 @@ jobs:
run: wget https://www.sdrplay.com/software/SDRplay_RSP_API-MacOSX-3.07.3.pkg && sudo installer -pkg SDRplay_RSP_API-MacOSX-3.07.3.pkg -target / run: wget https://www.sdrplay.com/software/SDRplay_RSP_API-MacOSX-3.07.3.pkg && sudo installer -pkg SDRplay_RSP_API-MacOSX-3.07.3.pkg -target /
- name: Install libiio - name: Install libiio
run: git clone https://github.com/analogdevicesinc/libiio && cd libiio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install libad9361 - name: Install libad9361
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
@ -114,9 +114,15 @@ jobs:
- name: Install LimeSuite - name: Install LimeSuite
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install libperseus
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
- name: Install more recent librtlsdr
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
- name: Prepare CMake - name: Prepare CMake
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build - name: Build
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
@ -131,13 +137,13 @@ jobs:
with: with:
name: sdrpp_macos_intel name: sdrpp_macos_intel
path: ${{runner.workspace}}/sdrpp_macos_intel.zip path: ${{runner.workspace}}/sdrpp_macos_intel.zip
build_debian_buster: build_debian_buster:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Create Docker Image - name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build
@ -176,6 +182,28 @@ jobs:
name: sdrpp_debian_bullseye_amd64 name: sdrpp_debian_bullseye_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bookworm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bookworm && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- name: Recover Deb Archive
working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v3
with:
name: sdrpp_debian_bookworm_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_sid: build_debian_sid:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -198,28 +226,6 @@ jobs:
name: sdrpp_debian_sid_amd64 name: sdrpp_debian_sid_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
# build_ubuntu_bionic:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - name: Create Docker Image
# run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_bionic && docker build . --tag sdrpp_build
# - name: Run Container
# run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
# - name: Recover Deb Archive
# working-directory: ${{runner.workspace}}
# run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
# - name: Save Deb Archive
# uses: actions/upload-artifact@v3
# with:
# name: sdrpp_ubuntu_bionic_amd64
# path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_focal: build_ubuntu_focal:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -275,7 +281,7 @@ jobs:
- name: Prepare CMake - name: Prepare CMake
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_USRP_SOURCE=ON run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_USRP_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
- name: Build - name: Build
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
@ -319,7 +325,7 @@ jobs:
path: ${{runner.workspace}}/sdrpp.apk path: ${{runner.workspace}}/sdrpp.apk
create_full_archive: create_full_archive:
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_raspios_bullseye_armhf', 'build_android'] needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_raspios_bullseye_armhf', 'build_android']
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -333,6 +339,7 @@ jobs:
mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ && mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ &&
mv sdrpp_debian_buster_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_amd64.deb && mv sdrpp_debian_buster_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_amd64.deb &&
mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb && mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb &&
mv sdrpp_debian_bookworm_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_amd64.deb &&
mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb && mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb && mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb && mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb &&

View File

@ -17,24 +17,25 @@ option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON) option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON) option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF) option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
option(OPT_BUILD_SDRPP_SERVER_SOURCE "Build SDR++ Server Source Module (no dependencies required)" ON) option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON)
option(OPT_BUILD_RFSPACE_SOURCE "Build RFspace Source Module (no dependencies required)" ON) option(OPT_BUILD_RFSPACE_SOURCE "Build RFspace Source Module (no dependencies required)" ON)
option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Dependencies: librtlsdr)" ON) option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Dependencies: librtlsdr)" ON)
option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON) option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON)
option(OPT_BUILD_SDRPP_SERVER_SOURCE "Build SDR++ Server Source Module (no dependencies required)" ON)
option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Dependencies: libsdrplay)" OFF) option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Dependencies: libsdrplay)" OFF)
option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Dependencies: soapysdr)" ON) option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Dependencies: soapysdr)" ON)
option(OPT_BUILD_SPECTRAN_SOURCE "Build Spectran Source Module (Dependencies: Aaronia RTSA Suite)" OFF) option(OPT_BUILD_SPECTRAN_SOURCE "Build Spectran Source Module (Dependencies: Aaronia RTSA Suite)" OFF)
option(OPT_BUILD_SPECTRAN_HTTP_SOURCE "Build Spectran HTTP Source Module (no dependencies required)" ON) option(OPT_BUILD_SPECTRAN_HTTP_SOURCE "Build Spectran HTTP Source Module (no dependencies required)" ON)
option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON) option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON)
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON)
option(OPT_BUILD_USRP_SOURCE "Build USRP Source Module (libuhd)" OFF) option(OPT_BUILD_USRP_SOURCE "Build USRP Source Module (libuhd)" OFF)
# Sinks # Sinks
option(OPT_BUILD_ANDROID_AUDIO_SINK "Build Android Audio Sink Module (Dependencies: AAudio, only for android)" OFF) option(OPT_BUILD_ANDROID_AUDIO_SINK "Build Android Audio Sink Module (Dependencies: AAudio, only for android)" OFF)
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Dependencies: rtaudio)" ON) option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Dependencies: rtaudio)" ON)
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
option(OPT_BUILD_NETWORK_SINK "Build Audio Sink Module (no dependencies required)" ON) option(OPT_BUILD_NETWORK_SINK "Build Audio Sink Module (no dependencies required)" ON)
option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Dependencies: portaudio)" OFF) option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Dependencies: portaudio)" OFF)
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
# Decoders # Decoders
option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF) option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF)
@ -141,9 +142,13 @@ if (OPT_BUILD_LIMESDR_SOURCE)
add_subdirectory("source_modules/limesdr_source") add_subdirectory("source_modules/limesdr_source")
endif (OPT_BUILD_LIMESDR_SOURCE) endif (OPT_BUILD_LIMESDR_SOURCE)
if (OPT_BUILD_SDRPP_SERVER_SOURCE) if (OPT_BUILD_PERSEUS_SOURCE)
add_subdirectory("source_modules/sdrpp_server_source") add_subdirectory("source_modules/perseus_source")
endif (OPT_BUILD_SDRPP_SERVER_SOURCE) endif (OPT_BUILD_PERSEUS_SOURCE)
if (OPT_BUILD_PLUTOSDR_SOURCE)
add_subdirectory("source_modules/plutosdr_source")
endif (OPT_BUILD_PLUTOSDR_SOURCE)
if (OPT_BUILD_RFSPACE_SOURCE) if (OPT_BUILD_RFSPACE_SOURCE)
add_subdirectory("source_modules/rfspace_source") add_subdirectory("source_modules/rfspace_source")
@ -157,6 +162,10 @@ if (OPT_BUILD_RTL_TCP_SOURCE)
add_subdirectory("source_modules/rtl_tcp_source") add_subdirectory("source_modules/rtl_tcp_source")
endif (OPT_BUILD_RTL_TCP_SOURCE) endif (OPT_BUILD_RTL_TCP_SOURCE)
if (OPT_BUILD_SDRPP_SERVER_SOURCE)
add_subdirectory("source_modules/sdrpp_server_source")
endif (OPT_BUILD_SDRPP_SERVER_SOURCE)
if (OPT_BUILD_SDRPLAY_SOURCE) if (OPT_BUILD_SDRPLAY_SOURCE)
add_subdirectory("source_modules/sdrplay_source") add_subdirectory("source_modules/sdrplay_source")
endif (OPT_BUILD_SDRPLAY_SOURCE) endif (OPT_BUILD_SDRPLAY_SOURCE)
@ -177,10 +186,6 @@ if (OPT_BUILD_SPYSERVER_SOURCE)
add_subdirectory("source_modules/spyserver_source") add_subdirectory("source_modules/spyserver_source")
endif (OPT_BUILD_SPYSERVER_SOURCE) endif (OPT_BUILD_SPYSERVER_SOURCE)
if (OPT_BUILD_PLUTOSDR_SOURCE)
add_subdirectory("source_modules/plutosdr_source")
endif (OPT_BUILD_PLUTOSDR_SOURCE)
if (OPT_BUILD_USRP_SOURCE) if (OPT_BUILD_USRP_SOURCE)
add_subdirectory("source_modules/usrp_source") add_subdirectory("source_modules/usrp_source")
endif (OPT_BUILD_USRP_SOURCE) endif (OPT_BUILD_USRP_SOURCE)

View File

@ -1,6 +1,7 @@
# Pull Requests # Pull Requests
TODO **I DO NOT ACCEPT PULL-REQUEST FOR FEATURES OR BUGFIXES REQUIRING SIGNIFICANT CODE/STRUCTURE CHANGES.**
**SUCH PULL REQUESTS WILL BE CLOSED AUTOMATICALLY. OPEN AN ISSUE DETAILING FEATURE REQUESTS OR POTENTIAL BUGFIX INSTEAD.**
# Code Style # Code Style
@ -122,4 +123,4 @@ Please follow this guide to properly format the JSON files for custom color maps
* All additions and/or bug fixes to the core must not add additional dependencies. * All additions and/or bug fixes to the core must not add additional dependencies.
* Use VSCode for development, VS seems to cause issues. * Use VSCode for development, VS seems to cause issues.
* DO NOT use libboost for any code meant for this repository * DO NOT use libboost for any code meant for this repository

View File

@ -13,6 +13,7 @@ endif (USE_BUNDLE_DEFAULTS)
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c") file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
add_definitions(-DSDRPP_IS_CORE) add_definitions(-DSDRPP_IS_CORE)
add_definitions(-DFLOG_ANDROID_TAG="SDR++")
if (MSVC) if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif () endif ()
@ -107,7 +108,6 @@ elseif (ANDROID)
) )
target_link_libraries(sdrpp_core PUBLIC target_link_libraries(sdrpp_core PUBLIC
/sdr-kit/${ANDROID_ABI}/lib/libcpu_features.a
/sdr-kit/${ANDROID_ABI}/lib/libvolk.so /sdr-kit/${ANDROID_ABI}/lib/libvolk.so
/sdr-kit/${ANDROID_ABI}/lib/libfftw3f.so /sdr-kit/${ANDROID_ABI}/lib/libfftw3f.so
/sdr-kit/${ANDROID_ABI}/lib/libzstd.so /sdr-kit/${ANDROID_ABI}/lib/libzstd.so

View File

@ -117,6 +117,10 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["colorMap"] = "Classic"; defConfig["colorMap"] = "Classic";
defConfig["fftHold"] = false; defConfig["fftHold"] = false;
defConfig["fftHoldSpeed"] = 60; defConfig["fftHoldSpeed"] = 60;
defConfig["fftSmoothing"] = false;
defConfig["fftSmoothingSpeed"] = 100;
defConfig["snrSmoothing"] = false;
defConfig["snrSmoothingSpeed"] = 20;
defConfig["fastFFT"] = false; defConfig["fastFFT"] = false;
defConfig["fftHeight"] = 300; defConfig["fftHeight"] = 300;
defConfig["fftRate"] = 20; defConfig["fftRate"] = 20;
@ -177,6 +181,8 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true; defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source"; defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true; defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source"; defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true; defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true;
defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source"; defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";

View File

@ -12,6 +12,7 @@ namespace sdrpp_credits {
"Howard0su", "Howard0su",
"John Donkersley", "John Donkersley",
"Joshua Kimsey", "Joshua Kimsey",
"Manawyrm",
"Martin Hauke", "Martin Hauke",
"Marvin Sinister", "Marvin Sinister",
"Maxime Biette", "Maxime Biette",
@ -21,7 +22,6 @@ namespace sdrpp_credits {
"Shuyuan Liu", "Shuyuan Liu",
"Syne Ardwin (WI9SYN)", "Syne Ardwin (WI9SYN)",
"Szymon Zakrent", "Szymon Zakrent",
"Tobias Mädel",
"Youssef Touil", "Youssef Touil",
"Zimm" "Zimm"
}; };
@ -55,24 +55,33 @@ namespace sdrpp_credits {
"Dale L Puckett (K0HYD)", "Dale L Puckett (K0HYD)",
"Daniele D'Agnelli", "Daniele D'Agnelli",
"D. Jones", "D. Jones",
"Dexruus",
"EB3FRN", "EB3FRN",
"Eric Johnson", "Eric Johnson",
"Ernest Murphy (NH7L)", "Ernest Murphy (NH7L)",
"Flinger Films", "Flinger Films",
"Frank Werner (HB9FXQ)",
"gringogrigio", "gringogrigio",
"Jeff Moe",
"Joe Cupano", "Joe Cupano",
"KD1SQ",
"Kezza", "Kezza",
"Krys Kamieniecki", "Krys Kamieniecki",
"Lee Donaghy", "Lee Donaghy",
"Lee KD1SQ", "Lee KD1SQ",
".lozenge. (Hank Hill)", ".lozenge. (Hank Hill)",
"Martin Herren (HB9FXX)",
"ON4MU", "ON4MU",
"Passion-Radio.com", "Passion-Radio.com",
"Paul Maine", "Paul Maine",
"Peter Betz",
"Scanner School", "Scanner School",
"Scott Palmer",
"SignalsEverywhere", "SignalsEverywhere",
"Syne Ardwin (WI9SYN)", "Syne Ardwin (WI9SYN)",
"W4IPA", "W4IPA",
"William Arcand (W1WRA)",
"Yves Rougy",
"Zipper" "Zipper"
}; };

View File

@ -67,10 +67,6 @@ namespace dsp::buffer {
sizes[writeCur] = count; sizes[writeCur] = count;
writeCur++; writeCur++;
writeCur = ((writeCur) % TEST_BUFFER_SIZE); writeCur = ((writeCur) % TEST_BUFFER_SIZE);
// if (((writeCur - readCur + TEST_BUFFER_SIZE) % TEST_BUFFER_SIZE) >= (TEST_BUFFER_SIZE-2)) {
// flog::warn("Overflow");
// }
} }
cnd.notify_all(); cnd.notify_all();
_in->flush(); _in->flush();

View File

@ -3,6 +3,8 @@
#include "quadrature.h" #include "quadrature.h"
#include "../filter/fir.h" #include "../filter/fir.h"
#include "../taps/low_pass.h" #include "../taps/low_pass.h"
#include "../taps/high_pass.h"
#include "../taps/band_pass.h"
#include "../convert/mono_to_stereo.h" #include "../convert/mono_to_stereo.h"
namespace dsp::demod { namespace dsp::demod {
@ -17,22 +19,26 @@ namespace dsp::demod {
~FM() { ~FM() {
if (!base_type::_block_init) { return; } if (!base_type::_block_init) { return; }
base_type::stop(); base_type::stop();
dsp::taps::free(lpfTaps); dsp::taps::free(filterTaps);
} }
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass) { void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass, bool highPass) {
_samplerate = samplerate; _samplerate = samplerate;
_bandwidth = bandwidth; _bandwidth = bandwidth;
_lowPass = lowPass; _lowPass = lowPass;
_highPass = highPass;
demod.init(NULL, bandwidth / 2.0, _samplerate); demod.init(NULL, bandwidth / 2.0, _samplerate);
lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate); loadDummyTaps();
lpf.init(NULL, lpfTaps); fir.init(NULL, filterTaps);
// Initialize taps
updateFilter(lowPass, highPass);
if constexpr (std::is_same_v<T, float>) { if constexpr (std::is_same_v<T, float>) {
demod.out.free(); demod.out.free();
} }
lpf.out.free(); fir.out.free();
base_type::init(in); base_type::init(in);
} }
@ -43,9 +49,7 @@ namespace dsp::demod {
base_type::tempStop(); base_type::tempStop();
_samplerate = samplerate; _samplerate = samplerate;
demod.setDeviation(_bandwidth / 2.0, _samplerate); demod.setDeviation(_bandwidth / 2.0, _samplerate);
dsp::taps::free(lpfTaps); updateFilter(_lowPass, _highPass);
lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
lpf.setTaps(lpfTaps);
base_type::tempStart(); base_type::tempStart();
} }
@ -54,19 +58,20 @@ namespace dsp::demod {
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
if (bandwidth == _bandwidth) { return; } if (bandwidth == _bandwidth) { return; }
_bandwidth = bandwidth; _bandwidth = bandwidth;
std::lock_guard<std::mutex> lck2(lpfMtx);
demod.setDeviation(_bandwidth / 2.0, _samplerate); demod.setDeviation(_bandwidth / 2.0, _samplerate);
dsp::taps::free(lpfTaps); updateFilter(_lowPass, _highPass);
lpfTaps = dsp::taps::lowPass(_bandwidth / 2, (_bandwidth / 2) * 0.1, _samplerate);
lpf.setTaps(lpfTaps);
} }
void setLowPass(bool lowPass) { void setLowPass(bool lowPass) {
assert(base_type::_block_init); assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
std::lock_guard<std::mutex> lck2(lpfMtx); updateFilter(lowPass, _highPass);
_lowPass = lowPass; }
lpf.reset();
void setHighPass(bool highPass) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
updateFilter(_lowPass, highPass);
} }
void reset() { void reset() {
@ -74,23 +79,23 @@ namespace dsp::demod {
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop(); base_type::tempStop();
demod.reset(); demod.reset();
lpf.reset(); fir.reset();
base_type::tempStart(); base_type::tempStart();
} }
inline int process(int count, dsp::complex_t* in, T* out) { inline int process(int count, dsp::complex_t* in, T* out) {
if constexpr (std::is_same_v<T, float>) { if constexpr (std::is_same_v<T, float>) {
demod.process(count, in, out); demod.process(count, in, out);
if (_lowPass) { if (filtering) {
std::lock_guard<std::mutex> lck(lpfMtx); std::lock_guard<std::mutex> lck(filterMtx);
lpf.process(count, out, out); fir.process(count, out, out);
} }
} }
if constexpr (std::is_same_v<T, stereo_t>) { if constexpr (std::is_same_v<T, stereo_t>) {
demod.process(count, in, demod.out.writeBuf); demod.process(count, in, demod.out.writeBuf);
if (_lowPass) { if (filtering) {
std::lock_guard<std::mutex> lck(lpfMtx); std::lock_guard<std::mutex> lck(filterMtx);
lpf.process(count, demod.out.writeBuf, demod.out.writeBuf); fir.process(count, demod.out.writeBuf, demod.out.writeBuf);
} }
convert::MonoToStereo::process(count, demod.out.writeBuf, out); convert::MonoToStereo::process(count, demod.out.writeBuf, out);
} }
@ -109,13 +114,50 @@ namespace dsp::demod {
} }
private: private:
void updateFilter(bool lowPass, bool highPass) {
std::lock_guard<std::mutex> lck(filterMtx);
// Update values
_lowPass = lowPass;
_highPass = highPass;
filtering = (lowPass || highPass);
// Free filter taps
dsp::taps::free(filterTaps);
// Generate filter depending on low and high pass settings
if (_lowPass && _highPass) {
filterTaps = dsp::taps::bandPass<float>(300.0, _bandwidth / 2.0, 100.0, _samplerate);
}
else if (_highPass) {
filterTaps = dsp::taps::highPass(300.0, 100.0, _samplerate);
}
else if (_lowPass) {
filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
}
else {
loadDummyTaps();
}
// Set filter to use new taps
fir.setTaps(filterTaps);
fir.reset();
}
void loadDummyTaps() {
float dummyTap = 1.0f;
filterTaps = dsp::taps::fromArray<float>(1, &dummyTap);
}
double _samplerate; double _samplerate;
double _bandwidth; double _bandwidth;
bool _lowPass; bool _lowPass;
bool _highPass;
bool filtering;
Quadrature demod; Quadrature demod;
tap<float> lpfTaps; tap<float> filterTaps;
filter::FIR<float, float> lpf; filter::FIR<float, float> fir;
std::mutex lpfMtx; std::mutex filterMtx;
}; };
} }

View File

@ -2,5 +2,6 @@
#include "../multirate/rrc_interpolator.h" #include "../multirate/rrc_interpolator.h"
namespace dsp::mod { namespace dsp::mod {
// TODO: Check if resample before RRC is better than using the RRC taps as a filter (bandwidth probably not correct for alias-free resampling)
typedef multirate::RRCInterpolator<complex_t> PSK; typedef multirate::RRCInterpolator<complex_t> PSK;
} }

View File

@ -5,7 +5,7 @@
#include "../math/hz_to_rads.h" #include "../math/hz_to_rads.h"
namespace dsp::mod { namespace dsp::mod {
class Quadrature : Processor<float, complex_t> { class Quadrature : public Processor<float, complex_t> {
using base_type = Processor<float, complex_t>; using base_type = Processor<float, complex_t>;
public: public:
Quadrature() {} Quadrature() {}

View File

@ -83,8 +83,6 @@ namespace dsp::multirate {
int interp = OutSR / gcd; int interp = OutSR / gcd;
int decim = InSR / gcd; int decim = InSR / gcd;
flog::warn("interp: {0}, decim: {1}", interp, decim);
// Configure resampler // Configure resampler
double tapSamplerate = _symbolrate * (double)interp; double tapSamplerate = _symbolrate * (double)interp;
rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount * interp, _rrcBeta, _symbolrate, tapSamplerate); rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount * interp, _rrcBeta, _symbolrate, tapSamplerate);

View File

@ -15,7 +15,7 @@ namespace dsp::taps {
if (oddTapCount && !(count % 2)) { count++; } if (oddTapCount && !(count % 2)) { count++; }
return windowedSinc<T>(count, (bandStop - bandStart) / 2.0, sampleRate, [=](double n, double N) { return windowedSinc<T>(count, (bandStop - bandStart) / 2.0, sampleRate, [=](double n, double N) {
if constexpr (std::is_same_v<T, float>) { if constexpr (std::is_same_v<T, float>) {
return cosf(offsetOmega * (float)n) * window::nuttall(n, N); return 2.0f * cosf(offsetOmega * (float)n) * window::nuttall(n, N);
} }
if constexpr (std::is_same_v<T, complex_t>) { if constexpr (std::is_same_v<T, complex_t>) {
// The offset is negative to flip the taps. Complex bandpass are asymetric // The offset is negative to flip the taps. Complex bandpass are asymetric

View File

@ -82,7 +82,7 @@ namespace dsp {
inline float fastAmplitude() { inline float fastAmplitude() {
float re_abs = fabsf(re); float re_abs = fabsf(re);
float im_abs = fabsf(re); float im_abs = fabsf(im);
if (re_abs > im_abs) { return re_abs + 0.4f * im_abs; } if (re_abs > im_abs) { return re_abs + 0.4f * im_abs; }
return im_abs + 0.4f * re_abs; return im_abs + 0.4f * re_abs;
} }
@ -125,4 +125,4 @@ namespace dsp {
float l; float l;
float r; float r;
}; };
} }

View File

@ -574,10 +574,22 @@ void MainWindow::draw() {
// Handle scrollwheel // Handle scrollwheel
int wheel = ImGui::GetIO().MouseWheel; int wheel = ImGui::GetIO().MouseWheel;
if (wheel != 0 && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) { if (wheel != 0 && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) {
// Select factor depending on modifier keys
double interval;
if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) {
interval = vfo->snapInterval * 10.0;
}
else if (ImGui::IsKeyDown(ImGuiKey_LeftAlt)) {
interval = vfo->snapInterval * 0.1;
}
else {
interval = vfo->snapInterval;
}
double nfreq; double nfreq;
if (vfo != NULL) { if (vfo != NULL) {
nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (vfo->snapInterval * wheel); nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (interval * wheel);
nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval; nfreq = roundl(nfreq / interval) * interval;
} }
else { else {
nfreq = gui::waterfall.getCenterFrequency() - (gui::waterfall.getViewBandwidth() * wheel / 20.0); nfreq = gui::waterfall.getCenterFrequency() - (gui::waterfall.getViewBandwidth() * wheel / 20.0);

View File

@ -8,6 +8,7 @@
#include <signal_path/signal_path.h> #include <signal_path/signal_path.h>
#include <gui/style.h> #include <gui/style.h>
#include <utils/optionlist.h> #include <utils/optionlist.h>
#include <algorithm>
namespace displaymenu { namespace displaymenu {
bool showWaterfall; bool showWaterfall;
@ -22,6 +23,10 @@ namespace displaymenu {
bool restartRequired = false; bool restartRequired = false;
bool fftHold = false; bool fftHold = false;
int fftHoldSpeed = 60; int fftHoldSpeed = 60;
bool fftSmoothing = false;
int fftSmoothingSpeed = 100;
bool snrSmoothing = false;
int snrSmoothingSpeed = 20;
OptionList<float, float> uiScales; OptionList<float, float> uiScales;
@ -57,8 +62,10 @@ namespace displaymenu {
IQFrontEnd::FFTWindow::NUTTALL IQFrontEnd::FFTWindow::NUTTALL
}; };
void updateFFTHoldSpeed() { void updateFFTSpeeds() {
gui::waterfall.setFFTHoldSpeed(fftHoldSpeed / (fftRate * 10.0f)); gui::waterfall.setFFTHoldSpeed((float)fftHoldSpeed / ((float)fftRate * 10.0f));
gui::waterfall.setFFTSmoothingSpeed(std::min<float>((float)fftSmoothingSpeed / (float)(fftRate * 10.0f), 1.0f));
gui::waterfall.setSNRSmoothingSpeed(std::min<float>((float)snrSmoothingSpeed / (float)(fftRate * 10.0f), 1.0f));
} }
void init() { void init() {
@ -104,7 +111,13 @@ namespace displaymenu {
fftHold = core::configManager.conf["fftHold"]; fftHold = core::configManager.conf["fftHold"];
fftHoldSpeed = core::configManager.conf["fftHoldSpeed"]; fftHoldSpeed = core::configManager.conf["fftHoldSpeed"];
gui::waterfall.setFFTHold(fftHold); gui::waterfall.setFFTHold(fftHold);
updateFFTHoldSpeed(); fftSmoothing = core::configManager.conf["fftSmoothing"];
fftSmoothingSpeed = core::configManager.conf["fftSmoothingSpeed"];
gui::waterfall.setFFTSmoothing(fftSmoothing);
snrSmoothing = core::configManager.conf["snrSmoothing"];
snrSmoothingSpeed = core::configManager.conf["snrSmoothingSpeed"];
gui::waterfall.setSNRSmoothing(snrSmoothing);
updateFFTSpeeds();
// Define and load UI scales // Define and load UI scales
uiScales.define(1.0f, "100%", 1.0f); uiScales.define(1.0f, "100%", 1.0f);
@ -144,16 +157,47 @@ namespace displaymenu {
core::configManager.conf["fftHold"] = fftHold; core::configManager.conf["fftHold"] = fftHold;
core::configManager.release(true); core::configManager.release(true);
} }
ImGui::SameLine();
ImGui::LeftLabel("FFT Hold Speed");
ImGui::FillWidth(); ImGui::FillWidth();
if (ImGui::InputInt("##sdrpp_fft_hold_speed", &fftHoldSpeed)) { if (ImGui::InputInt("##sdrpp_fft_hold_speed", &fftHoldSpeed)) {
updateFFTHoldSpeed(); updateFFTSpeeds();
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["fftHoldSpeed"] = fftHoldSpeed; core::configManager.conf["fftHoldSpeed"] = fftHoldSpeed;
core::configManager.release(true); core::configManager.release(true);
} }
if (ImGui::Checkbox("FFT Smoothing##_sdrpp", &fftSmoothing)) {
gui::waterfall.setFFTSmoothing(fftSmoothing);
core::configManager.acquire();
core::configManager.conf["fftSmoothing"] = fftSmoothing;
core::configManager.release(true);
}
ImGui::SameLine();
ImGui::FillWidth();
if (ImGui::InputInt("##sdrpp_fft_smoothing_speed", &fftSmoothingSpeed)) {
fftSmoothingSpeed = std::max<int>(fftSmoothingSpeed, 1);
updateFFTSpeeds();
core::configManager.acquire();
core::configManager.conf["fftSmoothingSpeed"] = fftSmoothingSpeed;
core::configManager.release(true);
}
if (ImGui::Checkbox("SNR Smoothing##_sdrpp", &snrSmoothing)) {
gui::waterfall.setSNRSmoothing(snrSmoothing);
core::configManager.acquire();
core::configManager.conf["snrSmoothing"] = snrSmoothing;
core::configManager.release(true);
}
ImGui::SameLine();
ImGui::FillWidth();
if (ImGui::InputInt("##sdrpp_snr_smoothing_speed", &snrSmoothingSpeed)) {
snrSmoothingSpeed = std::max<int>(snrSmoothingSpeed, 1);
updateFFTSpeeds();
core::configManager.acquire();
core::configManager.conf["snrSmoothingSpeed"] = snrSmoothingSpeed;
core::configManager.release(true);
}
ImGui::LeftLabel("High-DPI Scaling"); ImGui::LeftLabel("High-DPI Scaling");
ImGui::FillWidth(); ImGui::FillWidth();
if (ImGui::Combo("##sdrpp_ui_scale", &uiScaleId, uiScales.txt)) { if (ImGui::Combo("##sdrpp_ui_scale", &uiScaleId, uiScales.txt)) {
@ -168,7 +212,7 @@ namespace displaymenu {
if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) { if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) {
fftRate = std::max<int>(1, fftRate); fftRate = std::max<int>(1, fftRate);
sigpath::iqFrontEnd.setFFTRate(fftRate); sigpath::iqFrontEnd.setFFTRate(fftRate);
updateFFTHoldSpeed(); updateFFTSpeeds();
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["fftRate"] = fftRate; core::configManager.conf["fftRate"] = fftRate;
core::configManager.release(true); core::configManager.release(true);
@ -210,4 +254,4 @@ namespace displaymenu {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Restart required."); ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Restart required.");
} }
} }
} }

View File

@ -15,6 +15,10 @@ namespace sourcemenu {
bool iqCorrection = false; bool iqCorrection = false;
bool invertIQ = false; bool invertIQ = false;
EventHandler<std::string> sourceRegisteredHandler;
EventHandler<std::string> sourceUnregisterHandler;
EventHandler<std::string> sourceUnregisteredHandler;
std::vector<std::string> sourceNames; std::vector<std::string> sourceNames;
std::string sourceNamesTxt; std::string sourceNamesTxt;
std::string selectedSource; std::string selectedSource;
@ -95,10 +99,10 @@ namespace sourcemenu {
} }
sourceId = std::distance(sourceNames.begin(), it); sourceId = std::distance(sourceNames.begin(), it);
selectedSource = sourceNames[sourceId]; selectedSource = sourceNames[sourceId];
sigpath::sourceManager.select(sourceNames[sourceId]); sigpath::sourceManager.selectSource(sourceNames[sourceId]);
} }
void onSourceRegistered(std::string name) { void onSourceRegistered(std::string name, void* ctx) {
refreshSources(); refreshSources();
if (selectedSource.empty()) { if (selectedSource.empty()) {
@ -110,13 +114,13 @@ namespace sourcemenu {
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource)); sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
} }
void onSourceUnregister(std::string name) { void onSourceUnregister(std::string name, void* ctx) {
if (name != selectedSource) { return; } if (name != selectedSource) { return; }
// TODO: Stop everything // TODO: Stop everything
} }
void onSourceUnregistered(std::string name) { void onSourceUnregistered(std::string name, void* ctx) {
refreshSources(); refreshSources();
if (sourceNames.empty()) { if (sourceNames.empty()) {
@ -149,9 +153,12 @@ namespace sourcemenu {
selectSource(selected); selectSource(selected);
sigpath::iqFrontEnd.setDecimation(1 << decimationPower); sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
sigpath::sourceManager.onSourceRegistered.bind(onSourceRegistered); sourceRegisteredHandler.handler = onSourceRegistered;
sigpath::sourceManager.onSourceUnregister.bind(onSourceUnregister); sourceUnregisterHandler.handler = onSourceUnregister;
sigpath::sourceManager.onSourceUnregistered.bind(onSourceUnregistered); sourceUnregisteredHandler.handler = onSourceUnregistered;
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
core::configManager.release(); core::configManager.release();
} }
@ -172,7 +179,7 @@ namespace sourcemenu {
if (running) { style::endDisabled(); } if (running) { style::endDisabled(); }
sigpath::sourceManager.showMenu(); sigpath::sourceManager.showSelectedMenu();
if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) { if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) {
sigpath::iqFrontEnd.setDCBlocking(iqCorrection); sigpath::iqFrontEnd.setDCBlocking(iqCorrection);

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <stdint.h>
#include <imgui.h> #include <imgui.h>
#include <string> #include <string>
#include <vector> #include <vector>
@ -144,4 +145,4 @@ namespace SmGui {
// Config configs // Config configs
void ForceSyncForNext(); void ForceSyncForNext();
} }

View File

@ -689,6 +689,7 @@ namespace ImGui {
void WaterFall::onResize() { void WaterFall::onResize() {
std::lock_guard<std::recursive_mutex> lck(latestFFTMtx); std::lock_guard<std::recursive_mutex> lck(latestFFTMtx);
std::lock_guard<std::mutex> lck2(smoothingBufMtx);
// return if widget is too small // return if widget is too small
if (widgetSize.x < 100 || widgetSize.y < 100) { if (widgetSize.x < 100 || widgetSize.y < 100) {
return; return;
@ -740,14 +741,23 @@ namespace ImGui {
} }
latestFFTHold = new float[dataWidth]; latestFFTHold = new float[dataWidth];
// Reallocate smoothing buffer
if (fftSmoothing) {
if (smoothingBuf) { delete[] smoothingBuf; }
smoothingBuf = new float[dataWidth];
for (int i = 0; i < dataWidth; i++) {
smoothingBuf[i] = -1000.0f;
}
}
if (waterfallVisible) { if (waterfallVisible) {
delete[] waterfallFb; delete[] waterfallFb;
waterfallFb = new uint32_t[dataWidth * waterfallHeight]; waterfallFb = new uint32_t[dataWidth * waterfallHeight];
memset(waterfallFb, 0, dataWidth * waterfallHeight * sizeof(uint32_t)); memset(waterfallFb, 0, dataWidth * waterfallHeight * sizeof(uint32_t));
} }
for (int i = 0; i < dataWidth; i++) { for (int i = 0; i < dataWidth; i++) {
latestFFT[i] = -1000.0; // Hide everything latestFFT[i] = -1000.0f; // Hide everything
latestFFTHold[i] = -1000.0; latestFFTHold[i] = -1000.0f;
} }
fftAreaMin = ImVec2(widgetPos.x + (50.0f * style::uiScale), widgetPos.y + (9.0f * style::uiScale)); fftAreaMin = ImVec2(widgetPos.x + (50.0f * style::uiScale), widgetPos.y + (9.0f * style::uiScale));
@ -873,9 +883,25 @@ namespace ImGui {
fftLines = 1; fftLines = 1;
} }
// Apply smoothing if enabled
if (fftSmoothing && latestFFT != NULL && smoothingBuf != NULL && fftLines != 0) {
std::lock_guard<std::mutex> lck2(smoothingBufMtx);
volk_32f_s32f_multiply_32f(latestFFT, latestFFT, fftSmoothingAlpha, dataWidth);
volk_32f_s32f_multiply_32f(smoothingBuf, smoothingBuf, fftSmoothingBeta, dataWidth);
volk_32f_x2_add_32f(smoothingBuf, latestFFT, smoothingBuf, dataWidth);
memcpy(latestFFT, smoothingBuf, dataWidth * sizeof(float));
}
if (selectedVFO != "" && vfos.size() > 0) { if (selectedVFO != "" && vfos.size() > 0) {
float dummy; float dummy;
calculateVFOSignalInfo(waterfallVisible ? &rawFFTs[currentFFTLine * rawFFTSize] : rawFFTs, vfos[selectedVFO], dummy, selectedVFOSNR); if (snrSmoothing) {
float newSNR = 0.0f;
calculateVFOSignalInfo(waterfallVisible ? &rawFFTs[currentFFTLine * rawFFTSize] : rawFFTs, vfos[selectedVFO], dummy, newSNR);
selectedVFOSNR = (snrSmoothingBeta*selectedVFOSNR) + (snrSmoothingAlpha*newSNR);
}
else {
calculateVFOSignalInfo(waterfallVisible ? &rawFFTs[currentFFTLine * rawFFTSize] : rawFFTs, vfos[selectedVFO], dummy, selectedVFOSNR);
}
} }
// If FFT hold is enabled, update it // If FFT hold is enabled, update it
@ -1110,6 +1136,45 @@ namespace ImGui {
fftHoldSpeed = speed; fftHoldSpeed = speed;
} }
void WaterFall::setFFTSmoothing(bool enabled) {
std::lock_guard<std::mutex> lck(smoothingBufMtx);
fftSmoothing = enabled;
// Free buffer if not null
if (smoothingBuf) {delete[] smoothingBuf; }
// If disabled, stop here
if (!enabled) {
smoothingBuf = NULL;
return;
}
// Allocate and copy existing FFT into it
smoothingBuf = new float[dataWidth];
if (latestFFT) {
std::lock_guard<std::recursive_mutex> lck2(latestFFTMtx);
memcpy(smoothingBuf, latestFFT, dataWidth * sizeof(float));
}
else {
memset(smoothingBuf, 0, dataWidth * sizeof(float));
}
}
void WaterFall::setFFTSmoothingSpeed(float speed) {
std::lock_guard<std::mutex> lck(smoothingBufMtx);
fftSmoothingAlpha = speed;
fftSmoothingBeta = 1.0f - speed;
}
void WaterFall::setSNRSmoothing(bool enabled) {
snrSmoothing = enabled;
}
void WaterFall::setSNRSmoothingSpeed(float speed) {
snrSmoothingAlpha = speed;
snrSmoothingBeta = 1.0f - speed;
}
float* WaterFall::acquireLatestFFT(int& width) { float* WaterFall::acquireLatestFFT(int& width) {
latestFFTMtx.lock(); latestFFTMtx.lock();
if (!latestFFT) { if (!latestFFT) {

View File

@ -169,6 +169,12 @@ namespace ImGui {
void setFFTHold(bool hold); void setFFTHold(bool hold);
void setFFTHoldSpeed(float speed); void setFFTHoldSpeed(float speed);
void setFFTSmoothing(bool enabled);
void setFFTSmoothingSpeed(float speed);
void setSNRSmoothing(bool enabled);
void setSNRSmoothingSpeed(float speed);
float* acquireLatestFFT(int& width); float* acquireLatestFFT(int& width);
void releaseLatestFFT(); void releaseLatestFFT();
@ -182,7 +188,7 @@ namespace ImGui {
bool mouseInFFT = false; bool mouseInFFT = false;
bool mouseInWaterfall = false; bool mouseInWaterfall = false;
float selectedVFOSNR = NAN; float selectedVFOSNR = 0.0f;
bool centerFrequencyLocked = false; bool centerFrequencyLocked = false;
@ -270,6 +276,7 @@ namespace ImGui {
std::recursive_mutex buf_mtx; std::recursive_mutex buf_mtx;
std::recursive_mutex latestFFTMtx; std::recursive_mutex latestFFTMtx;
std::mutex texMtx; std::mutex texMtx;
std::mutex smoothingBufMtx;
float vRange; float vRange;
@ -304,8 +311,9 @@ namespace ImGui {
//std::vector<std::vector<float>> rawFFTs; //std::vector<std::vector<float>> rawFFTs;
int rawFFTSize; int rawFFTSize;
float* rawFFTs = NULL; float* rawFFTs = NULL;
float* latestFFT; float* latestFFT = NULL;
float* latestFFTHold; float* latestFFTHold = NULL;
float* smoothingBuf = NULL;
int currentFFTLine = 0; int currentFFTLine = 0;
int fftLines = 0; int fftLines = 0;
@ -325,6 +333,14 @@ namespace ImGui {
bool fftHold = false; bool fftHold = false;
float fftHoldSpeed = 0.3f; float fftHoldSpeed = 0.3f;
bool fftSmoothing = false;
float fftSmoothingAlpha = 0.5;
float fftSmoothingBeta = 0.5;
bool snrSmoothing = false;
float snrSmoothingAlpha = 0.5;
float snrSmoothingBeta = 0.5;
// UI Select elements // UI Select elements
bool fftResizeSelect = false; bool fftResizeSelect = false;
bool freqScaleSelect = false; bool freqScaleSelect = false;

View File

@ -33,7 +33,7 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
#else #else
mod.handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); mod.handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (mod.handle == NULL) { if (mod.handle == NULL) {
flog::error("Couldn't load {0}.", path); flog::error("Couldn't load {0}: {1}", path, dlerror());
mod.handle = NULL; mod.handle = NULL;
return mod; return mod;
} }
@ -183,4 +183,4 @@ void ModuleManager::doPostInitAll() {
flog::info("Running post-init for {0}", name); flog::info("Running post-init for {0}", name);
inst.instance->postInit(); inst.instance->postInit();
} }
} }

View File

@ -2,7 +2,7 @@
#include <utils/flog.h> #include <utils/flog.h>
bool ModuleComManager::registerInterface(std::string moduleName, std::string name, void (*handler)(int code, void* in, void* out, void* ctx), void* ctx) { bool ModuleComManager::registerInterface(std::string moduleName, std::string name, void (*handler)(int code, void* in, void* out, void* ctx), void* ctx) {
std::lock_guard<std::mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
if (interfaces.find(name) != interfaces.end()) { if (interfaces.find(name) != interfaces.end()) {
flog::error("Tried creating module interface with an existing name: {0}", name); flog::error("Tried creating module interface with an existing name: {0}", name);
return false; return false;
@ -16,7 +16,7 @@ bool ModuleComManager::registerInterface(std::string moduleName, std::string nam
} }
bool ModuleComManager::unregisterInterface(std::string name) { bool ModuleComManager::unregisterInterface(std::string name) {
std::lock_guard<std::mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
if (interfaces.find(name) == interfaces.end()) { if (interfaces.find(name) == interfaces.end()) {
flog::error("Tried to erase module interface with unknown name: {0}", name); flog::error("Tried to erase module interface with unknown name: {0}", name);
return false; return false;
@ -26,13 +26,13 @@ bool ModuleComManager::unregisterInterface(std::string name) {
} }
bool ModuleComManager::interfaceExists(std::string name) { bool ModuleComManager::interfaceExists(std::string name) {
std::lock_guard<std::mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
if (interfaces.find(name) == interfaces.end()) { return false; } if (interfaces.find(name) == interfaces.end()) { return false; }
return true; return true;
} }
std::string ModuleComManager::getModuleName(std::string name) { std::string ModuleComManager::getModuleName(std::string name) {
std::lock_guard<std::mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
if (interfaces.find(name) == interfaces.end()) { if (interfaces.find(name) == interfaces.end()) {
flog::error("Tried to call unknown module interface: {0}", name); flog::error("Tried to call unknown module interface: {0}", name);
return ""; return "";
@ -41,7 +41,7 @@ std::string ModuleComManager::getModuleName(std::string name) {
} }
bool ModuleComManager::callInterface(std::string name, int code, void* in, void* out) { bool ModuleComManager::callInterface(std::string name, int code, void* in, void* out) {
std::lock_guard<std::mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
if (interfaces.find(name) == interfaces.end()) { if (interfaces.find(name) == interfaces.end()) {
flog::error("Tried to call unknown module interface: {0}", name); flog::error("Tried to call unknown module interface: {0}", name);
return false; return false;

View File

@ -18,6 +18,6 @@ public:
bool callInterface(std::string name, int code, void* in, void* out); bool callInterface(std::string name, int code, void* in, void* out);
private: private:
std::mutex mtx; std::recursive_mutex mtx;
std::map<std::string, ModuleComInterface> interfaces; std::map<std::string, ModuleComInterface> interfaces;
}; };

View File

@ -146,7 +146,7 @@ namespace server {
// Load sourceId from config // Load sourceId from config
sourceId = 0; sourceId = 0;
if (sourceList.keyExists(sourceName)) { sourceId = sourceList.keyId(sourceName); } if (sourceList.keyExists(sourceName)) { sourceId = sourceList.keyId(sourceName); }
sigpath::sourceManager.select(sourceList[sourceId]); sigpath::sourceManager.selectSource(sourceList[sourceId]);
// TODO: Use command line option // TODO: Use command line option
std::string host = (std::string)core::args["addr"]; std::string host = (std::string)core::args["addr"];
@ -280,7 +280,8 @@ namespace server {
} }
} }
else if (cmd == COMMAND_START) { else if (cmd == COMMAND_START) {
running = sigpath::sourceManager.start(); sigpath::sourceManager.start();
running = true;
} }
else if (cmd == COMMAND_STOP) { else if (cmd == COMMAND_STOP) {
sigpath::sourceManager.stop(); sigpath::sourceManager.stop();
@ -308,14 +309,14 @@ namespace server {
SmGui::FillWidth(); SmGui::FillWidth();
SmGui::ForceSync(); SmGui::ForceSync();
if (SmGui::Combo("##sdrpp_server_src_sel", &sourceId, sourceList.txt)) { if (SmGui::Combo("##sdrpp_server_src_sel", &sourceId, sourceList.txt)) {
sigpath::sourceManager.select(sourceList[sourceId]); sigpath::sourceManager.selectSource(sourceList[sourceId]);
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["source"] = sourceList.key(sourceId); core::configManager.conf["source"] = sourceList.key(sourceId);
core::configManager.release(true); core::configManager.release(true);
} }
if (running) { SmGui::EndDisabled(); } if (running) { SmGui::EndDisabled(); }
sigpath::sourceManager.showMenu(); sigpath::sourceManager.showSelectedMenu();
} }
void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue) { void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue) {

View File

@ -1,186 +1,106 @@
#include "source.h" #include <server.h>
#include <signal_path/source.h>
#include <utils/flog.h> #include <utils/flog.h>
#include <signal_path/signal_path.h>
#include <core.h>
void SourceManager::registerSource(const std::string& name, Source* source) { SourceManager::SourceManager() {
std::lock_guard<std::recursive_mutex> lck(mtx); }
// Check arguments void SourceManager::registerSource(std::string name, SourceHandler* handler) {
if (source || name.empty()) {
flog::error("Invalid argument to register source", name);
return;
}
// Check that a source with that name doesn't already exist
if (sources.find(name) != sources.end()) { if (sources.find(name) != sources.end()) {
flog::error("Tried to register source with existing name: {}", name); flog::error("Tried to register new source with existing name: {0}", name);
return; return;
} }
sources[name] = handler;
// Add source to map onSourceRegistered.emit(name);
sources[name] = source;
// Add source to lists
sourceNames.push_back(name);
onSourceRegistered(name);
} }
void SourceManager::unregisterSource(const std::string& name) { void SourceManager::unregisterSource(std::string name) {
std::lock_guard<std::recursive_mutex> lck(mtx);
// Check that a source with that name exists
if (sources.find(name) == sources.end()) { if (sources.find(name) == sources.end()) {
flog::error("Tried to unregister a non-existent source: {}", name); flog::error("Tried to unregister non existent source: {0}", name);
return; return;
} }
onSourceUnregister.emit(name);
// Notify event listeners of the imminent deletion if (name == selectedName) {
onSourceUnregister(name); if (selectedHandler != NULL) {
sources[selectedName]->deselectHandler(sources[selectedName]->ctx);
// Delete from lists }
sourceNames.erase(std::find(sourceNames.begin(), sourceNames.end(), name)); sigpath::iqFrontEnd.setInput(&nullSource);
selectedHandler = NULL;
}
sources.erase(name); sources.erase(name);
onSourceUnregistered.emit(name);
// Notify event listeners of the deletion
onSourceUnregistered(name);
} }
const std::vector<std::string>& SourceManager::getSourceNames() { std::vector<std::string> SourceManager::getSourceNames() {
std::lock_guard<std::recursive_mutex> lck(mtx); std::vector<std::string> names;
return sourceNames; for (auto const& [name, src] : sources) { names.push_back(name); }
return names;
} }
void SourceManager::select(const std::string& name) { void SourceManager::selectSource(std::string name) {
std::lock_guard<std::recursive_mutex> lck(mtx);
// make sure that source isn't currently selected
if (selectedSourceName == name) { return; }
// Deselect current source
deselect();
// Check that a source with that name exists
if (sources.find(name) == sources.end()) { if (sources.find(name) == sources.end()) {
flog::error("Tried to select a non-existent source: {}", name); flog::error("Tried to select non existent source: {0}", name);
return; return;
} }
if (selectedHandler != NULL) {
// Select the source sources[selectedName]->deselectHandler(sources[selectedName]->ctx);
selectedSourceName = name; }
selectedSource = sources[name]; selectedHandler = sources[name];
selectedHandler->selectHandler(selectedHandler->ctx);
// Call the selected source selectedName = name;
selectedSource->select(); if (core::args["server"].b()) {
server::setInput(selectedHandler->stream);
// Retune to make sure the source has the latest frequency }
tune(frequency); else {
sigpath::iqFrontEnd.setInput(selectedHandler->stream);
}
// Set server input here
} }
const std::string& SourceManager::getSelected() { void SourceManager::showSelectedMenu() {
std::lock_guard<std::recursive_mutex> lck(mtx); if (selectedHandler == NULL) {
return selectedSourceName; return;
}
selectedHandler->menuHandler(selectedHandler->ctx);
} }
bool SourceManager::start() { void SourceManager::start() {
std::lock_guard<std::recursive_mutex> lck(mtx); if (selectedHandler == NULL) {
return;
// Check if not already running }
if (running) { return true; } selectedHandler->startHandler(selectedHandler->ctx);
// Call source if selected and save if started
running = (!selectedSource) ? false : selectedSource->start();
return running;
} }
void SourceManager::stop() { void SourceManager::stop() {
std::lock_guard<std::recursive_mutex> lck(mtx); if (selectedHandler == NULL) {
return;
// Check if running }
if (!running) { return; } selectedHandler->stopHandler(selectedHandler->ctx);
// Call source if selected and save state
if (selectedSource) { selectedSource->stop(); }
running = false;
}
bool SourceManager::isRunning() {
std::lock_guard<std::recursive_mutex> lck(mtx);
return running;
} }
void SourceManager::tune(double freq) { void SourceManager::tune(double freq) {
std::lock_guard<std::recursive_mutex> lck(mtx); if (selectedHandler == NULL) {
return;
// Save frequency
frequency = freq;
// Call source if selected
if (selectedSource) {
selectedSource->tune(((mode == TUNING_MODE_NORMAL) ? freq : ifFrequency) + offset);
} }
// TODO: No need to always retune the hardware in panadpter mode
selectedHandler->tuneHandler(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset, selectedHandler->ctx);
onRetune.emit(freq);
currentFreq = freq;
} }
void SourceManager::showMenu() {
std::lock_guard<std::recursive_mutex> lck(mtx);
// Call source if selected
if (selectedSource) { selectedSource->showMenu(); }
}
double SourceManager::getSamplerate() {
std::lock_guard<std::recursive_mutex> lck(mtx);
return samplerate;
}
// =========== TODO: These functions should not happen in this class ===========
void SourceManager::setTuningOffset(double offset) { void SourceManager::setTuningOffset(double offset) {
std::lock_guard<std::recursive_mutex> lck(mtx); tuneOffset = offset;
tune(currentFreq);
// Update offset
this->offset = offset;
// Retune to take affect
tune(frequency);
} }
void SourceManager::setTuningMode(TuningMode mode) { void SourceManager::setTuningMode(TuningMode mode) {
std::lock_guard<std::recursive_mutex> lck(mtx); tuneMode = mode;
tune(currentFreq);
// Update mode
this->mode = mode;
// Retune to take affect
tune(frequency);
} }
void SourceManager::setPanadpterIF(double freq) { void SourceManager::setPanadpterIF(double freq) {
std::lock_guard<std::recursive_mutex> lck(mtx); ifFreq = freq;
tune(currentFreq);
// Update offset
ifFrequency = freq;
// Return to take affect if in panadapter mode
if (mode == TUNING_MODE_PANADAPTER) { tune(frequency); }
}
// =============================================================================
void SourceManager::deselect() {
std::lock_guard<std::recursive_mutex> lck(mtx);
// Call source if selected
if (selectedSource) { selectedSource->deselect(); }
// Mark as deselected
selectedSourceName.clear();
selectedSource = NULL;
}
void SourceManager::setSamplerate(double samplerate) {
std::lock_guard<std::recursive_mutex> lck(mtx);
// Save samplerate and emit event
this->samplerate = samplerate;
onSamplerateChanged(samplerate);
} }

View File

@ -1,153 +1,56 @@
#pragma once #pragma once
#include <string> #include <string>
#include <functional> #include <vector>
#include <map> #include <map>
#include <mutex>
#include <dsp/types.h>
#include <dsp/stream.h> #include <dsp/stream.h>
#include <dsp/types.h>
#include <utils/event.h> #include <utils/event.h>
enum TuningMode {
TUNING_MODE_NORMAL,
TUNING_MODE_PANADAPTER
};
class Source;
class SourceManager { class SourceManager {
friend Source;
public: public:
/** SourceManager();
* Register a source.
* @param name Name of the source.
* @param source Pointer to the source instance.
*/
void registerSource(const std::string& name, Source* source);
/** struct SourceHandler {
* Unregister a source. dsp::stream<dsp::complex_t>* stream;
* @param name Name of the source. void (*menuHandler)(void* ctx);
*/ void (*selectHandler)(void* ctx);
void unregisterSource(const std::string& name); void (*deselectHandler)(void* ctx);
void (*startHandler)(void* ctx);
void (*stopHandler)(void* ctx);
void (*tuneHandler)(double freq, void* ctx);
void* ctx;
};
/** enum TuningMode {
* Get a list of source names. NORMAL,
* @return List of source names. PANADAPTER
*/ };
const std::vector<std::string>& getSourceNames();
/** void registerSource(std::string name, SourceHandler* handler);
* Select a source. void unregisterSource(std::string name);
* @param name Name of the source. void selectSource(std::string name);
*/ void showSelectedMenu();
void select(const std::string& name); void start();
/**
* Get the name of the currently selected source.
* @return Name of the source or empty if no source is selected.
*/
const std::string& getSelected();
/**
* Start the radio.
* @return True if the radio started successfully, false if not.
*/
bool start();
/**
* Stop the radio.
*/
void stop(); void stop();
/**
* Check if the radio is running.
* @return True if the radio is running, false if not.
*/
bool isRunning();
/**
* Tune the radio.
* @param freq Frequency in Hz.
*/
void tune(double freq); void tune(double freq);
/**
* Tune the radio.
* @param freq Frequency to tune the radio to.
*/
void showMenu();
/**
* Get the current samplerate of the radio.
* @return Samplerate in Hz.
*/
double getSamplerate();
// =========== TODO: These functions should not happen in this class ===========
/**
* Set offset to add to the tuned frequency.
* @param offset Offset in Hz.
*/
void setTuningOffset(double offset); void setTuningOffset(double offset);
/**
* Set tuning mode.
* @param mode Tuning mode.
*/
void setTuningMode(TuningMode mode); void setTuningMode(TuningMode mode);
/**
* Set panadapter mode IF frequency.
* @param freq IF frequency in Hz.
*/
void setPanadpterIF(double freq); void setPanadpterIF(double freq);
// ============================================================================= std::vector<std::string> getSourceNames();
// Emitted after a new source has been registered.
Event<std::string> onSourceRegistered; Event<std::string> onSourceRegistered;
// Emitted when a source is about to be unregistered.
Event<std::string> onSourceUnregister; Event<std::string> onSourceUnregister;
// Emitted after a source has been unregistered.
Event<std::string> onSourceUnregistered; Event<std::string> onSourceUnregistered;
// Emitted when the samplerate of the incoming IQ has changed.
Event<double> onSamplerateChanged;
// Emitted when the source manager is instructed to tune the radio.
Event<double> onRetune; Event<double> onRetune;
private: private:
void deselect(); std::map<std::string, SourceHandler*> sources;
void setSamplerate(double samplerate); std::string selectedName;
SourceHandler* selectedHandler = NULL;
std::vector<std::string> sourceNames; double tuneOffset;
std::map<std::string, Source*> sources; double currentFreq;
double ifFreq = 0.0;
std::string selectedSourceName = ""; TuningMode tuneMode = TuningMode::NORMAL;
Source* selectedSource = NULL; dsp::stream<dsp::complex_t> nullSource;
bool running = false;
double samplerate = 1e6;
double frequency = 100e6;
double offset = 0;
double ifFrequency = 8.830e6;
TuningMode mode = TUNING_MODE_NORMAL;
std::recursive_mutex mtx;
};
class Source {
public:
virtual void showMenu() {}
virtual void select() = 0;
virtual void deselect() {}
virtual bool start() = 0;
virtual void stop() = 0;
virtual void tune(double freq) {}
dsp::stream<dsp::complex_t> stream;
}; };

View File

@ -1,51 +1,43 @@
#pragma once #pragma once
#include <functional> #include <vector>
#include <stdexcept> #include <utils/flog.h>
#include <mutex>
#include <map>
typedef int HandlerID; template <class T>
struct EventHandler {
EventHandler() {}
EventHandler(void (*handler)(T, void*), void* ctx) {
this->handler = handler;
this->ctx = ctx;
}
template <typename... Args> void (*handler)(T, void*);
void* ctx;
};
template <class T>
class Event { class Event {
using Handler = std::function<void(Args...)>;
public: public:
HandlerID bind(Handler handler) { Event() {}
std::lock_guard<std::mutex> lck(mtx); ~Event() {}
HandlerID id = genID();
handlers[id] = handler;
return id;
}
template<typename MHandler, class T> void emit(T value) {
HandlerID bind(MHandler handler, T* ctx) { for (auto const& handler : handlers) {
return bind([=](Args... args){ handler->handler(value, handler->ctx);
(ctx->*handler)(args...);
});
}
void unbind(HandlerID id) {
std::lock_guard<std::mutex> lck(mtx);
if (handlers.find(id) == handlers.end()) {
throw std::runtime_error("Could not unbind handler, unknown ID");
} }
handlers.erase(id);
} }
void operator()(Args... args) { void bindHandler(EventHandler<T>* handler) {
std::lock_guard<std::mutex> lck(mtx); handlers.push_back(handler);
for (const auto& [desc, handler] : handlers) { }
handler(args...);
void unbindHandler(EventHandler<T>* handler) {
if (std::find(handlers.begin(), handlers.end(), handler) == handlers.end()) {
flog::error("Tried to remove a non-existent event handler");
return;
} }
handlers.erase(std::remove(handlers.begin(), handlers.end(), handler), handlers.end());
} }
private: private:
HandlerID genID() { std::vector<EventHandler<T>*> handlers;
int id;
for (id = 1; handlers.find(id) != handlers.end(); id++);
return id;
}
std::map<HandlerID, Handler> handlers;
std::mutex mtx;
}; };

View File

@ -169,7 +169,7 @@ namespace flog {
fprintf(outStream, "] %s\n", out.c_str()); fprintf(outStream, "] %s\n", out.c_str());
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
// Print format string // Print format string
__android_log_buf_print(LOG_ID_DEFAULT, TYPE_PRIORITIES[type], FLOG_ANDROID_TAG, COLOR_WHITE "[%02d/%02d/%02d %02d:%02d:%02d.%03d] [%s%s" COLOR_WHITE "] %s\n", __android_log_print(TYPE_PRIORITIES[type], FLOG_ANDROID_TAG, COLOR_WHITE "[%02d/%02d/%02d %02d:%02d:%02d.%03d] [%s%s" COLOR_WHITE "] %s\n",
nowc->tm_mday, nowc->tm_mon + 1, nowc->tm_year + 1900, nowc->tm_hour, nowc->tm_min, nowc->tm_sec, 0, TYPE_COLORS[type], TYPE_STR[type], out.c_str()); nowc->tm_mday, nowc->tm_mon + 1, nowc->tm_year + 1900, nowc->tm_hour, nowc->tm_min, nowc->tm_sec, 0, TYPE_COLORS[type], TYPE_STR[type], out.c_str());
#else #else
// Print format string // Print format string

View File

@ -1,6 +1,7 @@
#include "net.h" #include "net.h"
#include <string.h> #include <string.h>
#include <codecvt> #include <codecvt>
#include <stdexcept>
#ifdef _WIN32 #ifdef _WIN32
#define WOULD_BLOCK (WSAGetLastError() == WSAEWOULDBLOCK) #define WOULD_BLOCK (WSAGetLastError() == WSAEWOULDBLOCK)
@ -288,6 +289,7 @@ namespace net {
// Save data // Save data
for (auto iface = addresses; iface; iface = iface->ifa_next) { for (auto iface = addresses; iface; iface = iface->ifa_next) {
if (!iface->ifa_addr || !iface->ifa_netmask) { continue; }
if (iface->ifa_addr->sa_family != AF_INET) { continue; } if (iface->ifa_addr->sa_family != AF_INET) { continue; }
InterfaceInfo info; InterfaceInfo info;
info.address = ntohl(*(uint32_t*)&iface->ifa_addr->sa_data[2]); info.address = ntohl(*(uint32_t*)&iface->ifa_addr->sa_data[2]);
@ -402,4 +404,4 @@ namespace net {
std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost, int lport) { std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost, int lport) {
return openudp(Address(rhost, rport), Address(lhost, lport)); return openudp(Address(rhost, rport), Address(lhost, lport));
} }
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <string>
#include <mutex> #include <mutex>
#include <memory> #include <memory>
#include <map> #include <map>

View File

@ -1,6 +1,7 @@
#include <utils/networking.h> #include <utils/networking.h>
#include <assert.h> #include <assert.h>
#include <utils/flog.h> #include <utils/flog.h>
#include <stdexcept>
namespace net { namespace net {

View File

@ -0,0 +1,52 @@
#pragma once
#include <functional>
#include <stdexcept>
#include <mutex>
#include <map>
typedef int HandlerID;
template <typename... Args>
class NewEvent {
public:
using Handler = std::function<void(Args...)>;
HandlerID bind(const Handler& handler) {
std::lock_guard<std::mutex> lck(mtx);
HandlerID id = genID();
handlers[id] = handler;
return id;
}
template<typename MHandler, class T>
HandlerID bind(MHandler handler, T* ctx) {
return bind([=](Args... args){
(ctx->*handler)(args...);
});
}
void unbind(HandlerID id) {
std::lock_guard<std::mutex> lck(mtx);
if (handlers.find(id) == handlers.end()) {
throw std::runtime_error("Could not unbind handler, unknown ID");
}
handlers.erase(id);
}
void operator()(Args... args) {
std::lock_guard<std::mutex> lck(mtx);
for (const auto& [desc, handler] : handlers) {
handler(args...);
}
}
private:
HandlerID genID() {
int id;
for (id = 1; handlers.find(id) != handlers.end(); id++);
return id;
}
std::map<HandlerID, Handler> handlers;
std::mutex mtx;
};

View File

@ -8,7 +8,7 @@ class OptionList {
public: public:
OptionList() { updateText(); } OptionList() { updateText(); }
void define(K key, std::string name, T value) { void define(const K& key, const std::string& name, const T& value) {
if (keyExists(key)) { throw std::runtime_error("Key already exists"); } if (keyExists(key)) { throw std::runtime_error("Key already exists"); }
if (nameExists(name)) { throw std::runtime_error("Name already exists"); } if (nameExists(name)) { throw std::runtime_error("Name already exists"); }
if (valueExists(value)) { throw std::runtime_error("Value already exists"); } if (valueExists(value)) { throw std::runtime_error("Value already exists"); }
@ -18,27 +18,27 @@ public:
updateText(); updateText();
} }
void define(std::string name, T value) { void define(const std::string& name, const T& value) {
define(name, name, value); define(name, name, value);
} }
void undefined(int id) { void undefine(int id) {
keys.erase(keys.begin() + id); keys.erase(keys.begin() + id);
names.erase(names.begin() + id); names.erase(names.begin() + id);
values.erase(values.begin() + id); values.erase(values.begin() + id);
updateText(); updateText();
} }
void undefineKey(K key) { void undefineKey(const K& key) {
undefined(keyId(key)); undefine(keyId(key));
} }
void undefineName(std::string name) { void undefineName(const std::string& name) {
undefined(nameId(name)); undefine(nameId(name));
} }
void undefineValue(T value) { void undefineValue(const T& value) {
undefined(valueId(value)); undefine(valueId(value));
} }
void clear() { void clear() {
@ -48,61 +48,61 @@ public:
updateText(); updateText();
} }
int size() { int size() const {
return keys.size(); return keys.size();
} }
bool empty() { bool empty() const {
return keys.empty(); return keys.empty();
} }
bool keyExists(K key) { bool keyExists(const K& key) const {
if (std::find(keys.begin(), keys.end(), key) != keys.end()) { return true; } if (std::find(keys.begin(), keys.end(), key) != keys.end()) { return true; }
return false; return false;
} }
bool nameExists(std::string name) { bool nameExists(const std::string& name) const {
if (std::find(names.begin(), names.end(), name) != names.end()) { return true; } if (std::find(names.begin(), names.end(), name) != names.end()) { return true; }
return false; return false;
} }
bool valueExists(T value) { bool valueExists(const T& value) const {
if (std::find(values.begin(), values.end(), value) != values.end()) { return true; } if (std::find(values.begin(), values.end(), value) != values.end()) { return true; }
return false; return false;
} }
int keyId(K key) { int keyId(const K& key) const {
auto it = std::find(keys.begin(), keys.end(), key); auto it = std::find(keys.begin(), keys.end(), key);
if (it == keys.end()) { throw std::runtime_error("Key doesn't exists"); } if (it == keys.end()) { throw std::runtime_error("Key doesn't exists"); }
return std::distance(keys.begin(), it); return std::distance(keys.begin(), it);
} }
int nameId(std::string name) { int nameId(const std::string& name) const {
auto it = std::find(names.begin(), names.end(), name); auto it = std::find(names.begin(), names.end(), name);
if (it == names.end()) { throw std::runtime_error("Name doesn't exists"); } if (it == names.end()) { throw std::runtime_error("Name doesn't exists"); }
return std::distance(names.begin(), it); return std::distance(names.begin(), it);
} }
int valueId(T value) { int valueId(const T& value) const {
auto it = std::find(values.begin(), values.end(), value); auto it = std::find(values.begin(), values.end(), value);
if (it == values.end()) { throw std::runtime_error("Value doesn't exists"); } if (it == values.end()) { throw std::runtime_error("Value doesn't exists"); }
return std::distance(values.begin(), it); return std::distance(values.begin(), it);
} }
K key(int id) { inline const K& key(int id) const {
return keys[id]; return keys[id];
} }
std::string name(int id) { inline const std::string& name(int id) const {
return names[id]; return names[id];
} }
T value(int id) { inline const T& value(int id) const {
return values[id]; return values[id];
} }
T operator[](int& id) { inline const T& operator[](int& id) const {
return value(id); return values[id];
} }
const char* txt = NULL; const char* txt = NULL;

View File

@ -91,9 +91,9 @@ namespace riff {
file.write((char*)&desc.hdr.size, sizeof(desc.hdr.size)); file.write((char*)&desc.hdr.size, sizeof(desc.hdr.size));
file.seekp(pos); file.seekp(pos);
// If parent chunk, increment its size // If parent chunk, increment its size by the size of the sub-chunk plus the size of its header)
if (!chunks.empty()) { if (!chunks.empty()) {
chunks.top().hdr.size += desc.hdr.size; chunks.top().hdr.size += desc.hdr.size + sizeof(ChunkHeader);
} }
} }

View File

@ -214,10 +214,10 @@ private:
if (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) { if (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) {
if (_this->showLines) { if (_this->showLines) {
_this->diag.lines.push_back(-0.75f); _this->diag.lines.push_back(-1.0);
_this->diag.lines.push_back(-0.25f); _this->diag.lines.push_back(-1.0/3.0);
_this->diag.lines.push_back(0.25f); _this->diag.lines.push_back(1.0/3.0);
_this->diag.lines.push_back(0.75f); _this->diag.lines.push_back(1.0);
} }
else { else {
_this->diag.lines.clear(); _this->diag.lines.clear();

View File

@ -57,10 +57,13 @@ public:
if (config.conf[name].contains("brokenModulation")) { if (config.conf[name].contains("brokenModulation")) {
brokenModulation = config.conf[name]["brokenModulation"]; brokenModulation = config.conf[name]["brokenModulation"];
} }
if (config.conf[name].contains("oqpsk")) {
oqpsk = config.conf[name]["oqpsk"];
}
config.release(); config.release();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 150000, INPUT_SAMPLE_RATE, 150000, 150000, true); vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, true);
demod.init(vfo->output, 72000.0f, INPUT_SAMPLE_RATE, 33, 0.6f, 0.1f, 0.005f, brokenModulation, 1e-6, 0.01); demod.init(vfo->output, 72000.0f, INPUT_SAMPLE_RATE, 33, 0.6f, 0.1f, 0.005f, brokenModulation, oqpsk, 1e-6, 0.01);
split.init(&demod.out); split.init(&demod.out);
split.bindStream(&symSinkStream); split.bindStream(&symSinkStream);
split.bindStream(&sinkStream); split.bindStream(&sinkStream);
@ -99,6 +102,7 @@ public:
double bw = gui::waterfall.getBandwidth(); double bw = gui::waterfall.getBandwidth();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), 150000, INPUT_SAMPLE_RATE, 150000, 150000, true); vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), 150000, INPUT_SAMPLE_RATE, 150000, 150000, true);
demod.setBrokenModulation(brokenModulation);
demod.setInput(vfo->output); demod.setInput(vfo->output);
demod.start(); demod.start();
@ -151,6 +155,13 @@ private:
config.release(true); config.release(true);
} }
if (ImGui::Checkbox(CONCAT("OQPSK##oqpsk", _this->name), &_this->oqpsk)) {
_this->demod.setOQPSK(_this->oqpsk);
config.acquire();
config.conf[_this->name]["oqpsk"] = _this->oqpsk;
config.release(true);
}
if (!_this->folderSelect.pathIsValid() && _this->enabled) { style::beginDisabled(); } if (!_this->folderSelect.pathIsValid() && _this->enabled) { style::beginDisabled(); }
if (_this->recording) { if (_this->recording) {
@ -245,7 +256,7 @@ private:
uint64_t dataWritten = 0; uint64_t dataWritten = 0;
std::ofstream recFile; std::ofstream recFile;
bool brokenModulation = false; bool brokenModulation = false;
bool oqpsk = false;
int8_t* writeBuffer; int8_t* writeBuffer;
}; };

View File

@ -11,8 +11,8 @@ namespace dsp::demod {
public: public:
Meteor() {} Meteor() {}
Meteor(stream<complex_t>* in, double symbolrate, double samplerate, int rrcTapCount, double rrcBeta, double agcRate, double costasBandwidth, bool brokenModulation, double omegaGain, double muGain, double omegaRelLimit = 0.01) { Meteor(stream<complex_t>* in, double symbolrate, double samplerate, int rrcTapCount, double rrcBeta, double agcRate, double costasBandwidth, bool brokenModulation, bool oqpsk, double omegaGain, double muGain, double omegaRelLimit = 0.01) {
init(in, symbolrate, samplerate, rrcTapCount, rrcBeta, agcRate, costasBandwidth, brokenModulation, omegaGain, muGain); init(in, symbolrate, samplerate, rrcTapCount, rrcBeta, agcRate, costasBandwidth, brokenModulation, oqpsk, omegaGain, muGain);
} }
~Meteor() { ~Meteor() {
@ -21,11 +21,12 @@ namespace dsp::demod {
taps::free(rrcTaps); taps::free(rrcTaps);
} }
void init(stream<complex_t>* in, double symbolrate, double samplerate, int rrcTapCount, double rrcBeta, double agcRate, double costasBandwidth, bool brokenModulation, double omegaGain, double muGain, double omegaRelLimit = 0.01) { void init(stream<complex_t>* in, double symbolrate, double samplerate, int rrcTapCount, double rrcBeta, double agcRate, double costasBandwidth, bool brokenModulation, bool oqpsk, double omegaGain, double muGain, double omegaRelLimit = 0.01) {
_symbolrate = symbolrate; _symbolrate = symbolrate;
_samplerate = samplerate; _samplerate = samplerate;
_rrcTapCount = rrcTapCount; _rrcTapCount = rrcTapCount;
_rrcBeta = rrcBeta; _rrcBeta = rrcBeta;
_oqpsk = oqpsk;
rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount, _rrcBeta, _symbolrate, _samplerate); rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount, _rrcBeta, _symbolrate, _samplerate);
rrc.init(NULL, rrcTaps); rrc.init(NULL, rrcTaps);
@ -129,6 +130,12 @@ namespace dsp::demod {
costas.setBrokenModulation(enabled); costas.setBrokenModulation(enabled);
} }
void setOQPSK(bool enabled) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_oqpsk = enabled;
}
void reset() { void reset() {
assert(base_type::_block_init); assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
@ -144,6 +151,18 @@ namespace dsp::demod {
rrc.process(count, in, out); rrc.process(count, in, out);
agc.process(count, out, out); agc.process(count, out, out);
costas.process(count, out, out); costas.process(count, out, out);
if (_oqpsk) {
// Single sample delay + deinterleave
for (int i = 0; i < count; i++) {
float tmp = out[i].im;
out[i].im = lastI;
lastI = tmp;
}
// TODO: Additional 1/24th sample delay
}
return recov.process(count, out, out); return recov.process(count, out, out);
} }
@ -166,6 +185,8 @@ namespace dsp::demod {
double _samplerate; double _samplerate;
int _rrcTapCount; int _rrcTapCount;
double _rrcBeta; double _rrcBeta;
float lastI = 0.0f;
bool _oqpsk = false;
tap<float> rrcTaps; tap<float> rrcTaps;
filter::FIR<complex_t, float> rrc; filter::FIR<complex_t, float> rrc;

View File

@ -19,15 +19,17 @@ namespace demod {
// Load config // Load config
_config->acquire(); _config->acquire();
bool modified = false;
if (config->conf[name][getName()].contains("lowPass")) { if (config->conf[name][getName()].contains("lowPass")) {
_lowPass = config->conf[name][getName()]["lowPass"]; _lowPass = config->conf[name][getName()]["lowPass"];
} }
_config->release(modified); if (config->conf[name][getName()].contains("highPass")) {
_highPass = config->conf[name][getName()]["highPass"];
}
_config->release();
// Define structure // Define structure
demod.init(input, getIFSampleRate(), bandwidth, _lowPass); demod.init(input, getIFSampleRate(), bandwidth, _lowPass, _highPass);
} }
void start() { demod.start(); } void start() { demod.start(); }
@ -41,6 +43,12 @@ namespace demod {
_config->conf[name][getName()]["lowPass"] = _lowPass; _config->conf[name][getName()]["lowPass"] = _lowPass;
_config->release(true); _config->release(true);
} }
if (ImGui::Checkbox(("High Pass##_radio_wfm_highpass_" + name).c_str(), &_highPass)) {
demod.setHighPass(_highPass);
_config->acquire();
_config->conf[name][getName()]["highPass"] = _highPass;
_config->release(true);
}
} }
void setBandwidth(double bandwidth) { void setBandwidth(double bandwidth) {
@ -75,6 +83,7 @@ namespace demod {
ConfigManager* _config = NULL; ConfigManager* _config = NULL;
bool _lowPass = true; bool _lowPass = true;
bool _highPass = false;
std::string name; std::string name;
}; };

View File

@ -0,0 +1,4 @@
FROM debian:bookworm
ENV DEBIAN_FRONTEND=noninteractive
COPY do_build.sh /root
RUN chmod +x /root/do_build.sh

View File

@ -0,0 +1,35 @@
#!/bin/bash
set -e
cd /root
# Install dependencies and tools
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
git clone https://github.com/Microtelecom/libperseus-sdr
cd libperseus-sdr
autoreconf -i
./configure
make
make install
ldconfig
cd ..
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
make VERBOSE=1 -j2
cd ..
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev, libzstd-dev'

View File

@ -6,7 +6,7 @@ cd /root
apt update apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries # Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
@ -15,10 +15,20 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus
git clone https://github.com/Microtelecom/libperseus-sdr
cd libperseus-sdr
autoreconf -i
./configure
make
make install
ldconfig
cd ..
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@ -6,7 +6,7 @@ cd /root
apt update apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries # Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
@ -15,10 +15,20 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus
git clone https://github.com/Microtelecom/libperseus-sdr
cd libperseus-sdr
autoreconf -i
./configure
make
make install
ldconfig
cd ..
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@ -4,9 +4,9 @@ cd /root
# Install dependencies and tools # Install dependencies and tools
apt update apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries # Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
@ -15,11 +15,21 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus
git clone https://github.com/Microtelecom/libperseus-sdr
cd libperseus-sdr
autoreconf -i
./configure
make
make install
ldconfig
cd ..
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev, libzstd-dev' sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'

View File

@ -12,7 +12,7 @@ apt update
# Install dependencies and tools # Install dependencies and tools
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libsoapysdr-dev libairspy-dev \ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libsoapysdr-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev libudev-dev libcodec2-dev libudev-dev autoconf libtool xxd
# Install SDRPlay libraries # Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
@ -41,6 +41,16 @@ make install
ldconfig ldconfig
cd ../../ cd ../../
# Install libperseus
git clone https://github.com/Microtelecom/libperseus-sdr
cd libperseus-sdr
autoreconf -i
./configure
make
make install
ldconfig
cd ..
# Fix missing .pc file for codec2 # Fix missing .pc file for codec2
echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc
echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc
@ -56,7 +66,7 @@ echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
# Generate package # Generate package

View File

@ -6,7 +6,7 @@ cd /root
apt update apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries # Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
@ -15,10 +15,20 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus
git clone https://github.com/Microtelecom/libperseus-sdr
cd libperseus-sdr
autoreconf -i
./configure
make
make install
ldconfig
cd ..
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@ -6,7 +6,7 @@ cd /root
apt update apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries # Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
@ -15,10 +15,20 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus
git clone https://github.com/Microtelecom/libperseus-sdr
cd libperseus-sdr
autoreconf -i
./configure
make
make install
ldconfig
cd ..
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@ -81,7 +81,7 @@ bundle_find_full_path() {
# Correct dep path # Correct dep path
echo $RPATH/$RPATH_NEXT echo $RPATH/$RPATH_NEXT
return return -1
done done
# Search other common paths # Search other common paths

View File

@ -38,6 +38,7 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfspace_source/rfspace_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfspace_source/rfspace_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_sdr_source/rtl_sdr_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_sdr_source/rtl_sdr_source.dylib
@ -62,6 +63,7 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_module
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/discord_integration/discord_integration.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/discord_integration/discord_integration.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/frequency_manager/frequency_manager.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/frequency_manager/frequency_manager.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/recorder/recorder.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/recorder/recorder.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/rigctl_client/rigctl_client.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/rigctl_server/rigctl_server.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/rigctl_server/rigctl_server.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/scanner/scanner.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/scanner/scanner.dylib

View File

@ -32,6 +32,9 @@ cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windo
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/ cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/ cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_windows_x64/modules/ cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/ cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/ cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/

View File

@ -5,4 +5,5 @@ file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE}) include(${SDRPP_MODULE_CMAKE})
target_include_directories(recorder PRIVATE "src/") target_include_directories(recorder PRIVATE "src/")
target_include_directories(recorder PRIVATE "../../decoder_modules/radio/src")

View File

@ -21,9 +21,11 @@
#include <core.h> #include <core.h>
#include <utils/optionlist.h> #include <utils/optionlist.h>
#include <utils/wav.h> #include <utils/wav.h>
#include <radio_interface.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
#define SILENCE_LVL 10e-20
#define SILENCE_LVL 10e-6
SDRPP_MOD_INFO{ SDRPP_MOD_INFO{
/* Name: */ "recorder", /* Name: */ "recorder",
@ -167,7 +169,7 @@ public:
// Open file // Open file
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband"; std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? gui::waterfall.selectedVFO : ""; std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
std::string extension = ".wav"; std::string extension = ".wav";
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension); std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
if (!writer.open(expandedPath)) { if (!writer.open(expandedPath)) {
@ -315,11 +317,11 @@ private:
} }
if (_this->recording) { style::endDisabled(); } if (_this->recording) { style::endDisabled(); }
// if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ignore_silence_", _this->name), &_this->ignoreSilence)) { if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ignore_silence_", _this->name), &_this->ignoreSilence)) {
// config.acquire(); config.acquire();
// config.conf[_this->name]["ignoreSilence"] = _this->ignoreSilence; config.conf[_this->name]["ignoreSilence"] = _this->ignoreSilence;
// config.release(true); config.release(true);
// } }
} }
// Record button // Record button
@ -338,7 +340,13 @@ private:
uint64_t seconds = _this->writer.getSamplesWritten() / _this->samplerate; uint64_t seconds = _this->writer.getSamplesWritten() / _this->samplerate;
time_t diff = seconds; time_t diff = seconds;
tm* dtm = gmtime(&diff); tm* dtm = gmtime(&diff);
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
if (_this->ignoreSilence && _this->ignoringSilence) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Paused %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
}
else {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
}
} }
} }
@ -430,6 +438,17 @@ private:
if (dbLvl.r > lvl.r) { lvl.r = dbLvl.r; } if (dbLvl.r > lvl.r) { lvl.r = dbLvl.r; }
} }
std::map<int, const char*> radioModeToString = {
{ RADIO_IFACE_MODE_NFM, "NFM" },
{ RADIO_IFACE_MODE_WFM, "WFM" },
{ RADIO_IFACE_MODE_AM, "AM" },
{ RADIO_IFACE_MODE_DSB, "DSB" },
{ RADIO_IFACE_MODE_USB, "USB" },
{ RADIO_IFACE_MODE_CW, "CW" },
{ RADIO_IFACE_MODE_LSB, "LSB" },
{ RADIO_IFACE_MODE_RAW, "RAW" }
};
std::string genFileName(std::string templ, std::string type, std::string name) { std::string genFileName(std::string templ, std::string type, std::string name) {
// Get data // Get data
time_t now = time(0); time_t now = time(0);
@ -448,6 +467,7 @@ private:
char dayStr[128]; char dayStr[128];
char monStr[128]; char monStr[128];
char yearStr[128]; char yearStr[128];
const char* modeStr = "Unknown";
sprintf(freqStr, "%.0lfHz", freq); sprintf(freqStr, "%.0lfHz", freq);
sprintf(hourStr, "%02d", ltm->tm_hour); sprintf(hourStr, "%02d", ltm->tm_hour);
sprintf(minStr, "%02d", ltm->tm_min); sprintf(minStr, "%02d", ltm->tm_min);
@ -455,6 +475,11 @@ private:
sprintf(dayStr, "%02d", ltm->tm_mday); sprintf(dayStr, "%02d", ltm->tm_mday);
sprintf(monStr, "%02d", ltm->tm_mon + 1); sprintf(monStr, "%02d", ltm->tm_mon + 1);
sprintf(yearStr, "%02d", ltm->tm_year + 1900); sprintf(yearStr, "%02d", ltm->tm_year + 1900);
if (core::modComManager.getModuleName(name) == "radio") {
int mode;
core::modComManager.callInterface(name, RADIO_IFACE_CMD_GET_MODE, NULL, &mode);
modeStr = radioModeToString[mode];
}
// Replace in template // Replace in template
templ = std::regex_replace(templ, std::regex("\\$t"), type); templ = std::regex_replace(templ, std::regex("\\$t"), type);
@ -465,6 +490,7 @@ private:
templ = std::regex_replace(templ, std::regex("\\$d"), dayStr); templ = std::regex_replace(templ, std::regex("\\$d"), dayStr);
templ = std::regex_replace(templ, std::regex("\\$M"), monStr); templ = std::regex_replace(templ, std::regex("\\$M"), monStr);
templ = std::regex_replace(templ, std::regex("\\$y"), yearStr); templ = std::regex_replace(templ, std::regex("\\$y"), yearStr);
templ = std::regex_replace(templ, std::regex("\\$r"), modeStr);
return templ; return templ;
} }
@ -480,13 +506,31 @@ private:
static void stereoHandler(dsp::stereo_t* data, int count, void* ctx) { static void stereoHandler(dsp::stereo_t* data, int count, void* ctx) {
RecorderModule* _this = (RecorderModule*)ctx; RecorderModule* _this = (RecorderModule*)ctx;
// TODO: Ignore silence if (_this->ignoreSilence) {
float absMax = 0.0f;
float* _data = (float*)data;
int _count = count * 2;
for (int i = 0; i < _count; i++) {
float val = fabsf(_data[i]);
if (val > absMax) { absMax = val; }
}
_this->ignoringSilence = (absMax < SILENCE_LVL);
if (_this->ignoringSilence) { return; }
}
_this->writer.write((float*)data, count); _this->writer.write((float*)data, count);
} }
static void monoHandler(float* data, int count, void* ctx) { static void monoHandler(float* data, int count, void* ctx) {
RecorderModule* _this = (RecorderModule*)ctx; RecorderModule* _this = (RecorderModule*)ctx;
// TODO: Ignore silence if (_this->ignoreSilence) {
float absMax = 0.0f;
for (int i = 0; i < count; i++) {
float val = fabsf(data[i]);
if (val > absMax) { absMax = val; }
}
_this->ignoringSilence = (absMax < SILENCE_LVL);
if (_this->ignoringSilence) { return; }
}
_this->writer.write(data, count); _this->writer.write(data, count);
} }
@ -529,6 +573,7 @@ private:
dsp::stereo_t audioLvl = { -100.0f, -100.0f }; dsp::stereo_t audioLvl = { -100.0f, -100.0f };
bool recording = false; bool recording = false;
bool ignoringSilence = false;
wav::Writer writer; wav::Writer writer;
std::recursive_mutex recMtx; std::recursive_mutex recMtx;
dsp::stream<dsp::complex_t>* basebandStream; dsp::stream<dsp::complex_t>* basebandStream;

View File

@ -45,6 +45,9 @@ public:
} }
config.release(); config.release();
_retuneHandler.ctx = this;
_retuneHandler.handler = retuneHandler;
gui::menu.registerEntry(name, menuHandler, this, NULL); gui::menu.registerEntry(name, menuHandler, this, NULL);
} }
@ -84,8 +87,8 @@ public:
// Switch source to panadapter mode // Switch source to panadapter mode
sigpath::sourceManager.setPanadpterIF(ifFreq); sigpath::sourceManager.setPanadpterIF(ifFreq);
sigpath::sourceManager.setTuningMode(TUNING_MODE_PANADAPTER); sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER);
retuneHandlerId = sigpath::sourceManager.onRetune.bind(retuneHandler, this); sigpath::sourceManager.onRetune.bindHandler(&_retuneHandler);
running = true; running = true;
} }
@ -95,8 +98,8 @@ public:
if (!running) { return; } if (!running) { return; }
// Switch source back to normal mode // Switch source back to normal mode
sigpath::sourceManager.onRetune.unbind(retuneHandlerId); sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler);
sigpath::sourceManager.setTuningMode(TUNING_MODE_NORMAL); sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL);
// Disconnect from rigctl server // Disconnect from rigctl server
client->close(); client->close();
@ -156,9 +159,10 @@ private:
} }
} }
void retuneHandler(double freq) { static void retuneHandler(double freq, void* ctx) {
if (!client || !client->isOpen()) { return; } RigctlClientModule* _this = (RigctlClientModule*)ctx;
if (client->setFreq(freq)) { if (!_this->client || !_this->client->isOpen()) { return; }
if (_this->client->setFreq(freq)) {
flog::error("Could not set frequency"); flog::error("Could not set frequency");
} }
} }
@ -174,7 +178,7 @@ private:
double ifFreq = 8830000.0; double ifFreq = 8830000.0;
HandlerID retuneHandlerId; EventHandler<double> _retuneHandler;
}; };
MOD_EXPORT void _INIT_() { MOD_EXPORT void _INIT_() {

View File

@ -333,6 +333,17 @@ private:
_this->client->readAsync(1024, _this->dataBuf, dataHandler, _this, false); _this->client->readAsync(1024, _this->dataBuf, dataHandler, _this, false);
} }
std::map<int, const char*> radioModeToString = {
{ RADIO_IFACE_MODE_NFM, "NFM" },
{ RADIO_IFACE_MODE_WFM, "WFM" },
{ RADIO_IFACE_MODE_AM, "AM" },
{ RADIO_IFACE_MODE_DSB, "DSB" },
{ RADIO_IFACE_MODE_USB, "USB" },
{ RADIO_IFACE_MODE_CW, "CW" },
{ RADIO_IFACE_MODE_LSB, "LSB" },
{ RADIO_IFACE_MODE_RAW, "RAW" }
};
void commandHandler(std::string cmd) { void commandHandler(std::string cmd) {
std::string corr = ""; std::string corr = "";
std::vector<std::string> parts; std::vector<std::string> parts;
@ -442,38 +453,18 @@ private:
pos++; pos++;
} }
const std::string& newModeStr = parts[1];
float newBandwidth = std::atoi(parts[2].c_str()); float newBandwidth = std::atoi(parts[2].c_str());
int newMode; auto it = std::find_if(radioModeToString.begin(), radioModeToString.end(), [&newModeStr](const auto& e) {
if (parts[1] == "FM") { return e.second == newModeStr;
newMode = RADIO_IFACE_MODE_NFM; });
} if (it == radioModeToString.end()) {
else if (parts[1] == "WFM") {
newMode = RADIO_IFACE_MODE_WFM;
}
else if (parts[1] == "AM") {
newMode = RADIO_IFACE_MODE_AM;
}
else if (parts[1] == "DSB") {
newMode = RADIO_IFACE_MODE_DSB;
}
else if (parts[1] == "USB") {
newMode = RADIO_IFACE_MODE_USB;
}
else if (parts[1] == "CW") {
newMode = RADIO_IFACE_MODE_CW;
}
else if (parts[1] == "LSB") {
newMode = RADIO_IFACE_MODE_LSB;
}
else if (parts[1] == "RAW") {
newMode = RADIO_IFACE_MODE_RAW;
}
else {
resp = "RPRT 1\n"; resp = "RPRT 1\n";
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
return; return;
} }
int newMode = it->first;
// If tuning is enabled, set the mode and optionally the bandwidth // If tuning is enabled, set the mode and optionally the bandwidth
if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio" && tuningEnabled) { if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio" && tuningEnabled) {
@ -492,31 +483,9 @@ private:
if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio") { if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio") {
int mode; int mode;
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_GET_MODE, NULL, &mode); core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_GET_MODE, NULL, &mode);
resp = std::string(radioModeToString[mode]) + "\n";
if (mode == RADIO_IFACE_MODE_NFM) {
resp = "FM\n";
}
else if (mode == RADIO_IFACE_MODE_WFM) {
resp = "WFM\n";
}
else if (mode == RADIO_IFACE_MODE_AM) {
resp = "AM\n";
}
else if (mode == RADIO_IFACE_MODE_DSB) {
resp = "DSB\n";
}
else if (mode == RADIO_IFACE_MODE_USB) {
resp = "USB\n";
}
else if (mode == RADIO_IFACE_MODE_CW) {
resp = "CW\n";
}
else if (mode == RADIO_IFACE_MODE_LSB) {
resp = "LSB\n";
}
} }
else if (!selectedVfo.empty()) {
if (!selectedVfo.empty()) {
resp += std::to_string((int)sigpath::vfoManager.getBandwidth(selectedVfo)) + "\n"; resp += std::to_string((int)sigpath::vfoManager.getBandwidth(selectedVfo)) + "\n";
} }
else { else {
@ -690,6 +659,11 @@ private:
"0\n" /* RIG_PARM_NONE */; "0\n" /* RIG_PARM_NONE */;
client->write(resp.size(), (uint8_t*)resp.c_str()); client->write(resp.size(), (uint8_t*)resp.c_str());
} }
// This get_powerstat stuff is a wordaround for WSJT-X 2.7.0
else if (parts[0] == "\\get_powerstat") {
resp = "1\n";
client->write(resp.size(), (uint8_t*)resp.c_str());
}
else { else {
// If command is not recognized, return error // If command is not recognized, return error
flog::error("Rigctl client sent invalid command: '{0}'", cmd); flog::error("Rigctl client sent invalid command: '{0}'", cmd);

View File

@ -44,7 +44,7 @@ Download the latest release from [the Releases page](https://github.com/Alexandr
Then, run: Then, run:
```sh ```sh
sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev
sudo dpkg -i sdrpp_debian_amd64.deb sudo dpkg -i sdrpp_debian_amd64.deb
``` ```
@ -52,7 +52,9 @@ If `libvolk2-dev` is not available, use `libvolk1-dev`.
### Arch-based ### Arch-based
Install the latest release from the [sdrpp-git](https://aur.archlinux.org/packages/sdrpp-git/) AUR package Install from source following the instructions below.
**WARNING: The sdrpp-git AUR package is no longer official, it is not recommended to use it.**
### Other ### Other
@ -74,7 +76,7 @@ The preferred IDE is [VS Code](https://code.visualstudio.com/) in order to have
* [cmake](https://cmake.org) * [cmake](https://cmake.org)
* [vcpkg](https://vcpkg.io) * [vcpkg](https://vcpkg.io)
* [PothosSDR](https://github.com/pothosware/PothosSDR) (This will install libraries for most SDRs) * [PothosSDR](https://github.com/pothosware/PothosSDR) (This will install libraries for most SDRs. You have to install it in `C:/Program Files/PothosSDR`)
* [RtAudio](https://www.music.mcgill.ca/~gary/rtaudio/) (You have to build and install it in `C:/Program Files (x86)/RtAudio/`) * [RtAudio](https://www.music.mcgill.ca/~gary/rtaudio/) (You have to build and install it in `C:/Program Files (x86)/RtAudio/`)
After this, install the following dependencies using vcpkg: After this, install the following dependencies using vcpkg:
@ -113,16 +115,16 @@ You will next need to edit the `root_dev/config.json` file to point to the modul
From the top directory, you can simply run: From the top directory, you can simply run:
```bat ```bat
./build/Release/sdrpp.exe -r root_dev -s ./build/Release/sdrpp.exe -r root_dev -c
``` ```
Or, if you wish to run from the build directory e.g. `build/Release` and adapt the relative path to the `root_dev` folder: Or, if you wish to run from the build directory e.g. `build/Release` and adapt the relative path to the `root_dev` folder:
```bat ```bat
./sdrpp.exe -r ../../root_dev -s ./sdrpp.exe -r ../../root_dev -c
``` ```
The optional `-s` argument is for keeping the console active in order to see the error messages. The optional `-c` argument is for keeping the console active in order to see the error messages.
Because all the paths are relative, for the rest of the command line instructions we are going to assume you are running from the top directory using the former command. Because all the paths are relative, for the rest of the command line instructions we are going to assume you are running from the top directory using the former command.
As mentioned previously you need to edit `root_dev/config.json` to add the modules that were built. From the default configuration file you need to add the paths in the `modules` section. Add to this list all the modules you wish to use. As mentioned previously you need to edit `root_dev/config.json` to add the modules that were built. From the default configuration file you need to add the paths in the `modules` section. Add to this list all the modules you wish to use.
@ -302,7 +304,7 @@ Here is an example of build commands that will build almost all modules at the t
```sh ```sh
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release cmake .. -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
make -j<N> make -j<N>
``` ```
@ -327,11 +329,12 @@ Modules in beta are still included in releases for the most part but not enabled
|----------------------|------------|-------------------|--------------------------------|:---------------:|:-----------------------:|:---------------------------:| |----------------------|------------|-------------------|--------------------------------|:---------------:|:-----------------------:|:---------------------------:|
| airspy_source | Working | libairspy | OPT_BUILD_AIRSPY_SOURCE | ✅ | ✅ | ✅ | | airspy_source | Working | libairspy | OPT_BUILD_AIRSPY_SOURCE | ✅ | ✅ | ✅ |
| airspyhf_source | Working | libairspyhf | OPT_BUILD_AIRSPYHF_SOURCE | ✅ | ✅ | ✅ | | airspyhf_source | Working | libairspyhf | OPT_BUILD_AIRSPYHF_SOURCE | ✅ | ✅ | ✅ |
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ⚠️ (not Debian Buster) | ✅ | | bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | (not Debian Buster) | ✅ |
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ | | file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ | | hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | | | hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | |
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ | | limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ⛔ | ⛔ |
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ | | plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
| rfspace_source | Working | - | OPT_BUILD_RFSPACE_SOURCE | ✅ | ✅ | ✅ | | rfspace_source | Working | - | OPT_BUILD_RFSPACE_SOURCE | ✅ | ✅ | ✅ |
| rtl_sdr_source | Working | librtlsdr | OPT_BUILD_RTL_SDR_SOURCE | ✅ | ✅ | ✅ | | rtl_sdr_source | Working | librtlsdr | OPT_BUILD_RTL_SDR_SOURCE | ✅ | ✅ | ✅ |
@ -348,7 +351,7 @@ Modules in beta are still included in releases for the most part but not enabled
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default | | Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|--------------------|------------|--------------|------------------------------|:---------------:|:----------------:|:---------------------------:| |--------------------|------------|--------------|------------------------------|:---------------:|:----------------:|:---------------------------:|
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | | | android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ | | audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ | | network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
| new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ | | new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
@ -374,9 +377,9 @@ Modules in beta are still included in releases for the most part but not enabled
| discord_integration | Working | - | OPT_BUILD_DISCORD_PRESENCE | ✅ | ✅ | ⛔ | | discord_integration | Working | - | OPT_BUILD_DISCORD_PRESENCE | ✅ | ✅ | ⛔ |
| frequency_manager | Working | - | OPT_BUILD_FREQUENCY_MANAGER | ✅ | ✅ | ✅ | | frequency_manager | Working | - | OPT_BUILD_FREQUENCY_MANAGER | ✅ | ✅ | ✅ |
| recorder | Working | - | OPT_BUILD_RECORDER | ✅ | ✅ | ✅ | | recorder | Working | - | OPT_BUILD_RECORDER | ✅ | ✅ | ✅ |
| rigctl_client | Unfinished | - | OPT_BUILD_RIGCTL_CLIENT | | | ⛔ | | rigctl_client | Unfinished | - | OPT_BUILD_RIGCTL_CLIENT | | | ⛔ |
| rigctl_server | Working | - | OPT_BUILD_RIGCTL_SERVER | ✅ | ✅ | ✅ | | rigctl_server | Working | - | OPT_BUILD_RIGCTL_SERVER | ✅ | ✅ | ✅ |
| scanner | Beta | - | OPT_BUILD_SCANNER | ✅ | ✅ | | | scanner | Beta | - | OPT_BUILD_SCANNER | ✅ | ✅ | |
| scheduler | Unfinished | - | OPT_BUILD_SCHEDULER | ⛔ | ⛔ | ⛔ | | scheduler | Unfinished | - | OPT_BUILD_SCHEDULER | ⛔ | ⛔ | ⛔ |
# Troubleshooting # Troubleshooting
@ -429,25 +432,34 @@ I will soon publish a contributing.md listing the code style to use.
* Dale L Puckett (K0HYD) * Dale L Puckett (K0HYD)
* [Daniele D'Agnelli](https://linkedin.com/in/dagnelli) * [Daniele D'Agnelli](https://linkedin.com/in/dagnelli)
* D. Jones * D. Jones
* Dexruus
* [EB3FRN](https://www.eb3frn.net/) * [EB3FRN](https://www.eb3frn.net/)
* Eric Johnson * Eric Johnson
* Ernest Murphy (NH7L) * Ernest Murphy (NH7L)
* Flinger Films * Flinger Films
* [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ)
* gringogrigio * gringogrigio
* Jeff Moe
* Joe Cupano * Joe Cupano
* KD1SQ
* Kezza * Kezza
* Krys Kamieniecki * Krys Kamieniecki
* Lee Donaghy * Lee Donaghy
* Lee KD1SQ * Lee KD1SQ
* .lozenge. (Hank Hill) * .lozenge. (Hank Hill)
* Martin Herren (HB9FXX)
* ON4MU * ON4MU
* [Passion-Radio.com](https://passion-radio.com/) * [Passion-Radio.com](https://passion-radio.com/)
* Paul Maine * Paul Maine
* Peter Betz
* [Scanner School](https://scannerschool.com/) * [Scanner School](https://scannerschool.com/)
* Scott Palmer
* [SignalsEverywhere](https://signalseverywhere.com/) * [SignalsEverywhere](https://signalseverywhere.com/)
* Syne Ardwin (WI9SYN) * Syne Ardwin (WI9SYN)
* [W4IPA](https://twitter.com/W4IPAstroke5) * [W4IPA](https://twitter.com/W4IPAstroke5)
* [Zipper](github.com/reppiZ) * William Arcand (W1WRA)
* [Yves Rougy](https://www.twitch.tv/yorzian)
* [Zipper](https://github.com/reppiZ)
## Contributors ## Contributors
@ -461,6 +473,7 @@ I will soon publish a contributing.md listing the code style to use.
* [Howard0su](https://github.com/howard0su) * [Howard0su](https://github.com/howard0su)
* John Donkersley * John Donkersley
* [Joshua Kimsey](https://github.com/JoshuaKimsey) * [Joshua Kimsey](https://github.com/JoshuaKimsey)
* [Manawyrm](https://github.com/Manawyrm)
* [Martin Hauke](https://github.com/mnhauke) * [Martin Hauke](https://github.com/mnhauke)
* [Marvin Sinister](https://github.com/marvin-sinister) * [Marvin Sinister](https://github.com/marvin-sinister)
* [Maxime Biette](https://github.com/mbiette) * [Maxime Biette](https://github.com/mbiette)
@ -470,7 +483,6 @@ I will soon publish a contributing.md listing the code style to use.
* [Shuyuan Liu](https://github.com/shuyuan-liu) * [Shuyuan Liu](https://github.com/shuyuan-liu)
* [Syne Ardwin (WI9SYN)](https://esaille.me/) * [Syne Ardwin (WI9SYN)](https://esaille.me/)
* [Szymon Zakrent](https://github.com/zakrent) * [Szymon Zakrent](https://github.com/zakrent)
* [Tobias Mädel](https://github.com/Manawyrm)
* Youssef Touil * Youssef Touil
* [Zimm](https://github.com/invader-zimm) * [Zimm](https://github.com/invader-zimm)

View File

@ -0,0 +1,280 @@
{
"name": "Belgium",
"country_name": "Belgium",
"country_code": "BE",
"author_name": "Bastien Cabay - ON4BCY",
"author_url": "https://qrz.com/db/ON4BCY",
"bands": [
{
"name": "2200m - Amateur",
"type": "amateur",
"start": 135700,
"end": 137800
},
{
"name": "630m - Amateur",
"type": "amateur",
"start": 472000,
"end": 479000
},
{
"name": "600m - Amateur",
"type": "amateur",
"start": 501000,
"end": 504000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 526500,
"end": 1606500
},
{
"name": "160m - Amateur",
"type": "amateur",
"start": 1810000,
"end": 2000000
},
{
"name": "80m - Amateur",
"type": "amateur",
"start": 3500000,
"end": 3800000
},
{
"name": "60m - Amateur",
"type": "amateur",
"start": 5351500,
"end": 5366500
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 5950000,
"end": 6200000
},
{
"name": "40m - Amateur",
"type": "amateur",
"start": 7000000,
"end": 7200000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 7200000,
"end": 7300000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 9500000,
"end": 9900000
},
{
"name": "30m - Amateur",
"type": "amateur",
"start": 10100000,
"end": 10150000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 11650000,
"end": 12050000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 13600000,
"end": 13800000
},
{
"name": "20m - Amateur",
"type": "amateur",
"start": 14000000,
"end": 14350000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 15100000,
"end": 15600000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 17550000,
"end": 17900000
},
{
"name": "17m - Amateur",
"type": "amateur",
"start": 18068000,
"end": 18168000
},
{
"name": "15m - Amateur",
"type": "amateur",
"start": 21000000,
"end": 21450000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 21450000,
"end": 21850000
},
{
"name": "12m - Amateur",
"type": "amateur",
"start": 24890000,
"end": 24990000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 25670000,
"end": 26100000
},
{
"name": "11m - Citizen Band",
"type": "amateur",
"start": 26960000,
"end": 27410000
},
{
"name": "10m - Amateur",
"type": "amateur",
"start": 28000000,
"end": 29700000
},
{
"name": "8m - Amateur",
"type": "amateur",
"start": 40660000,
"end": 40690000
},
{
"name": "6m - Amateur",
"type": "amateur",
"start": 50000000,
"end": 52000000
},
{
"name": "4m - Amateur",
"type": "amateur",
"start": 69945000,
"end": 69955000
},
{
"name": "4m - Amateur",
"type": "amateur",
"start": 70190000,
"end": 70412500
},
{
"name": "FM Broadcast",
"type": "broadcast",
"start": 87500000,
"end": 108000000
},
{
"name": "Space Exploration / Meteorology Sat. / S-PCS",
"type": "satellite",
"start": 137000000,
"end": 138000000
},
{
"name": "2m - Amateur",
"type": "amateur",
"start": 144000000,
"end": 146000000
},
{
"name": "T-DAB Broadcast",
"type": "broadcast",
"start": 174000000,
"end": 223000000
},
{
"name": "70cm - Amateur",
"type": "amateur",
"start": 430000000,
"end": 440000000
},
{
"name": "PMR446",
"type": "amateur",
"start": 446000000,
"end": 446200000
},
{
"name": "DVB-T - Broadcast",
"type": "broadcast",
"start": 470000000,
"end": 790000000
},
{
"name": "23cm - Amateur",
"type": "amateur",
"start": 1240000000,
"end": 1300000000
},
{
"name": "13cm - Amateur",
"type": "amateur",
"start": 2300000000,
"end": 2450000000
},
{
"name": "6cm - Amateur",
"type": "amateur",
"start": 5650000000,
"end": 5850000000
},
{
"name": "3cm - Amateur",
"type": "amateur",
"start": 10000000000,
"end": 10500000000
},
{
"name": "1.25cm - Amateur",
"type": "amateur",
"start": 24000000000,
"end": 24250000000
},
{
"name": "6mm - Amateur",
"type": "amateur",
"start": 47000000000,
"end": 47200000000
},
{
"name": "4mm - Amateur",
"type": "amateur",
"start": 75500000000,
"end": 81000000000
},
{
"name": "2.5mm - Amateur",
"type": "amateur",
"start": 122250000000,
"end": 123000000000
},
{
"name": "2mm - Amateur",
"type": "amateur",
"start": 142000000000,
"end": 149000000000
},
{
"name": "1mm - Amateur",
"type": "amateur",
"start": 241000000000,
"end": 250000000000
}
]
}

View File

@ -115,7 +115,7 @@
"end": 4995000 "end": 4995000
}, },
{ {
"name": "60m - radiodiffusion", "name": "60m - radiodiffusion",
"type": "broadcast", "type": "broadcast",
"start": 5005000, "start": 5005000,
"end": 5060000 "end": 5060000
@ -340,7 +340,7 @@
"name": "11m - CB", "name": "11m - CB",
"type": "amateur", "type": "amateur",
"start": 26960000, "start": 26960000,
"end": 27230000 "end": 27410000
}, },
{ {
"name": "10m - Radioamateur", "name": "10m - Radioamateur",
@ -493,4 +493,4 @@
"end": 250000000000 "end": 250000000000
} }
] ]
} }

View File

@ -2,7 +2,7 @@
"name": "Germany", "name": "Germany",
"country_name": "Germany", "country_name": "Germany",
"country_code": "DE", "country_code": "DE",
"author_name": "Tobias Mädel", "author_name": "Manawyrm",
"author_url": "https://tbspace.de", "author_url": "https://tbspace.de",
"bands": [ "bands": [
{ {

File diff suppressed because it is too large Load Diff

View File

@ -529,9 +529,9 @@
}, },
{ {
"name": "Train communications", "name": "Train communications",
"type": "aviation", "type": "railway",
"start": 151775000, "start": 151712500,
"end": 151875000 "end": 156012500
}, },
{ {
"name": "Marine", "name": "Marine",
@ -557,12 +557,6 @@
"start": 270000000, "start": 270000000,
"end": 380000000 "end": 380000000
}, },
{
"name": "Train communications",
"type": "aviation",
"start": 299999000,
"end": 300001000
},
{ {
"name": "70cm", "name": "70cm",
"type": "amateur", "type": "amateur",

View File

@ -5,6 +5,12 @@
"author_name": "John Donkersley", "author_name": "John Donkersley",
"author_url": "", "author_url": "",
"bands": [ "bands": [
{
"name": "2200m Ham Band",
"type": "amateur",
"start": 135700,
"end": 137800
},
{ {
"name": "Long Wave", "name": "Long Wave",
"type": "broadcast", "type": "broadcast",
@ -209,18 +215,18 @@
"start": 15010000, "start": 15010000,
"end": 15100000 "end": 15100000
}, },
{
"name": "Maritime",
"type": "marine",
"start": 16360000,
"end": 17410000
},
{ {
"name": "19m Broadcast", "name": "19m Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 15100000, "start": 15100000,
"end": 15800000 "end": 15800000
}, },
{
"name": "Maritime",
"type": "marine",
"start": 16360000,
"end": 17410000
},
{ {
"name": "16m Broadcast", "name": "16m Broadcast",
"type": "broadcast", "type": "broadcast",
@ -345,7 +351,7 @@
"name": "Air Band TACAN/ILS", "name": "Air Band TACAN/ILS",
"type": "aviation", "type": "aviation",
"start": 108000000, "start": 108000000,
"end": 118000000 "end": 117975000
}, },
{ {
"name": "Air Band Voice", "name": "Air Band Voice",
@ -371,6 +377,12 @@
"start": 147343750, "start": 147343750,
"end": 147500000 "end": 147500000
}, },
{
"name": "Satellites",
"type": "satellite",
"start": 148000000,
"end": 150050000
},
{ {
"name": "Pagers - Flex/POCSAG", "name": "Pagers - Flex/POCSAG",
"type": "PMR", "type": "PMR",
@ -425,6 +437,18 @@
"start": 230000000, "start": 230000000,
"end": 400000000 "end": 400000000
}, },
{
"name": "Satellites",
"type": "satellite",
"start": 399900000,
"end": 401000000
},
{
"name": "Weather Balloons",
"type": "aviation",
"start": 401000000,
"end": 406000000
},
{ {
"name": "Private Mobile Radio inc trams", "name": "Private Mobile Radio inc trams",
"type": "PMR", "type": "PMR",
@ -462,22 +486,34 @@
"end": 455000000 "end": 455000000
}, },
{ {
"name": "Private Mobile Radio", "name": "Private Mobile Radio inc OB",
"type": "PMR", "type": "PMR",
"start": 455000000, "start": 455000000,
"end": 467200000 "end": 470000000
},
{
"name": "Outside Broadcast Talkback",
"type": "PMR",
"start": 467200000,
"end": 468600000
}, },
{ {
"name": "Digital TV Broadcast", "name": "Digital TV Broadcast",
"type": "broadcast", "type": "broadcast",
"start": 470000000, "start": 470000000,
"end": 790000000 "end": 700000000
},
{
"name": "Cell phones",
"type": "cellular",
"start": 703000000,
"end": 788000000
},
{
"name": "Band 20 Cell phone downlink",
"type": "cellular",
"start": 791000000,
"end": 821000000
},
{
"name": "Band 20 Cell phone uplink",
"type": "cellular",
"start": 832000000,
"end": 862000000
}, },
{ {
"name": "Licence Exempt Short Range", "name": "Licence Exempt Short Range",
@ -485,12 +521,84 @@
"start": 862000000, "start": 862000000,
"end": 875800000 "end": 875800000
}, },
{
"name": "Band 8 Cell phone uplink",
"type": "cellular",
"start": 880100000,
"end": 914900000
},
{
"name": "Band 8 Cell phone downlink",
"type": "cellular",
"start": 925100000,
"end": 929500000
},
{ {
"name": "23cm Ham Band", "name": "23cm Ham Band",
"type": "amateur", "type": "amateur",
"start": 1240000000, "start": 1240000000,
"end": 1325000000 "end": 1325000000
}, },
{
"name": "Band 32 Cell phone",
"type": "cellular",
"start": 1452000000,
"end": 1492000000
},
{
"name": "Satellite L-band",
"type": "satellite",
"start": 1518000000,
"end": 1559000000
},
{
"name": "Satellite L-band",
"type": "satellite",
"start": 1626500000,
"end": 1660500000
},
{
"name": "Satellite L-band",
"type": "satellite",
"start": 1668000000,
"end": 1675000000
},
{
"name": "Band 3 Cell phone uplink",
"type": "cellular",
"start": 1710000000,
"end": 1785000000
},
{
"name": "Band 3 Cell phone downlink",
"type": "cellular",
"start": 1805100000,
"end": 1880000000
},
{
"name": "DECT cordless phones",
"type": "cellular",
"start": 1880000000,
"end": 1900000000
},
{
"name": "Band 3 Cell phones",
"type": "cellular",
"start": 1900000000,
"end": 1920000000
},
{
"name": "Band 1 Cell phone uplink",
"type": "cellular",
"start": 1920000000,
"end": 1979700000
},
{
"name": "Band 1 Cell phone downlink",
"type": "cellular",
"start": 2110300000,
"end": 2169700000
},
{ {
"name": "13cm Ham Band", "name": "13cm Ham Band",
"type": "amateur", "type": "amateur",
@ -498,10 +606,28 @@
"end": 2302000000 "end": 2302000000
}, },
{ {
"name": "13cm Ham Band", "name": "ISM - wifi and bluettoth",
"type": "amateur", "type": "ISM",
"start": 2310000000, "start": 2400000000,
"end": 2450000000 "end": 2483000000
},
{
"name": "Band 38 Cell phones",
"type": "cellular",
"start": 2500000000,
"end": 269000000
},
{
"name": "Band 42 5G Cell phones",
"type": "cellular",
"start": 3410000000,
"end": 3720000000
},
{
"name": "ISM - wifi",
"type": "ISM",
"start": 5150000000,
"end": 5850000000
} }
] ]
} }

View File

@ -227,6 +227,18 @@
"start": 144000000, "start": 144000000,
"end": 148000000 "end": 148000000
}, },
{
"name": "MURS (lower)",
"type": "amateur",
"start": 151820000,
"end": 151940000
},
{
"name": "MURS (upper)",
"type": "amateur",
"start": 154570000,
"end": 154600000
},
{ {
"name": "Marine", "name": "Marine",
"type": "marine", "type": "marine",

View File

@ -0,0 +1,14 @@
{
"name": "Smoke",
"author": "Yaroslav Andrianov",
"map": [
"#FFFFFF",
"#EEEEEE",
"#CCCCCC",
"#777777",
"#555555",
"#333333",
"#111111",
"#000000"
]
}

View File

@ -0,0 +1,28 @@
{
"name": "Temper Colors",
"author": "Yaroslav Andrianov",
"map": [
"#000000",
"#05011f",
"#0f0836",
"#2f1436",
"#3d114d",
"#4e186f",
"#592a8f",
"#5e43a5",
"#5f5eb3",
"#6276ba",
"#6b8cbf",
"#7ba1c2",
"#95b5c7",
"#b3c6ce",
"#d4bcac",
"#cca389",
"#c68a6d",
"#be6f5b",
"#b25652",
"#a24050",
"#8e2c50",
"#741e4f"
]
}

View File

@ -0,0 +1,29 @@
{
"name": "Vivid",
"author": "Yaroslav Andrianov",
"map": [
"#000000",
"#06001c",
"#090028",
"#12002c",
"#230039",
"#360143",
"#440154",
"#472c7a",
"#3b518b",
"#2c718e",
"#21908d",
"#27ad81",
"#5cc863",
"#aadc32",
"#f6fd25",
"#fdde17",
"#fecb31",
"#FE9029",
"#F56918",
"#DC3B07",
"#CE2D04",
"#AC1701",
"#980E01"
]
}

View File

@ -1,22 +0,0 @@
#!/bin/sh
set -e
[ $(id -u) = 0 ] && echo "Please do not run this script as root" && exit 100
echo "Installing dependencies"
sudo apt update
sudo apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget
echo "Preparing build"
mkdir -p build
cd build
cmake .. -DOPT_BUILD_LIMESDR_SOURCE=ON
echo "Building"
make
echo "Installing"
sudo make install
echo "Done!"

View File

@ -45,16 +45,20 @@ public:
int count = audio.getDeviceCount(); int count = audio.getDeviceCount();
RtAudio::DeviceInfo info; RtAudio::DeviceInfo info;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
info = audio.getDeviceInfo(i); try {
if (!info.probed) { continue; } info = audio.getDeviceInfo(i);
if (info.outputChannels == 0) { continue; } if (!info.probed) { continue; }
if (info.isDefaultOutput) { defaultDevId = devList.size(); } if (info.outputChannels == 0) { continue; }
devList.push_back(info); if (info.isDefaultOutput) { defaultDevId = devList.size(); }
deviceIds.push_back(i); devList.push_back(info);
txtDevList += info.name; deviceIds.push_back(i);
txtDevList += '\0'; txtDevList += info.name;
txtDevList += '\0';
}
catch (std::exception e) {
flog::error("AudioSinkModule Error getting audio device info: {0}", e.what());
}
} }
selectByName(device); selectByName(device);
} }
@ -290,4 +294,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
MOD_EXPORT void _END_() { MOD_EXPORT void _END_() {
config.disableAutoSave(); config.disableAutoSave();
config.save(); config.save();
} }

View File

@ -25,7 +25,7 @@ SDRPP_MOD_INFO{
ConfigManager config; ConfigManager config;
class AirspySourceModule : public ModuleManager::Instance, public Source { class AirspySourceModule : public ModuleManager::Instance {
public: public:
AirspySourceModule(std::string name) { AirspySourceModule(std::string name) {
this->name = name; this->name = name;
@ -34,6 +34,15 @@ public:
sampleRate = 10000000.0; sampleRate = 10000000.0;
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &stream;
refresh(); refresh();
if (sampleRateList.size() > 0) { if (sampleRateList.size() > 0) {
sampleRate = sampleRateList[0]; sampleRate = sampleRateList[0];
@ -45,11 +54,11 @@ public:
config.release(); config.release();
selectByString(devSerial); selectByString(devSerial);
sigpath::sourceManager.registerSource("Airspy", this); sigpath::sourceManager.registerSource("Airspy", &handler);
} }
~AirspySourceModule() { ~AirspySourceModule() {
stop(); stop(this);
sigpath::sourceManager.unregisterSource("Airspy"); sigpath::sourceManager.unregisterSource("Airspy");
airspy_exit(); airspy_exit();
} }
@ -222,315 +231,6 @@ public:
airspy_close(dev); airspy_close(dev);
} }
void select() {
core::setInputSampleRate(sampleRate);
flog::info("AirspySourceModule '{0}': Select!", name);
}
void deselect() {
flog::info("AirspySourceModule '{0}': Deselect!", name);
}
bool start() {
if (running) { return true; }
if (selectedSerial == 0) {
flog::error("Tried to start Airspy source with null serial");
return false;
}
#ifndef __ANDROID__
int err = airspy_open_sn(&openDev, selectedSerial);
#else
int err = airspy_open_fd(&openDev, devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, selectedSerial);
flog::error("Could not open Airspy {0}", buf);
return false;
}
airspy_set_samplerate(openDev, sampleRateList[srId]);
airspy_set_freq(openDev, freq);
if (gainMode == 0) {
airspy_set_lna_agc(openDev, 0);
airspy_set_mixer_agc(openDev, 0);
airspy_set_sensitivity_gain(openDev, sensitiveGain);
}
else if (gainMode == 1) {
airspy_set_lna_agc(openDev, 0);
airspy_set_mixer_agc(openDev, 0);
airspy_set_linearity_gain(openDev, linearGain);
}
else if (gainMode == 2) {
if (lnaAgc) {
airspy_set_lna_agc(openDev, 1);
}
else {
airspy_set_lna_agc(openDev, 0);
airspy_set_lna_gain(openDev, lnaGain);
}
if (mixerAgc) {
airspy_set_mixer_agc(openDev, 1);
}
else {
airspy_set_mixer_agc(openDev, 0);
airspy_set_mixer_gain(openDev, mixerGain);
}
airspy_set_vga_gain(openDev, vgaGain);
}
airspy_set_rf_bias(openDev, biasT);
airspy_start_rx(openDev, callback, this);
running = true;
flog::info("AirspySourceModule '{0}': Start!", name);
}
void stop() {
if (!running) { return; }
running = false;
stream.stopWriter();
airspy_close(openDev);
stream.clearWriteStop();
flog::info("AirspySourceModule '{0}': Stop!", name);
}
void tune(double freq) {
this->freq = freq;
if (running) {
airspy_set_freq(openDev, freq);
}
flog::info("AirspySourceModule '{0}': Tune: {1}!", name, freq);
}
void showMenu() {
if (running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", name), &devId, devListTxt.c_str())) {
selectBySerial(devList[devId]);
core::setInputSampleRate(sampleRate);
if (selectedSerStr != "") {
config.acquire();
config.conf["device"] = selectedSerStr;
config.release(true);
}
}
if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", name), &srId, sampleRateListTxt.c_str())) {
sampleRate = sampleRateList[srId];
core::setInputSampleRate(sampleRate);
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["sampleRate"] = sampleRate;
config.release(true);
}
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", name))) {
refresh();
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
selectByString(devSerial);
core::setInputSampleRate(sampleRate);
}
if (running) { SmGui::EndDisabled(); }
SmGui::BeginGroup();
SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", name), false);
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", name), gainMode == 0)) {
gainMode = 0;
if (running) {
airspy_set_lna_agc(openDev, 0);
airspy_set_mixer_agc(openDev, 0);
airspy_set_sensitivity_gain(openDev, sensitiveGain);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["gainMode"] = 0;
config.release(true);
}
}
SmGui::NextColumn();
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", name), gainMode == 1)) {
gainMode = 1;
if (running) {
airspy_set_lna_agc(openDev, 0);
airspy_set_mixer_agc(openDev, 0);
airspy_set_linearity_gain(openDev, linearGain);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["gainMode"] = 1;
config.release(true);
}
}
SmGui::NextColumn();
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", name), gainMode == 2)) {
gainMode = 2;
if (running) {
if (lnaAgc) {
airspy_set_lna_agc(openDev, 1);
}
else {
airspy_set_lna_agc(openDev, 0);
airspy_set_lna_gain(openDev, lnaGain);
}
if (mixerAgc) {
airspy_set_mixer_agc(openDev, 1);
}
else {
airspy_set_mixer_agc(openDev, 0);
airspy_set_mixer_gain(openDev, mixerGain);
}
airspy_set_vga_gain(openDev, vgaGain);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["gainMode"] = 2;
config.release(true);
}
}
SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", name), false);
SmGui::EndGroup();
// Gain menus
if (gainMode == 0) {
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", name), &sensitiveGain, 0, 21)) {
if (running) {
airspy_set_sensitivity_gain(openDev, sensitiveGain);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["sensitiveGain"] = sensitiveGain;
config.release(true);
}
}
}
else if (gainMode == 1) {
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", name), &linearGain, 0, 21)) {
if (running) {
airspy_set_linearity_gain(openDev, linearGain);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["linearGain"] = linearGain;
config.release(true);
}
}
}
else if (gainMode == 2) {
// TODO: Switch to a table for alignment
if (lnaAgc) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("LNA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", name), &lnaGain, 0, 15)) {
if (running) {
airspy_set_lna_gain(openDev, lnaGain);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["lnaGain"] = lnaGain;
config.release(true);
}
}
if (lnaAgc) { SmGui::EndDisabled(); }
if (mixerAgc) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("Mixer Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", name), &mixerGain, 0, 15)) {
if (running) {
airspy_set_mixer_gain(openDev, mixerGain);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["mixerGain"] = mixerGain;
config.release(true);
}
}
if (mixerAgc) { SmGui::EndDisabled(); }
SmGui::LeftLabel("VGA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", name), &vgaGain, 0, 15)) {
if (running) {
airspy_set_vga_gain(openDev, vgaGain);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["vgaGain"] = vgaGain;
config.release(true);
}
}
// AGC Control
SmGui::ForceSync();
if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", name), &lnaAgc)) {
if (running) {
if (lnaAgc) {
airspy_set_lna_agc(openDev, 1);
}
else {
airspy_set_lna_agc(openDev, 0);
airspy_set_lna_gain(openDev, lnaGain);
}
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["lnaAgc"] = lnaAgc;
config.release(true);
}
}
SmGui::ForceSync();
if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", name), &mixerAgc)) {
if (running) {
if (mixerAgc) {
airspy_set_mixer_agc(openDev, 1);
}
else {
airspy_set_mixer_agc(openDev, 0);
airspy_set_mixer_gain(openDev, mixerGain);
}
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["mixerAgc"] = mixerAgc;
config.release(true);
}
}
}
// Bias T
if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", name), &biasT)) {
if (running) {
airspy_set_rf_bias(openDev, biasT);
}
if (selectedSerStr != "") {
config.acquire();
config.conf["devices"][selectedSerStr]["biasT"] = biasT;
config.release(true);
}
}
}
private: private:
std::string getBandwdithScaled(double bw) { std::string getBandwdithScaled(double bw) {
char buf[1024]; char buf[1024];
@ -546,6 +246,322 @@ private:
return std::string(buf); return std::string(buf);
} }
static void menuSelected(void* ctx) {
AirspySourceModule* _this = (AirspySourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("AirspySourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
AirspySourceModule* _this = (AirspySourceModule*)ctx;
flog::info("AirspySourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
AirspySourceModule* _this = (AirspySourceModule*)ctx;
if (_this->running) { return; }
if (_this->selectedSerial == 0) {
flog::error("Tried to start Airspy source with null serial");
return;
}
#ifndef __ANDROID__
int err = airspy_open_sn(&_this->openDev, _this->selectedSerial);
#else
int err = airspy_open_fd(&_this->openDev, _this->devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
flog::error("Could not open Airspy {0}", buf);
return;
}
airspy_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]);
airspy_set_freq(_this->openDev, _this->freq);
if (_this->gainMode == 0) {
airspy_set_lna_agc(_this->openDev, 0);
airspy_set_mixer_agc(_this->openDev, 0);
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
}
else if (_this->gainMode == 1) {
airspy_set_lna_agc(_this->openDev, 0);
airspy_set_mixer_agc(_this->openDev, 0);
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
}
else if (_this->gainMode == 2) {
if (_this->lnaAgc) {
airspy_set_lna_agc(_this->openDev, 1);
}
else {
airspy_set_lna_agc(_this->openDev, 0);
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (_this->mixerAgc) {
airspy_set_mixer_agc(_this->openDev, 1);
}
else {
airspy_set_mixer_agc(_this->openDev, 0);
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
}
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
}
airspy_set_rf_bias(_this->openDev, _this->biasT);
airspy_start_rx(_this->openDev, callback, _this);
_this->running = true;
flog::info("AirspySourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
AirspySourceModule* _this = (AirspySourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
_this->stream.stopWriter();
airspy_close(_this->openDev);
_this->stream.clearWriteStop();
flog::info("AirspySourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
AirspySourceModule* _this = (AirspySourceModule*)ctx;
if (_this->running) {
airspy_set_freq(_this->openDev, freq);
}
_this->freq = freq;
flog::info("AirspySourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
AirspySourceModule* _this = (AirspySourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
_this->selectBySerial(_this->devList[_this->devId]);
core::setInputSampleRate(_this->sampleRate);
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["device"] = _this->selectedSerStr;
config.release(true);
}
}
if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) {
_this->sampleRate = _this->sampleRateList[_this->srId];
core::setInputSampleRate(_this->sampleRate);
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->sampleRate;
config.release(true);
}
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", _this->name))) {
_this->refresh();
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
_this->selectByString(devSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::BeginGroup();
SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", _this->name), false);
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", _this->name), _this->gainMode == 0)) {
_this->gainMode = 0;
if (_this->running) {
airspy_set_lna_agc(_this->openDev, 0);
airspy_set_mixer_agc(_this->openDev, 0);
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0;
config.release(true);
}
}
SmGui::NextColumn();
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", _this->name), _this->gainMode == 1)) {
_this->gainMode = 1;
if (_this->running) {
airspy_set_lna_agc(_this->openDev, 0);
airspy_set_mixer_agc(_this->openDev, 0);
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1;
config.release(true);
}
}
SmGui::NextColumn();
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", _this->name), _this->gainMode == 2)) {
_this->gainMode = 2;
if (_this->running) {
if (_this->lnaAgc) {
airspy_set_lna_agc(_this->openDev, 1);
}
else {
airspy_set_lna_agc(_this->openDev, 0);
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (_this->mixerAgc) {
airspy_set_mixer_agc(_this->openDev, 1);
}
else {
airspy_set_mixer_agc(_this->openDev, 0);
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
}
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2;
config.release(true);
}
}
SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false);
SmGui::EndGroup();
// Gain menus
if (_this->gainMode == 0) {
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) {
if (_this->running) {
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain;
config.release(true);
}
}
}
else if (_this->gainMode == 1) {
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) {
if (_this->running) {
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain;
config.release(true);
}
}
}
else if (_this->gainMode == 2) {
// TODO: Switch to a table for alignment
if (_this->lnaAgc) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("LNA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
if (_this->running) {
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain;
config.release(true);
}
}
if (_this->lnaAgc) { SmGui::EndDisabled(); }
if (_this->mixerAgc) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("Mixer Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) {
if (_this->running) {
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain;
config.release(true);
}
}
if (_this->mixerAgc) { SmGui::EndDisabled(); }
SmGui::LeftLabel("VGA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
if (_this->running) {
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain;
config.release(true);
}
}
// AGC Control
SmGui::ForceSync();
if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", _this->name), &_this->lnaAgc)) {
if (_this->running) {
if (_this->lnaAgc) {
airspy_set_lna_agc(_this->openDev, 1);
}
else {
airspy_set_lna_agc(_this->openDev, 0);
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
}
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc;
config.release(true);
}
}
SmGui::ForceSync();
if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", _this->name), &_this->mixerAgc)) {
if (_this->running) {
if (_this->mixerAgc) {
airspy_set_mixer_agc(_this->openDev, 1);
}
else {
airspy_set_mixer_agc(_this->openDev, 0);
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
}
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc;
config.release(true);
}
}
}
// Bias T
if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", _this->name), &_this->biasT)) {
if (_this->running) {
airspy_set_rf_bias(_this->openDev, _this->biasT);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT;
config.release(true);
}
}
}
static int callback(airspy_transfer_t* transfer) { static int callback(airspy_transfer_t* transfer) {
AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx; AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx;
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t)); memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
@ -558,6 +574,7 @@ private:
bool enabled = true; bool enabled = true;
dsp::stream<dsp::complex_t> stream; dsp::stream<dsp::complex_t> stream;
double sampleRate; double sampleRate;
SourceManager::SourceHandler handler;
bool running = false; bool running = false;
double freq; double freq;
uint64_t selectedSerial = 0; uint64_t selectedSerial = 0;

View File

@ -25,7 +25,7 @@ ConfigManager config;
struct DeviceInfo { struct DeviceInfo {
RtAudio::DeviceInfo info; RtAudio::DeviceInfo info;
int id; int id;
bool operator==(const struct DeviceInfo& other) { bool operator==(const struct DeviceInfo& other) const {
return other.id == id; return other.id == id;
} }
}; };

View File

@ -1,3 +1,4 @@
#define NOMINMAX
#include <imgui.h> #include <imgui.h>
#include <utils/flog.h> #include <utils/flog.h>
#include <module.h> #include <module.h>
@ -9,6 +10,8 @@
#include <filesystem> #include <filesystem>
#include <regex> #include <regex>
#include <gui/tuner.h> #include <gui/tuner.h>
#include <algorithm>
#include <stdexcept>
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -121,6 +124,12 @@ private:
} }
try { try {
_this->reader = new WavReader(_this->fileSelect.path); _this->reader = new WavReader(_this->fileSelect.path);
if (_this->reader->getSampleRate() == 0) {
_this->reader->close();
delete _this->reader;
_this->reader = NULL;
throw std::runtime_error("Sample rate may not be zero");
}
_this->sampleRate = _this->reader->getSampleRate(); _this->sampleRate = _this->reader->getSampleRate();
core::setInputSampleRate(_this->sampleRate); core::setInputSampleRate(_this->sampleRate);
std::string filename = std::filesystem::path(_this->fileSelect.path).filename().string(); std::string filename = std::filesystem::path(_this->fileSelect.path).filename().string();
@ -130,7 +139,7 @@ private:
//gui::freqSelect.maxFreq = _this->centerFreq + (_this->sampleRate/2); //gui::freqSelect.maxFreq = _this->centerFreq + (_this->sampleRate/2);
//gui::freqSelect.limitFreq = true; //gui::freqSelect.limitFreq = true;
} }
catch (std::exception e) { catch (std::exception& e) {
flog::error("Error: {0}", e.what()); flog::error("Error: {0}", e.what());
} }
config.acquire(); config.acquire();
@ -144,8 +153,8 @@ private:
static void worker(void* ctx) { static void worker(void* ctx) {
FileSourceModule* _this = (FileSourceModule*)ctx; FileSourceModule* _this = (FileSourceModule*)ctx;
double sampleRate = _this->reader->getSampleRate(); double sampleRate = std::max(_this->reader->getSampleRate(), (uint32_t)1);
int blockSize = sampleRate / 200.0f; int blockSize = std::min((int)(sampleRate / 200.0f), (int)STREAM_BUFFER_SIZE);
int16_t* inBuf = new int16_t[blockSize * 2]; int16_t* inBuf = new int16_t[blockSize * 2];
while (true) { while (true) {
@ -159,8 +168,8 @@ private:
static void floatWorker(void* ctx) { static void floatWorker(void* ctx) {
FileSourceModule* _this = (FileSourceModule*)ctx; FileSourceModule* _this = (FileSourceModule*)ctx;
double sampleRate = _this->reader->getSampleRate(); double sampleRate = std::max(_this->reader->getSampleRate(), (uint32_t)1);
int blockSize = sampleRate / 200.0f; int blockSize = std::min((int)(sampleRate / 200.0f), (int)STREAM_BUFFER_SIZE);
dsp::complex_t* inBuf = new dsp::complex_t[blockSize]; dsp::complex_t* inBuf = new dsp::complex_t[blockSize];
while (true) { while (true) {
@ -214,4 +223,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
MOD_EXPORT void _END_() { MOD_EXPORT void _END_() {
config.disableAutoSave(); config.disableAutoSave();
config.save(); config.save();
} }

View File

@ -137,6 +137,10 @@ public:
hackrf_device_list_t* _devList = hackrf_device_list(); hackrf_device_list_t* _devList = hackrf_device_list();
for (int i = 0; i < _devList->devicecount; i++) { for (int i = 0; i < _devList->devicecount; i++) {
// Skip devices that are in use
if (_devList->serial_numbers[i] == NULL) { continue; }
// Save the device serial number
devList.push_back(_devList->serial_numbers[i]); devList.push_back(_devList->serial_numbers[i]);
devListTxt += (char*)(_devList->serial_numbers[i] + 16); devListTxt += (char*)(_devList->serial_numbers[i] + 16);
devListTxt += '\0'; devListTxt += '\0';

View File

@ -205,6 +205,7 @@ namespace hermes {
} }
std::vector<Info> discover() { std::vector<Info> discover() {
// TODO: Maybe try to instead detect on each interface as a work around for 0.0.0.0 not receiving anything?
auto sock = net::openudp("0.0.0.0", 1024); auto sock = net::openudp("0.0.0.0", 1024);
// Build discovery packet // Build discovery packet

View File

@ -39,7 +39,7 @@ namespace hermes {
uint8_t gatewareVerMin; uint8_t gatewareVerMin;
BoardID boardId; BoardID boardId;
bool operator==(const Info& b) { bool operator==(const Info& b) const {
return !memcmp(mac, b.mac, 6); return !memcmp(mac, b.mac, 6);
} }
}; };

View File

@ -319,6 +319,7 @@ private:
static void start(void* ctx) { static void start(void* ctx) {
LimeSDRSourceModule* _this = (LimeSDRSourceModule*)ctx; LimeSDRSourceModule* _this = (LimeSDRSourceModule*)ctx;
if (_this->running) { return; } if (_this->running) { return; }
if (_this->selectedDevName.empty()) { return; }
// Open device // Open device
_this->openDev = NULL; _this->openDev = NULL;
@ -329,7 +330,10 @@ private:
if (err) { if (err) {
LMS_Close(_this->openDev); LMS_Close(_this->openDev);
LMS_Open(&_this->openDev, _this->devList[_this->devId], NULL); LMS_Open(&_this->openDev, _this->devList[_this->devId], NULL);
LMS_Init(_this->openDev); if (err = LMS_Init(_this->openDev)) {
flog::error("Failed to re-initialize device ({})", err);
return;
}
} }
flog::warn("Channel count: {0}", LMS_GetNumChannels(_this->openDev, false)); flog::warn("Channel count: {0}", LMS_GetNumChannels(_this->openDev, false));
@ -546,4 +550,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
MOD_EXPORT void _END_() { MOD_EXPORT void _END_() {
config.disableAutoSave(); config.disableAutoSave();
config.save(); config.save();
} }

View File

@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.13)
project(perseus_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
if (MSVC)
# Lib path
target_link_directories(perseus_source PRIVATE "C:/Program Files/PothosSDR/bin/")
target_include_directories(perseus_source PUBLIC "C:/Program Files/PothosSDR/include/perseus-sdr")
target_link_libraries(perseus_source PRIVATE perseus-sdr)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBPERSEUSSDR REQUIRED libperseus-sdr)
target_include_directories(perseus_source PRIVATE ${LIBPERSEUSSDR_INCLUDE_DIRS})
target_link_directories(perseus_source PRIVATE ${LIBPERSEUSSDR_LIBRARY_DIRS})
target_link_libraries(perseus_source PRIVATE ${LIBPERSEUSSDR_LIBRARIES})
# Include it because for some reason pkgconfig doesn't look here?
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_include_directories(perseus_source PRIVATE "/usr/local/include")
endif()
endif ()

View File

@ -0,0 +1,468 @@
#include <utils/flog.h>
#include <module.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <gui/style.h>
#include <config.h>
#include <gui/smgui.h>
#include <gui/widgets/stepped_slider.h>
#include <perseus-sdr.h>
#include <utils/optionlist.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "perseus_source",
/* Description: */ "Perseus SDR source module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ 1
};
#define MAX_SAMPLERATE_COUNT 128
ConfigManager config;
class PerseusSourceModule : public ModuleManager::Instance {
public:
PerseusSourceModule(std::string name) {
this->name = name;
sampleRate = 768000;
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &stream;
perseus_set_debug(9);
refresh();
config.acquire();
std::string serial = config.conf["device"];
config.release();
select(serial);
sigpath::sourceManager.registerSource("Perseus", &handler);
}
~PerseusSourceModule() {
stop(this);
sigpath::sourceManager.unregisterSource("Perseus");
if (libInit) { perseus_exit(); }
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
void refresh() {
// Re-initialize driver
if (libInit) { perseus_exit(); }
int devCount = perseus_init();
if (devCount < 0) {
libInit = false;
flog::error("Could not initialize libperseus: {}", perseus_errorstr());
return;
}
libInit = true;
// Open each device to get the serial number
for (int i = 0; i < devCount; i++) {
// Open device
perseus_descr* dev = perseus_open(i);
if (!dev) {
flog::error("Failed to open Perseus device with ID {}: {}", i, perseus_errorstr());
continue;
}
// Load firmware
int err = perseus_firmware_download(dev, NULL);
if (err) {
flog::error("Could not upload firmware to device {}: {}", i, perseus_errorstr());
perseus_close(dev);
continue;
}
// Get info
eeprom_prodid prodId;
err = perseus_get_product_id(dev, &prodId);
if (err) {
flog::error("Could not getproduct info from device {}: {}", i, perseus_errorstr());
perseus_close(dev);
continue;
}
// Create entry
char serial[128];
char buf[128];
sprintf(serial, "%05d", (int)prodId.sn);
sprintf(buf, "Perseus %d.%d [%s]", (int)prodId.hwver, (int)prodId.hwrel, serial);
devList.define(serial, buf, i);
// Close device
perseus_close(dev);
}
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devList.empty()) {
selectedSerial.clear();
return;
}
// If the serial number is not available, select first instead
if (!devList.keyExists(serial)) {
select(devList.key(0));
return;
}
// Open device
selectedSerial = serial;
selectedPerseusId = devList.value(devList.keyId(serial));
perseus_descr* dev = perseus_open(selectedPerseusId);
if (!dev) {
flog::error("Failed to open device {}: {}", selectedPerseusId, perseus_errorstr());
selectedSerial.clear();
return;
}
// Load firmware
int err = perseus_firmware_download(dev, NULL);
if (err) {
flog::error("Could not upload firmware to device: {}", perseus_errorstr());
perseus_close(dev);
selectedSerial.clear();
return;
}
// Get info
eeprom_prodid prodId;
err = perseus_get_product_id(dev, &prodId);
if (err) {
flog::error("Could not getproduct info from device: {}", perseus_errorstr());
perseus_close(dev);
selectedSerial.clear();
return;
}
// List samplerates
srList.clear();
int samplerates[MAX_SAMPLERATE_COUNT];
memset(samplerates, 0, sizeof(int)*MAX_SAMPLERATE_COUNT);
err = perseus_get_sampling_rates(dev, samplerates, MAX_SAMPLERATE_COUNT);
if (err) {
flog::error("Could not get samplerate list: {}", perseus_errorstr());
perseus_close(dev);
selectedSerial.clear();
return;
}
for (int i = 0; i < MAX_SAMPLERATE_COUNT; i++) {
if (!samplerates[i]) { break; }
srList.define(samplerates[i], getBandwdithScaled(samplerates[i]), samplerates[i]);
}
// TODO: List attenuator values
// Load options
srId = 0;
dithering = false;
preamp = false;
preselector = true;
atten = 0;
config.acquire();
if (config.conf["devices"][selectedSerial].contains("samplerate")) {
int sr = config.conf["devices"][selectedSerial]["samplerate"];
if (srList.keyExists(sr)) {
srId = srList.keyId(sr);
}
}
if (config.conf["devices"][selectedSerial].contains("dithering")) {
dithering = config.conf["devices"][selectedSerial]["dithering"];
}
if (config.conf["devices"][selectedSerial].contains("preamp")) {
preamp = config.conf["devices"][selectedSerial]["preamp"];
}
if (config.conf["devices"][selectedSerial].contains("preselector")) {
preselector = config.conf["devices"][selectedSerial]["preselector"];
}
if (config.conf["devices"][selectedSerial].contains("attenuation")) {
atten = config.conf["devices"][selectedSerial]["attenuation"];
}
config.release();
// Update samplerate
sampleRate = srList[srId];
core::setInputSampleRate(sampleRate);
// Close device
perseus_close(dev);
}
private:
std::string getBandwdithScaled(double bw) {
char buf[1024];
if (bw >= 1000000.0) {
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
}
else if (bw >= 1000.0) {
sprintf(buf, "%.1lfKHz", bw / 1000.0);
}
else {
sprintf(buf, "%.1lfHz", bw);
}
return std::string(buf);
}
static void menuSelected(void* ctx) {
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("PerseusSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
flog::info("PerseusSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
if (_this->running) { return; }
if (_this->selectedSerial.empty()) {
flog::error("No device is selected");
return;
}
// Open device
_this->openDev = perseus_open(_this->selectedPerseusId);
if (!_this->openDev) {
flog::error("Failed to open device {}: {}", _this->selectedPerseusId, perseus_errorstr());
return;
}
// Load firmware
int err = perseus_firmware_download(_this->openDev, NULL);
if (err) {
flog::error("Could not upload firmware to device: {}", perseus_errorstr());
perseus_close(_this->openDev);
return;
}
// Set samplerate
err = perseus_set_sampling_rate(_this->openDev, _this->sampleRate);
if (err) {
flog::error("Could not set samplerate: {}", perseus_errorstr());
perseus_close(_this->openDev);
return;
}
// Set options
perseus_set_adc(_this->openDev, _this->dithering, _this->preamp);
perseus_set_attenuator_in_db(_this->openDev, _this->atten);
perseus_set_ddc_center_freq(_this->openDev, _this->freq, _this->preselector);
// Start stream
int idealBufferSize = _this->sampleRate / 200;
int multipleOf1024 = std::clamp<int>(idealBufferSize / 1024, 1, 2);
int bufferSize = multipleOf1024 * 1024;
int bufferBytes = bufferSize*6;
err = perseus_start_async_input(_this->openDev, bufferBytes, callback, _this);
if (err) {
flog::error("Could not start stream: {}", perseus_errorstr());
perseus_close(_this->openDev);
return;
}
_this->running = true;
flog::info("PerseusSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop stream
_this->stream.stopWriter();
perseus_stop_async_input(_this->openDev);
_this->stream.clearWriteStop();
// Close device
perseus_close(_this->openDev);
flog::info("PerseusSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
if (_this->running) {
perseus_set_ddc_center_freq(_this->openDev, freq, _this->preselector);
}
_this->freq = freq;
flog::info("PerseusSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_airspyhf_dev_sel_", _this->name), &_this->devId, _this->devList.txt)) {
std::string serial = _this->devList.key(_this->devId);
_this->select(serial);
config.acquire();
config.conf["device"] = serial;
config.release(true);
}
if (SmGui::Combo(CONCAT("##_airspyhf_sr_sel_", _this->name), &_this->srId, _this->srList.txt)) {
_this->sampleRate = _this->srList[_this->srId];
core::setInputSampleRate(_this->sampleRate);
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->sampleRate;
config.release(true);
}
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_airspyhf_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("Attenuation");
SmGui::FillWidth();
if (SmGui::SliderFloatWithSteps(CONCAT("##_airspyhf_atten_", _this->name), &_this->atten, 0, 30, 10, SmGui::FMT_STR_FLOAT_DB_NO_DECIMAL)) {
if (_this->running) {
perseus_set_attenuator_in_db(_this->openDev, _this->atten);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["attenuation"] = _this->atten;
config.release(true);
}
}
if (SmGui::Checkbox(CONCAT("Preamp##_airspyhf_preamp_", _this->name), &_this->preamp)) {
if (_this->running) {
perseus_set_adc(_this->openDev, _this->dithering, _this->preamp);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["preamp"] = _this->preamp;
config.release(true);
}
}
if (SmGui::Checkbox(CONCAT("Dithering##_airspyhf_dither_", _this->name), &_this->dithering)) {
if (_this->running) {
perseus_set_adc(_this->openDev, _this->dithering, _this->preamp);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["dithering"] = _this->dithering;
config.release(true);
}
}
if (SmGui::Checkbox(CONCAT("Preselector##_airspyhf_presel_", _this->name), &_this->preselector)) {
if (_this->running) {
perseus_set_ddc_center_freq(_this->openDev, _this->freq, _this->preselector);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["preselector"] = _this->preselector;
config.release(true);
}
}
}
static int callback(void* buf, int bufferSize, void* ctx) {
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
uint8_t* samples = (uint8_t*)buf;
int sampleCount = bufferSize / 6;
for (int i = 0; i < sampleCount; i++) {
int32_t re, im;
re = *(samples++);
re |= *(samples++) << 8;
re |= *(samples++) << 16;
re |= (re >> 23) * (0xFF << 24); // Sign extend
im = *(samples++);
im |= *(samples++) << 8;
im |= *(samples++) << 16;
im |= (im >> 23) * (0xFF << 24); // Sign extend
_this->stream.writeBuf[i].re = ((float)re / (float)0x7FFFFF);
_this->stream.writeBuf[i].im = ((float)im / (float)0x7FFFFF);
}
_this->stream.swap(sampleCount);
return 0;
}
std::string name;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;
int sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
int devId = 0;
int srId = 0;
bool libInit = false;
perseus_descr* openDev;
std::string selectedSerial = "";
int selectedPerseusId;
float atten = 0;
bool preamp = false;
bool dithering = false;
bool preselector = true;
OptionList<std::string, int> devList;
OptionList<int, int> srList;
};
MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(core::args["root"].s() + "/perseus_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new PerseusSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
delete (PerseusSourceModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -108,12 +108,12 @@ namespace rfspace {
} }
break; break;
case RFSPACE_DEV_ID_NET_SDR: case RFSPACE_DEV_ID_NET_SDR:
case RFSPACE_DEV_ID_SDR_IP:
default:
for (int n = 80000000 / (4 * 25); n >= 32000; n /= 2) { for (int n = 80000000 / (4 * 25); n >= 32000; n /= 2) {
sr.push_back(n); sr.push_back(n);
} }
break; break;
default:
break;
} }
return sr; return sr;

View File

@ -171,7 +171,7 @@ public:
#ifndef __ANDROID__ #ifndef __ANDROID__
int oret = rtlsdr_open(&openDev, id); int oret = rtlsdr_open(&openDev, id);
#else #else
int oret = rtlsdr_open_fd(&openDev, devFd); int oret = rtlsdr_open_sys_dev(&openDev, devFd);
#endif #endif
if (oret < 0) { if (oret < 0) {
@ -285,7 +285,7 @@ private:
#ifndef __ANDROID__ #ifndef __ANDROID__
int oret = rtlsdr_open(&_this->openDev, _this->devId); int oret = rtlsdr_open(&_this->openDev, _this->devId);
#else #else
int oret = rtlsdr_open_fd(&_this->openDev, _this->devFd); int oret = rtlsdr_open_sys_dev(&_this->openDev, _this->devFd);
#endif #endif
if (oret < 0) { if (oret < 0) {
@ -523,8 +523,8 @@ private:
RTLSDRSourceModule* _this = (RTLSDRSourceModule*)ctx; RTLSDRSourceModule* _this = (RTLSDRSourceModule*)ctx;
int sampCount = len / 2; int sampCount = len / 2;
for (int i = 0; i < sampCount; i++) { for (int i = 0; i < sampCount; i++) {
_this->stream.writeBuf[i].re = (float)(buf[i * 2] - 127) / 128.0f; _this->stream.writeBuf[i].re = ((float)buf[i * 2] - 127.4) / 128.0f;
_this->stream.writeBuf[i].im = (float)(buf[(i * 2) + 1] - 127) / 128.0f; _this->stream.writeBuf[i].im = ((float)buf[(i * 2) + 1] - 127.4) / 128.0f;
} }
if (!_this->stream.swap(sampCount)) { return; } if (!_this->stream.swap(sampCount)) { return; }
} }

View File

@ -28,7 +28,7 @@ public:
this->name = name; this->name = name;
strcpy(hostname, "localhost"); strcpy(hostname, "localhost");
sampleRate = 41000000.0; sampleRate = 5750000.0;
handler.ctx = this; handler.ctx = this;
handler.selectHandler = menuSelected; handler.selectHandler = menuSelected;
@ -103,8 +103,14 @@ private:
static void tune(double freq, void* ctx) { static void tune(double freq, void* ctx) {
SpectranHTTPSourceModule* _this = (SpectranHTTPSourceModule*)ctx; SpectranHTTPSourceModule* _this = (SpectranHTTPSourceModule*)ctx;
if (_this->running) { bool connected = (_this->client && _this->client->isOpen());
// TODO if (connected) {
int64_t newfreq = round(freq);
if (newfreq != _this->lastReportedFreq && _this->gotReport) {
flog::debug("Sending tuning command");
_this->lastReportedFreq = newfreq;
_this->client->setCenterFrequency(newfreq);
}
} }
_this->freq = freq; _this->freq = freq;
flog::info("SpectranHTTPSourceModule '{0}': Tune: {1}!", _this->name, freq); flog::info("SpectranHTTPSourceModule '{0}': Tune: {1}!", _this->name, freq);
@ -138,6 +144,8 @@ private:
_this->tryConnect(); _this->tryConnect();
} }
else if (connected && SmGui::Button("Disconnect##spectran_http_source")) { else if (connected && SmGui::Button("Disconnect##spectran_http_source")) {
_this->client->onCenterFrequencyChanged.unbind(_this->onFreqChangedId);
_this->client->onCenterFrequencyChanged.unbind(_this->onSamplerateChangedId);
_this->client->close(); _this->client->close();
} }
if (_this->running) { style::endDisabled(); } if (_this->running) { style::endDisabled(); }
@ -154,13 +162,28 @@ private:
void tryConnect() { void tryConnect() {
try { try {
gotReport = false;
client = std::make_shared<SpectranHTTPClient>(hostname, port, &stream); client = std::make_shared<SpectranHTTPClient>(hostname, port, &stream);
onFreqChangedId = client->onCenterFrequencyChanged.bind(&SpectranHTTPSourceModule::onFreqChanged, this);
onSamplerateChangedId = client->onSamplerateChanged.bind(&SpectranHTTPSourceModule::onSamplerateChanged, this);
client->startWorker();
} }
catch (std::runtime_error e) { catch (std::runtime_error e) {
flog::error("Could not connect: {0}", e.what()); flog::error("Could not connect: {0}", e.what());
} }
} }
void onFreqChanged(double newFreq) {
if (lastReportedFreq == newFreq) { return; }
lastReportedFreq = newFreq;
tuner::tune(tuner::TUNER_MODE_IQ_ONLY, "", newFreq);
gotReport = true;
}
void onSamplerateChanged(double newSr) {
core::setInputSampleRate(newSr);
}
std::string name; std::string name;
bool enabled = true; bool enabled = true;
double sampleRate; double sampleRate;
@ -168,11 +191,16 @@ private:
bool running = false; bool running = false;
std::shared_ptr<SpectranHTTPClient> client; std::shared_ptr<SpectranHTTPClient> client;
HandlerID onFreqChangedId;
HandlerID onSamplerateChangedId;
double freq; double freq;
int64_t lastReportedFreq = 0;
bool gotReport;
char hostname[1024]; char hostname[1024];
int port = 80; int port = 54664;
dsp::stream<dsp::complex_t> stream; dsp::stream<dsp::complex_t> stream;
}; };

View File

@ -5,6 +5,8 @@ SpectranHTTPClient::SpectranHTTPClient(std::string host, int port, dsp::stream<d
this->stream = stream; this->stream = stream;
// Connect to server // Connect to server
this->host = host;
this->port = port;
sock = net::connect(host, port); sock = net::connect(host, port);
http = net::http::Client(sock); http = net::http::Client(sock);
@ -14,6 +16,13 @@ SpectranHTTPClient::SpectranHTTPClient(std::string host, int port, dsp::stream<d
net::http::ResponseHeader rshdr; net::http::ResponseHeader rshdr;
http.recvResponseHeader(rshdr, 5000); http.recvResponseHeader(rshdr, 5000);
if (rshdr.getStatusCode() != net::http::STATUS_CODE_OK) {
flog::error("HTTP request did not return ok: {}", rshdr.getStatusString());
throw std::runtime_error("HTTP request did not return ok");
}
}
void SpectranHTTPClient::startWorker() {
// Start chunk worker // Start chunk worker
workerThread = std::thread(&SpectranHTTPClient::worker, this); workerThread = std::thread(&SpectranHTTPClient::worker, this);
} }
@ -33,6 +42,27 @@ void SpectranHTTPClient::close() {
stream->clearWriteStop(); stream->clearWriteStop();
} }
void SpectranHTTPClient::setCenterFrequency(uint64_t freq) {
// Connect to control endpoint (TODO: Switch to an always connected endpoint)
auto controlSock = net::connect(host, port);
auto controlHttp = net::http::Client(controlSock);
// Make request
net::http::RequestHeader rqhdr(net::http::METHOD_PUT, "/control", host);
char buf[1024];
sprintf(buf, "{\"frequencyCenter\":%d,\"frequencySpan\":%d,\"type\":\"capture\"}", freq, _samplerate);
std::string data = buf;
char lenBuf[16];
sprintf(lenBuf, "%d", data.size());
rqhdr.setField("Content-Length", lenBuf);
controlHttp.sendRequestHeader(rqhdr);
controlSock->sendstr(data);
net::http::ResponseHeader rshdr;
controlHttp.recvResponseHeader(rshdr, 5000);
flog::debug("Response: {}", rshdr.getStatusString());
}
void SpectranHTTPClient::worker() { void SpectranHTTPClient::worker() {
while (sock->isOpen()) { while (sock->isOpen()) {
// Get chunk header // Get chunk header
@ -52,6 +82,41 @@ void SpectranHTTPClient::worker() {
return; return;
} }
// Decode JSON (yes, this is hacky, but it must be extremely fast)
auto startFreqBegin = jsonData.find("\"startFrequency\":");
auto startFreqEnd = jsonData.find(',', startFreqBegin);
std::string startFreqStr = jsonData.substr(startFreqBegin + 17, startFreqEnd - startFreqBegin - 17);
int64_t startFreq = std::stoll(startFreqStr);
auto endFreqBegin = jsonData.find("\"endFrequency\":");
auto endFreqEnd = jsonData.find(',', endFreqBegin);
std::string endFreqStr = jsonData.substr(endFreqBegin + 15, endFreqEnd - endFreqBegin - 15);
int64_t endFreq = std::stoll(endFreqStr);
auto sampleFreqBegin = jsonData.find("\"sampleFrequency\":");
bool sampleFreqReceived = (sampleFreqBegin != -1);
int64_t sampleFreq;
if (sampleFreqReceived) {
auto sampleFreqEnd = jsonData.find(',', sampleFreqBegin);
std::string sampleFreqStr = jsonData.substr(sampleFreqBegin + 18, sampleFreqEnd - sampleFreqBegin - 18);
sampleFreq = std::stoll(sampleFreqStr);
//flog::debug("{}", jsonData);
}
// Calculate and update center freq
int64_t samplerate = /* sampleFreqReceived ? sampleFreq : */(endFreq - startFreq);
int64_t centerFreq = round(((double)endFreq + (double)startFreq) / 2.0);
if (centerFreq != _centerFreq) {
flog::debug("New center freq: {}", centerFreq);
_centerFreq = centerFreq;
onCenterFrequencyChanged(centerFreq);
}
if (samplerate != _samplerate) {
flog::debug("New samplerate: {}", samplerate);
_samplerate = samplerate;
onSamplerateChanged(samplerate);
}
// Read (and check for) record separator // Read (and check for) record separator
uint8_t rs; uint8_t rs;
int rslen = sock->recv(&rs, 1, true, 5000); int rslen = sock->recv(&rs, 1, true, 5000);
@ -72,10 +137,11 @@ void SpectranHTTPClient::worker() {
i += read; i += read;
sampLen += read; sampLen += read;
} }
int sampCount = sampLen / 8;
// Swap to stream // Swap to stream
if (streamingEnabled) { if (streamingEnabled) {
if (!stream->swap(sampLen / 8)) { return; } if (!stream->swap(sampCount)) { return; }
} }
// Read trailing CRLF // Read trailing CRLF

View File

@ -4,22 +4,35 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <utils/proto/http.h> #include <utils/proto/http.h>
#include <utils/new_event.h>
#include <stdint.h>
class SpectranHTTPClient { class SpectranHTTPClient {
public: public:
SpectranHTTPClient(std::string host, int port, dsp::stream<dsp::complex_t>* stream); SpectranHTTPClient(std::string host, int port, dsp::stream<dsp::complex_t>* stream);
void startWorker();
void streaming(bool enabled); void streaming(bool enabled);
bool isOpen(); bool isOpen();
void close(); void close();
void setCenterFrequency(uint64_t freq);
NewEvent<uint64_t> onCenterFrequencyChanged;
NewEvent<uint64_t> onSamplerateChanged;
private: private:
void worker(); void worker();
std::string host;
int port;
std::shared_ptr<net::Socket> sock; std::shared_ptr<net::Socket> sock;
net::http::Client http; net::http::Client http;
dsp::stream<dsp::complex_t>* stream; dsp::stream<dsp::complex_t>* stream;
std::thread workerThread; std::thread workerThread;
bool streamingEnabled = false; bool streamingEnabled = false;
int64_t _centerFreq = 0;
uint64_t _samplerate = 0;
}; };

View File

@ -503,7 +503,7 @@ private:
float refStep = 0.5; float refStep = 0.5;
struct SRCombo { struct SRCombo {
bool operator==(const SRCombo& b) { bool operator==(const SRCombo& b) const {
return baseId == b.baseId && decimId == b.decimId; return baseId == b.baseId && decimId == b.decimId;
} }