126 Commits
1.0.1 ... 1.0.4

Author SHA1 Message Date
f539cfad32 Fixed alrge FFT size bug 2021-10-18 00:38:02 +02:00
ab1a482352 Increased max FFT size 2021-10-18 00:06:03 +02:00
35fc973a65 new 2021-10-17 21:01:47 +02:00
db8eb47422 Merge pull request #444 from KentuckyFriedData/patch-2
Typo
2021-10-16 19:33:28 +02:00
d4795cb369 Typo 2021-10-16 09:53:08 -07:00
75249fe849 Fixed draw list size in code 2021-10-16 16:12:51 +02:00
d1aab6f25d fixed typo 2021-10-13 00:22:10 +02:00
afe13969a7 Added support for Bias-T in the bladerf_source module & added bladerf type detection 2021-10-12 19:45:42 +02:00
47e5c589bc Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2021-10-10 18:26:31 +02:00
3795e7cf23 Fixed RSP1A FM notch bug 2021-10-10 18:26:17 +02:00
d18a019abf Merge pull request #402 from corvus-ch/mac_build
Document how to build on MacOS
2021-10-10 14:13:07 +02:00
4d7b46b1a8 Sync build example with github action workflow 2021-10-09 22:22:01 +02:00
cf3e024fb9 Use Port Audio instead of RtAudio 2021-10-09 21:26:35 +02:00
8deb018684 Added RTLD_LOCAL to the dynamic linker flags FIX 2021-10-09 18:34:44 +02:00
33b9b53328 Added RTLD_LOCAL to the dynamic linker flags 2021-10-09 18:34:20 +02:00
011e7ca409 Added new theme 2021-10-08 23:24:58 +02:00
a27a46350e Merge pull request #419 from thomasgi1/memoryCorruption
renamed SpyServer source module class
2021-10-08 23:15:35 +02:00
3fd4294afa renamed SpyServer source module class AirspyHFSourceModule -> SpyServerSourceModule, so that it does not collide with the original Airspy HF source module causing massive memory corruption 2021-10-08 22:24:09 +02:00
4b2f3e4f60 Added new patrons to the credits + fixed patreon order list to be alphabetical 2021-10-05 20:57:15 +02:00
82cacf14bb Fixed text in M17 decoder 2021-10-04 05:07:11 +02:00
0fffa7d45a More cmake fixes 2021-10-04 04:00:06 +02:00
9555dda8a4 More cmake cleanup 2021-10-04 03:31:13 +02:00
44ad0a1a7e CMakeLists cleanup 2021-10-04 02:34:17 +02:00
c515ddb811 MacOS fix final (final)???? 2021-10-04 01:13:02 +02:00
ea0d905177 MacOS fix final???? 2021-10-04 00:58:16 +02:00
1be3092c7d MacOS fix 8 2021-10-03 23:58:11 +02:00
f29f77bbdc MacOS fix 7 2021-10-03 22:44:15 +02:00
91d960da3c MacOS fix 6 2021-10-03 22:11:03 +02:00
dd083b6634 MacOS fix 5 2021-10-03 21:40:39 +02:00
b4e2875acd MacOS fix 4 2021-10-03 21:07:12 +02:00
4ea9b96397 MacOS fix 3 2021-10-03 20:56:24 +02:00
8c15e78315 CI fix 2 2021-10-03 19:00:50 +02:00
94b7676ca5 CI fix 2021-10-03 18:49:56 +02:00
83fc20cacc Another potential fix for the MacOS issues 2021-10-03 17:48:32 +02:00
fcd759654c Potential fix for MacOS ci 2021-10-03 17:41:07 +02:00
73393e36c6 Changed project structure 2021-10-03 16:50:36 +02:00
c36034dbb8 Potential MacOS CI fix 2021-10-03 03:40:16 +02:00
a9d92d3a8e More fixes + debugging macos ci 2021-10-03 02:04:02 +02:00
8666aa07e7 Fixed missing const 2021-10-03 01:48:44 +02:00
a37a2d3b36 fixed warning + verbose cmake test 2021-10-03 01:41:18 +02:00
dc31539f6e Fixed compile arguments for the discord module 2021-10-03 01:29:32 +02:00
4564475821 Reworked cmake compiler arguments 2021-10-03 01:13:15 +02:00
163e35727c Fixed M17 not being enabled in the CI 2021-10-02 20:51:59 +02:00
40d21b3321 Final tweaks to windows CI for M17 decoder 2021-10-02 20:33:22 +02:00
fb9bbcb158 Fixed windows CI 2021-10-02 20:04:14 +02:00
c0801a8aed Added codec2 to the windows CI 2021-10-02 19:58:30 +02:00
dc43e76405 Document how to build on MacOS 2021-10-02 19:40:27 +02:00
8b2f019ce4 Enabled the M17 Decoder in the macos build 2021-10-02 18:06:30 +02:00
13bafa002a Changed back to pkg-config for codec2 2021-10-02 17:45:52 +02:00
90ebe373df Fixed missing .pc file in ubuntu bionic CI 2021-10-02 17:43:44 +02:00
5b7fc417ff fixed typo + switched off pkg-config for codec2 2021-10-02 17:24:23 +02:00
b4213ea049 Bugfix + added M17 decoder to the linux CI 2021-10-02 17:01:23 +02:00
26fa23c8f5 Improved feature lsit 2021-10-02 00:16:16 +02:00
c2da150d43 Added new patron to the credits 2021-09-29 08:53:15 +02:00
48f6642c70 M17 decoder bugfix 2021-09-29 08:44:03 +02:00
efd3c47a6c more progress on M17 support 2021-09-28 20:46:19 +02:00
187fc2cb9e Added M17 decoder module (unfinished) 2021-09-27 03:42:26 +02:00
c9a78d5dc6 Merge pull request #327 from arkhnchul/sdrplay-lowif
IF Mode selection for SDRPlay devices
2021-09-24 19:24:37 +02:00
b7c95de8cc Fixed network sink not being present by default 2021-09-23 20:37:34 +02:00
bc0de50ba6 Fixed missing renamed filed 2021-09-20 20:08:53 +02:00
049fc77ff6 Fixed typos 2021-09-20 19:59:35 +02:00
9ee0951874 Fixed CI typo 2021-09-20 19:03:48 +02:00
841c31daf4 Added spell check to CI 2021-09-20 19:02:53 +02:00
d9b061ef89 Fixed typo in french bandplan 2021-09-20 17:14:32 +02:00
ce48257706 Update rpi_install.sh 2021-09-19 15:10:08 +02:00
fe09a30279 Update rpi_install.sh 2021-09-19 12:18:39 +02:00
daa26e8e97 Merge branch 'AlexandreRouma:master' into sdrplay-lowif 2021-09-14 14:56:14 +03:00
318e57dc3d Fixed wonky fft resize behavior 2021-09-13 23:31:01 +02:00
b74e2d37a5 fixed dependencies in deb files 2021-09-13 20:42:13 +02:00
9d34c6a8c1 Fixed wonky center tuning behavior 2021-09-13 20:07:40 +02:00
2184e15e44 Added rtaudio to the base dependencies 2021-09-13 19:37:38 +02:00
bd427d23b3 Fixed typo in the readme + Fixed bandwidth and NFM and AM demodulators 2021-09-13 19:22:32 +02:00
5a2b0c9d79 Added new patron to the readme and fixed radio remove/disable bug 2021-09-12 19:12:23 +02:00
a208d4078e IF titles consistent with sdrplay doc 2021-09-02 03:25:09 +03:00
f56113aae4 IFBW 5000 samplerate fix 2021-08-31 21:37:44 +03:00
11c6377c7f Merge branch 'AlexandreRouma:master' into sdrplay-lowif 2021-08-31 20:48:46 +03:00
22acf33c01 UI Cleanup + Fixed waterfall zoom bug 2021-08-31 18:39:48 +02:00
ec6a258958 Removed logging when zooming 2021-08-30 19:54:43 +02:00
012903fbf4 Fixed wrong bandwidth when zooming 2021-08-30 19:22:00 +02:00
e48b5c6035 Added missing refresh button when in low IF mode 2021-08-30 16:37:29 +02:00
0a54b92c58 Merge branch 'AlexandreRouma:master' into sdrplay-lowif 2021-08-30 17:37:00 +03:00
7c1d1cad22 Switched the place of the Sample rate and IF mode combos 2021-08-30 15:53:41 +02:00
e101c1ebd7 Fixed the way the IF mode combo is displayed 2021-08-30 15:50:47 +02:00
68b3eb9b21 Merge pull request #329 from ericek111/soapy_fix
soapy_source: refresh devices on start if none in devList
2021-08-30 15:47:08 +02:00
c579aca1db Merge pull request #330 from ericek111/exp_zoom
Make the zoom slider exponential
2021-08-30 15:24:38 +02:00
c31e260425 Added error message 2021-08-30 03:07:36 +02:00
fa75a3a176 Update main.cpp 2021-08-30 03:06:40 +02:00
af59144f5b Merge pull request #328 from ericek111/master
soapy_source: always show the Refresh button
2021-08-30 03:01:42 +02:00
04ce4c3583 Make the zoom slider increase exponentially 2021-08-30 02:27:48 +02:00
ba6d9f7202 minor style change 2021-08-30 01:23:42 +02:00
49fd49b4d6 soapy_source: refresh devices on start if none in devList 2021-08-30 01:07:40 +02:00
99441ea2eb soapy_source: always show the Refresh button 2021-08-30 00:56:46 +02:00
dbd734a0b5 IF Mode selection for SDRPlay devices 2021-08-30 01:02:45 +03:00
f572d12936 Fixed macos build 2021-08-27 14:07:01 +02:00
37bf9c46c5 Enabled airspy hf+ for all MacOS automated builds 2021-08-27 13:44:45 +02:00
aa9a2a8b46 Merge pull request #309 from drfruct/macos_sdrplay
fix: CMakeList to build SDRPlay on MacOS
2021-08-26 02:26:13 +02:00
124f3368d4 MacOS SDRPlay 2021-08-25 22:50:19 +03:00
51d084c20e Fixed ubuntu 18.04 support issues 2021-08-24 19:36:13 +02:00
101674379d Added support for ubuntu 18.04 2021-08-24 19:23:08 +02:00
61bbb4a374 work towards ubuntu 18.04 compatibility 2021-08-24 18:59:45 +02:00
2d8bbef12a Merge pull request #300 from NickEngmann/master
Fix Wrong Source Branch within BladeRF build
2021-08-23 14:59:55 +02:00
4109ea73b3 Merge pull request #1 from NickEngmann/fix-bladeRF-build-patch
fix:wrong source branch mentioned
2021-08-23 08:52:06 -04:00
a7d8ec8a2d fix:wrong source branch mentioned 2021-08-23 08:51:07 -04:00
6036300ecb Added missing people to the credits 2021-08-22 17:18:15 +02:00
9cf3ba79f3 Merge pull request #297 from shuyuan-liu/master
Add bandplan for (mainland) China
2021-08-22 17:11:18 +02:00
dcda74c0cf Merge branch 'AlexandreRouma:master' into master 2021-08-22 22:38:09 +08:00
4f16c6bf90 Correct freqs of 2200m ham band 2021-08-22 20:04:05 +08:00
73daca9116 Add the rest of china.json
Amateur bands above 100GHz are omitted.
2021-08-22 19:59:51 +08:00
2fd91459fd Fixed bug 2021-08-21 23:13:38 +02:00
8502bae236 Added basic dependencies to the debian/ubuntu packages 2021-08-21 18:03:34 +02:00
f01cb4af9f Added support for OpenGL 2.1 and OpenGL ES 3.0 with no modifications to the code necessary by the user 2021-08-21 17:47:55 +02:00
53cb328c2c Merge pull request #289 from 3cky/set-reuseaddr-listen-sock
Set SO_REUSEADDR option to listen sockets on non-Windows systems
2021-08-21 13:42:54 +02:00
9a2794a0dd Set SO_REUSEADDR option to listen sockets on non-Windows systems 2021-08-21 09:45:46 +03:00
c84371ba52 Merge pull request #288 from 3cky/fix-rigctl-wsjtx
Fix rigctl server to work with WSJT-X
2021-08-20 23:51:57 +02:00
dbf1fb790f Changed back to PR 2021-08-20 21:45:07 +02:00
271d1f9240 Update main.cpp 2021-08-20 20:41:54 +02:00
b2ce47d975 Fixed radio bandwidth not adjusted when the menu is closed or hidden 2021-08-20 20:40:14 +02:00
6ab59ad3c5 Fix rigctl server to work with WSJT-X 2021-08-20 20:38:05 +03:00
3516baa042 Create partial bandplan for (mainland) China 2021-08-20 23:10:26 +08:00
1465fb784f Merge pull request #276 from ge0metrix/master
Updated US Bandplan to add FRS/GMRS
2021-08-20 14:48:46 +02:00
42918908a0 Updated US Bandplan to add FRS/GMRS 2021-08-19 13:15:35 -04:00
9f0e050d1b Started work for older system support 2021-08-16 18:49:00 +02:00
dcc17cdee7 Fixed LimeSDR + Fixed FFTSize bug + added ppm option to RTL-SDR & RTL-TCP + Fixed RTL-TCP not saving settings 2021-08-10 00:54:00 +02:00
9eb3ef0500 Fixed rigctl server not starting on AOS 2021-08-08 21:38:15 +02:00
ffc20f5fee Added new patron 2021-08-08 21:38:15 +02:00
6144f3a3f9 Update readme.md 2021-08-04 16:46:32 +02:00
351 changed files with 13056 additions and 1212 deletions

View File

@ -35,19 +35,34 @@ jobs:
- name: Install SDRPlay API
run: 7z x ${{runner.workspace}}/SDRPlay.zip -o"C:/Program Files/"
- name: Download codec2
run: git clone https://github.com/drowe67/codec2
- name: Prepare MinGW
run: C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -c "pacman --noconfirm -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja"
- name: Prepare build for codec2
run: cd codec2 ; mkdir build ; cd build ; C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -c "cmake .. -DCMAKE_GNUtoMS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS=-static-libgcc"
- name: Build codec2
run: cd codec2/build ; C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -c "ninja"
- name: Install 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
run: vcpkg install fftw3:x64-windows glew:x64-windows glfw3:x64-windows portaudio:x64-windows
- name: Install rtaudio
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; 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: Prepare CMake
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
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
- name: Build
working-directory: ${{runner.workspace}}/build
run: cmake --build . --config Release
run: cmake --build . --config Release --verbose
- name: Create Archive
working-directory: ${{runner.workspace}}
@ -68,16 +83,19 @@ jobs:
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Update brew repositories
run: brew update
- name: Install dependencies
run: brew install fftw glew glfw volk airspy portaudio hackrf rtl-sdr libbladerf
run: brew install fftw glew glfw volk airspy airspyhf portaudio hackrf rtl-sdr libbladerf codec2
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_AIRSPYHF_SOURCE=OFF -DOPT_BUILD_PLUTOSDR_SOURCE=OFF -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=OFF -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
- name: Build
working-directory: ${{runner.workspace}}/build
run: make -j3
run: make VERBOSE=1 -j3
- name: Create Archive
working-directory: ${{runner.workspace}}
@ -155,6 +173,28 @@ jobs:
name: sdrpp_debian_sid_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_bionic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- 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@v2
with:
name: sdrpp_ubuntu_bionic_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_focal:
runs-on: ubuntu-latest
@ -222,7 +262,7 @@ jobs:
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
create_full_archive:
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_groovy', 'build_ubuntu_hirsute']
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_sid', 'build_ubuntu_bionic', 'build_ubuntu_focal', 'build_ubuntu_groovy', 'build_ubuntu_hirsute']
runs-on: ubuntu-latest
steps:
@ -237,6 +277,7 @@ jobs:
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_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
mv sdrpp_ubuntu_bionic_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_bionic_amd64.deb &&
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
mv sdrpp_ubuntu_groovy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_groovy_amd64.deb &&
mv sdrpp_ubuntu_hirsute_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_hirsute_amd64.deb
@ -245,3 +286,15 @@ jobs:
with:
name: sdrpp_all
path: sdrpp_all/
check_spelling:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install codespell
run: sudo apt update -y && sudo apt install -y codespell
- name: Running codespell
run: cd $GITHUB_WORKSPACE && codespell -q 2 || true

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ root_dev/
Folder.DotSettings.user
CMakeSettings.json
poggers_decoder
m17_decoder/libcorrect

View File

@ -7,29 +7,33 @@ else()
set(CMAKE_INSTALL_PREFIX "/usr")
endif()
# Compatibility Options
option(OPT_OVERRIDE_STD_FILESYSTEM "Use a local version of std::filesystem on systems that don't have it yet" OFF)
# Sources
option(OPT_BUILD_AIRSPY_SOURCE "Build Airspy Source Module (Depedencies: libairspy)" ON)
option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Depedencies: libairspyhf)" ON)
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Depedencies: libbladeRF)" OFF)
option(OPT_BUILD_AIRSPY_SOURCE "Build Airspy Source Module (Dependencies: libairspy)" ON)
option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Dependencies: libairspyhf)" ON)
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF)
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" ON)
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Depedencies: liblimesuite)" OFF)
option(OPT_BUILD_SDDC_SOURCE "Build SDDC Source Module (Depedencies: libusb-1.0)" OFF)
option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Depedencies: librtlsdr)" ON)
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
option(OPT_BUILD_SDDC_SOURCE "Build SDDC Source Module (Dependencies: libusb-1.0)" OFF)
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_SDRPLAY_SOURCE "Build SDRplay Source Module (Depedencies: libsdrplay)" OFF)
option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Depedencies: soapysdr)" ON)
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_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON)
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON)
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON)
# Sinks
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: rtaudio)" ON)
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Depedencies: portaudio)" OFF)
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_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Depedencies: portaudio)" OFF)
option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Dependencies: portaudio)" OFF)
# Decoders
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (no dependencies required)" OFF)
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" ON)
@ -40,130 +44,133 @@ option(OPT_BUILD_FREQUENCY_MANAGER "Build the Frequency Manager module" ON)
option(OPT_BUILD_RECORDER "Audio and baseband recorder" ON)
option(OPT_BUILD_RIGCTL_SERVER "Rigctl backend for controlling SDR++ with software like gpredict" ON)
# Compiler arguments for each platform
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
else ()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif ()
# Core of SDR++
add_subdirectory("core")
# Source modules
if (OPT_BUILD_AIRSPY_SOURCE)
add_subdirectory("airspy_source")
add_subdirectory("source_modules/airspy_source")
endif (OPT_BUILD_AIRSPY_SOURCE)
if (OPT_BUILD_AIRSPYHF_SOURCE)
add_subdirectory("airspyhf_source")
add_subdirectory("source_modules/airspyhf_source")
endif (OPT_BUILD_AIRSPYHF_SOURCE)
if (OPT_BUILD_BLADERF_SOURCE)
add_subdirectory("bladerf_source")
add_subdirectory("source_modules/bladerf_source")
endif (OPT_BUILD_BLADERF_SOURCE)
if (OPT_BUILD_FILE_SOURCE)
add_subdirectory("file_source")
add_subdirectory("source_modules/file_source")
endif (OPT_BUILD_FILE_SOURCE)
if (OPT_BUILD_HACKRF_SOURCE)
add_subdirectory("hackrf_source")
add_subdirectory("source_modules/hackrf_source")
endif (OPT_BUILD_HACKRF_SOURCE)
if (OPT_BUILD_LIMESDR_SOURCE)
add_subdirectory("limesdr_source")
add_subdirectory("source_modules/limesdr_source")
endif (OPT_BUILD_LIMESDR_SOURCE)
if (OPT_BUILD_SDDC_SOURCE)
add_subdirectory("sddc_source")
add_subdirectory("source_modules/sddc_source")
endif (OPT_BUILD_SDDC_SOURCE)
if (OPT_BUILD_RTL_SDR_SOURCE)
add_subdirectory("rtl_sdr_source")
add_subdirectory("source_modules/rtl_sdr_source")
endif (OPT_BUILD_RTL_SDR_SOURCE)
if (OPT_BUILD_RTL_TCP_SOURCE)
add_subdirectory("rtl_tcp_source")
add_subdirectory("source_modules/rtl_tcp_source")
endif (OPT_BUILD_RTL_TCP_SOURCE)
if (OPT_BUILD_SDRPLAY_SOURCE)
add_subdirectory("sdrplay_source")
add_subdirectory("source_modules/sdrplay_source")
endif (OPT_BUILD_SDRPLAY_SOURCE)
if (OPT_BUILD_SOAPY_SOURCE)
add_subdirectory("soapy_source")
add_subdirectory("source_modules/soapy_source")
endif (OPT_BUILD_SOAPY_SOURCE)
if (OPT_BUILD_SPYSERVER_SOURCE)
add_subdirectory("spyserver_source")
add_subdirectory("source_modules/spyserver_source")
endif (OPT_BUILD_SPYSERVER_SOURCE)
if (OPT_BUILD_PLUTOSDR_SOURCE)
add_subdirectory("plutosdr_source")
add_subdirectory("source_modules/plutosdr_source")
endif (OPT_BUILD_PLUTOSDR_SOURCE)
# Sink modules
if (OPT_BUILD_AUDIO_SINK)
add_subdirectory("audio_sink")
add_subdirectory("sink_modules/audio_sink")
endif (OPT_BUILD_AUDIO_SINK)
if (OPT_BUILD_PORTAUDIO_SINK)
add_subdirectory("portaudio_sink")
add_subdirectory("sink_modules/portaudio_sink")
endif (OPT_BUILD_PORTAUDIO_SINK)
if (OPT_BUILD_NETWORK_SINK)
add_subdirectory("network_sink")
add_subdirectory("sink_modules/network_sink")
endif (OPT_BUILD_NETWORK_SINK)
if (OPT_BUILD_NEW_PORTAUDIO_SINK)
add_subdirectory("new_portaudio_sink")
add_subdirectory("sink_modules/new_portaudio_sink")
endif (OPT_BUILD_NEW_PORTAUDIO_SINK)
# Decoders
if (OPT_BUILD_FALCON9_DECODER)
add_subdirectory("falcon9_decoder")
add_subdirectory("decoder_modules/falcon9_decoder")
endif (OPT_BUILD_FALCON9_DECODER)
if (OPT_BUILD_M17_DECODER)
add_subdirectory("decoder_modules/m17_decoder")
endif (OPT_BUILD_M17_DECODER)
if (OPT_BUILD_METEOR_DEMODULATOR)
add_subdirectory("meteor_demodulator")
add_subdirectory("decoder_modules/meteor_demodulator")
endif (OPT_BUILD_METEOR_DEMODULATOR)
if (OPT_BUILD_RADIO)
add_subdirectory("radio")
add_subdirectory("decoder_modules/radio")
endif (OPT_BUILD_RADIO)
if (OPT_BUILD_WEATHER_SAT_DECODER)
add_subdirectory("weather_sat_decoder")
add_subdirectory("decoder_modules/weather_sat_decoder")
endif (OPT_BUILD_WEATHER_SAT_DECODER)
# Misc
if (OPT_BUILD_DISCORD_PRESENCE)
add_subdirectory("discord_integration")
add_subdirectory("misc_modules/discord_integration")
endif (OPT_BUILD_DISCORD_PRESENCE)
if (OPT_BUILD_FREQUENCY_MANAGER)
add_subdirectory("frequency_manager")
add_subdirectory("misc_modules/frequency_manager")
endif (OPT_BUILD_FREQUENCY_MANAGER)
if (OPT_BUILD_RECORDER)
add_subdirectory("recorder")
add_subdirectory("misc_modules/recorder")
endif (OPT_BUILD_RECORDER)
if (OPT_BUILD_RIGCTL_SERVER)
add_subdirectory("rigctl_server")
add_subdirectory("misc_modules/rigctl_server")
endif (OPT_BUILD_RIGCTL_SERVER)
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
target_link_libraries(sdrpp PRIVATE sdrpp_core)
# Compiler arguments for each platform
if (MSVC)
target_compile_options(sdrpp PRIVATE /O2 /Ob2 /std:c++17 /EHsc)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(sdrpp PRIVATE -O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup)
else ()
target_compile_options(sdrpp PRIVATE -O3 -std=c++17)
endif ()
# Copy dynamic libs over
if (MSVC)
add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
@ -188,7 +195,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.dylib\" \"$<TARGET_FILE_DIR:sdrpp>\")
endif ()
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_M17_DECODER=ON
# Install directives
install(TARGETS sdrpp DESTINATION bin)

View File

@ -1,42 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(airspy_source)
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
else ()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif ()
include_directories("src/")
file(GLOB SRC "src/*.cpp")
add_library(airspy_source SHARED ${SRC})
target_link_libraries(airspy_source PRIVATE sdrpp_core)
set_target_properties(airspy_source PROPERTIES PREFIX "")
if (MSVC)
# Lib path
target_link_directories(airspy_source PUBLIC "C:/Program Files/PothosSDR/bin/")
target_link_libraries(airspy_source PUBLIC airspy)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBAIRSPY REQUIRED libairspy)
target_include_directories(airspy_source PUBLIC ${LIBAIRSPY_INCLUDE_DIRS})
target_link_directories(airspy_source PUBLIC ${LIBAIRSPY_LIBRARY_DIRS})
target_link_libraries(airspy_source PUBLIC ${LIBAIRSPY_LIBRARIES})
# Include it because for some reason pkgconfig doesn't look here?
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_include_directories(airspy_source PUBLIC "/usr/local/include")
endif()
endif ()
# Install directives
install(TARGETS airspy_source DESTINATION lib/sdrpp/plugins)

View File

@ -1,42 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(airspyhf_source)
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
else ()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif ()
include_directories("src/")
file(GLOB SRC "src/*.cpp")
add_library(airspyhf_source SHARED ${SRC})
target_link_libraries(airspyhf_source PRIVATE sdrpp_core)
set_target_properties(airspyhf_source PROPERTIES PREFIX "")
if (MSVC)
# Lib path
target_link_directories(airspyhf_source PUBLIC "C:/Program Files/PothosSDR/bin/")
target_link_libraries(airspyhf_source PUBLIC airspyhf)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBAIRSPYHF REQUIRED libairspyhf)
target_include_directories(airspyhf_source PUBLIC ${LIBAIRSPYHF_INCLUDE_DIRS})
target_link_directories(airspyhf_source PUBLIC ${LIBAIRSPYHF_LIBRARY_DIRS})
target_link_libraries(airspyhf_source PUBLIC ${LIBAIRSPYHF_LIBRARIES})
# Include it because for some reason pkgconfig doesn't look here?
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_include_directories(airspyhf_source PUBLIC "/usr/local/include")
endif()
endif ()
# Install directives
install(TARGETS airspyhf_source DESTINATION lib/sdrpp/plugins)

View File

@ -1,40 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(audio_sink)
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
else ()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif ()
file(GLOB SRC "src/*.cpp")
include_directories("src/")
add_library(audio_sink SHARED ${SRC})
target_link_libraries(audio_sink PRIVATE sdrpp_core)
set_target_properties(audio_sink PROPERTIES PREFIX "")
if (MSVC)
# Lib path
target_link_directories(audio_sink PUBLIC "C:/Program Files (x86)/RtAudio/lib")
# Misc headers
target_include_directories(audio_sink PUBLIC "C:/Program Files (x86)/RtAudio/include/rtaudio")
target_link_libraries(audio_sink PUBLIC rtaudio)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(RTAUDIO REQUIRED rtaudio)
target_include_directories(audio_sink PUBLIC ${RTAUDIO_INCLUDE_DIRS})
target_link_directories(audio_sink PUBLIC ${RTAUDIO_LIBRARY_DIRS})
target_link_libraries(audio_sink PUBLIC ${RTAUDIO_LIBRARIES})
endif ()
# Install directives
install(TARGETS audio_sink DESTINATION lib/sdrpp/plugins)

View File

@ -1,36 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(bladerf_source)
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
else ()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif ()
include_directories("src/")
file(GLOB SRC "src/*.cpp")
add_library(bladerf_source SHARED ${SRC})
target_link_libraries(bladerf_source PRIVATE sdrpp_core)
set_target_properties(bladerf_source PROPERTIES PREFIX "")
if (MSVC)
# Lib path
target_link_directories(bladerf_source PUBLIC "C:/Program Files/PothosSDR/bin/")
target_link_libraries(bladerf_source PUBLIC bladeRF)
else (MSVC)
# Not in pkg-config
target_link_libraries(bladerf_source PUBLIC bladeRF)
# Include it because for some reason pkgconfig doesn't look here?
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_include_directories(airspyhf_source PUBLIC "/usr/local/include")
endif()
endif ()
# Install directives
install(TARGETS bladerf_source DESTINATION lib/sdrpp/plugins)

View File

@ -56,7 +56,7 @@ If the module meets the code quality requirements, it may be added to the offici
# JSON Formatting
The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSOn files is important for reference and readability. The folowing guides will show you how to properly format the JSON files for their respective uses.
The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSOn files is important for reference and readability. The following guides will show you how to properly format the JSON files for their respective uses.
**IMPORTANT: JSON File cannot contain comments, there are only in this example for clarity**
@ -98,7 +98,7 @@ Please follow this guide to properly format the JSON files for custom color maps
"name": "Short name (has to fit in the menu)",
"author": "Name of the original/main creator of the color map",
"map": [
// These are the color codes, in hexidecimal (#RRGGBB) format, for the custom color scales for the waterfall. They must be entered as strings, not integers, with the hastag/pound-symbol proceeding the 6 digit number.
// These are the color codes, in hexadecimal (#RRGGBB) format, for the custom color scales for the waterfall. They must be entered as strings, not integers, with the hastag/pound-symbol proceeding the 6 digit number.
"#000020",
"#000030",
"#000050",

View File

@ -1,23 +1,29 @@
cmake_minimum_required(VERSION 3.13)
project(sdrpp_core)
# Set compiler options
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
else ()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif ()
add_definitions(-DSDRPP_IS_CORE)
add_subdirectory("libcorrect/")
# Main code
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
add_definitions(-DSDRPP_IS_CORE)
if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif ()
# Add code to dyn lib
add_library(sdrpp_core SHARED ${SRC})
# Set compiler options
if (MSVC)
target_compile_options(sdrpp_core PRIVATE /O2 /Ob2 /std:c++17 /EHsc)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(sdrpp_core PRIVATE -O3 -std=c++17)
else ()
target_compile_options(sdrpp_core PRIVATE -O3 -std=c++17)
endif ()
# Set the install prefix
target_compile_definitions(sdrpp_core PUBLIC INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}")
@ -25,6 +31,13 @@ target_compile_definitions(sdrpp_core PUBLIC INSTALL_PREFIX="${CMAKE_INSTALL_PRE
target_include_directories(sdrpp_core PUBLIC "src/")
target_include_directories(sdrpp_core PUBLIC "src/imgui")
# Link to linkcorrect
target_include_directories(sdrpp_core PUBLIC "libcorrect/include")
target_link_libraries(sdrpp_core PUBLIC correct_static)
if (OPT_OVERRIDE_STD_FILESYSTEM)
target_include_directories(sdrpp_core PUBLIC "std_replacement")
endif (OPT_OVERRIDE_STD_FILESYSTEM)
if (MSVC)
# Lib path
@ -86,16 +99,11 @@ else()
target_link_libraries(sdrpp_core PUBLIC stdc++fs)
endif ()
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
endif ()
endif ()
set(CORE_FILES ${RUNTIME_OUTPUT_DIRECTORY} PARENT_SCOPE)
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -G "Visual Studio 15 2017 Win64"
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake"
# Install directives
install(TARGETS sdrpp_core DESTINATION lib)

View File

@ -0,0 +1,47 @@
@echo on
if NOT EXIST C:\projects\tools (
mkdir C:\projects\tools
)
cd C:\projects\tools
::###########################################################################
:: Setup Compiler
::###########################################################################
if NOT EXIST llvm-installer.exe (
appveyor DownloadFile http://prereleases.llvm.org/win-snapshots/LLVM-5.0.0-r306282-win32.exe -FileName llvm-installer.exe
)
START /WAIT llvm-installer.exe /S /D=C:\"projects\tools\LLVM-install"
@set PATH="C:\projects\tools\LLVM-install\bin";%PATH%
clang-cl -v
if DEFINED MINGW_PATH rename "C:\Program Files\Git\usr\bin\sh.exe" "sh-ignored.exe"
if DEFINED MINGW_PATH @set "PATH=%PATH:C:\Program Files (x86)\Git\bin=%"
if DEFINED MINGW_PATH @set "PATH=%PATH%;%MINGW_PATH%"
if DEFINED MINGW_PATH g++ -v
::###########################################################################
:: Install a recent CMake
::###########################################################################
if NOT EXIST cmake (
appveyor DownloadFile https://cmake.org/files/v3.7/cmake-3.7.2-win64-x64.zip -FileName cmake.zip
7z x cmake.zip -oC:\projects\tools > nul
move C:\projects\tools\cmake-* C:\projects\tools\cmake
rm cmake.zip
)
@set PATH=C:\projects\tools\cmake\bin;%PATH%
cmake --version
::###########################################################################
:: Install Ninja
::###########################################################################
if NOT EXIST ninja (
appveyor DownloadFile https://github.com/ninja-build/ninja/releases/download/v1.6.0/ninja-win.zip -FileName ninja.zip
7z x ninja.zip -oC:\projects\tools\ninja > nul
rm ninja.zip
)
@set PATH=C:\projects\tools\ninja;%PATH%
ninja --version
@echo off

1
core/libcorrect/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

View File

@ -0,0 +1,12 @@
language: c
matrix:
include:
- os: linux
dist: trusty
- os: osx
script:
- mkdir build
- cd build
- cmake ..
- make shim
- make check CTEST_OUTPUT_ON_FAILURE=TRUE

View File

@ -0,0 +1,102 @@
cmake_minimum_required(VERSION 2.8)
project(Correct C)
include(CheckLibraryExists)
include(CheckIncludeFiles)
include(CheckCSourceCompiles)
include(CMakePushCheckState)
include(CheckCCompilerFlag)
if(MSVC)
set(LIBM "")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4")
else(MSVC)
set(LIBM "m")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -std=c99 -Wall")
check_c_compiler_flag(-Wpedantic COMPILER_SUPPORTS_WPEDANTIC)
if(COMPILER_SUPPORTS_WPEDANTIC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3 -O0 -fsanitize=address")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-no_pie,")
else()
if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Profiling")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -g3")
endif()
endif()
endif(MSVC)
find_library(FEC fec)
CHECK_LIBRARY_EXISTS(FEC dotprod "" HAVE_LIBFEC)
if(NOT CMAKE_CROSSCOMPILING)
# Check if host machine can compile with SSE 4.1 intrinsic
cmake_push_check_state(RESET)
set(CMAKE_REQUIRED_DEFINITIONS -march=native)
check_c_source_compiles("
#include <x86intrin.h>
int main() {
__m128i a;
__m128i b;
__m128i c = _mm_min_epu16(a, b);
return 0;
}" HAVE_SSE)
cmake_pop_check_state()
endif()
if(HAVE_SSE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.1")
endif()
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
include_directories(${PROJECT_SOURCE_DIR}/include)
add_subdirectory(src)
set(INSTALL_HEADERS "${PROJECT_BINARY_DIR}/include/correct.h")
add_custom_target(correct-h ALL COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/include/correct.h ${PROJECT_BINARY_DIR}/include/correct.h)
if(HAVE_SSE)
set(correct_obj_files $<TARGET_OBJECTS:correct-reed-solomon> $<TARGET_OBJECTS:correct-convolutional> $<TARGET_OBJECTS:correct-convolutional-sse>)
set(INSTALL_HEADERS ${INSTALL_HEADERS} ${PROJECT_BINARY_DIR}/include/correct-sse.h)
add_custom_target(correct-sse-h ALL COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/include/correct-sse.h ${PROJECT_BINARY_DIR}/include/correct-sse.h)
else()
set(correct_obj_files $<TARGET_OBJECTS:correct-reed-solomon> $<TARGET_OBJECTS:correct-convolutional>)
endif()
add_library(correct SHARED ${correct_obj_files})
add_library(correct_static STATIC ${correct_obj_files})
set_target_properties(correct_static PROPERTIES OUTPUT_NAME "correct")
if(HAVE_SSE)
target_compile_definitions(correct PUBLIC HAVE_SSE=1)
target_compile_definitions(correct_static PUBLIC HAVE_SSE=1)
endif()
add_subdirectory(util)
add_subdirectory(tests)
add_subdirectory(tools)
# add_subdirectory(benchmarks)
install(TARGETS correct correct_static
DESTINATION lib)
install(FILES ${INSTALL_HEADERS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include")
add_library(fec_shim_static EXCLUDE_FROM_ALL src/fec_shim.c ${correct_obj_files})
set_target_properties(fec_shim_static PROPERTIES OUTPUT_NAME "fec")
add_library(fec_shim_shared SHARED EXCLUDE_FROM_ALL src/fec_shim.c ${correct_obj_files})
set_target_properties(fec_shim_shared PROPERTIES OUTPUT_NAME "fec")
add_custom_target(fec-shim-h COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/include/fec_shim.h ${PROJECT_BINARY_DIR}/include/fec.h)
add_custom_target(shim DEPENDS fec_shim_static fec_shim_shared fec-shim-h)
install(TARGETS fec_shim_static fec_shim_shared
DESTINATION lib
OPTIONAL)
install(FILES ${PROJECT_BINARY_DIR}/include/fec.h DESTINATION "${CMAKE_INSTALL_PREFIX}/include" OPTIONAL)

12
core/libcorrect/LICENSE Normal file
View File

@ -0,0 +1,12 @@
Copyright (c) 2016, Brian Armstrong
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

38
core/libcorrect/README.md Normal file
View File

@ -0,0 +1,38 @@
[libcorrect](https://github.com/quiet/libcorrect)
===========
[![OSX/Linux Build Status](https://travis-ci.org/quiet/libcorrect.svg?branch=master)](https://travis-ci.org/quiet/libcorrect)
[![Windows Build status](https://ci.appveyor.com/api/projects/status/i3e84jmj00fa5my8/branch/master?svg=true)](https://ci.appveyor.com/project/brian-armstrong/libcorrect/branch/master)
libcorrect is a library for Forward Error Correction. By using libcorrect, you can encode extra redundancy into a packet of data and then send it across a lossy channel. When the packet is received, it can be decoded to recover the original, pre-encoded data.
libcorrect accomplishes this task with two algorithms, [Convolutional codes](https://en.wikipedia.org/wiki/Convolutional_code) and [Reed-Solomon](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction). Convolutional codes are robust to a constant background noise, while Reed-Solomon error correction is effective at dealing with noise that occurs in bursts. These algorithms have played an important role in [telecommunications](https://en.wikipedia.org/wiki/Error_detection_and_correction#Deep-space_telecommunications). libcorrect uses a [Viterbi algorithm](https://en.wikipedia.org/wiki/Viterbi_algorithm) decoder to decode convolutional codes.
libcorrect is a performant, BSD-licensed library. It is also the author's hope that this library's contents could help others learn how its algorithms work.
Design goals
-----------
1. libcorrect should be a drop-in, BSD-licensed substitute for [libfec](http://www.ka9q.net/code/fec/), which offers similar functionality under the LGPL-license. Although libfec is a fantastic library, the state of LGPL-licensed libraries on mobile devices is somewhat uncertain. For this reason, libcorrect is a completely new approach under the BSD license which supports the same algorithms as libfec. Additionally, libcorrect can be built with a compatibility layer so that libcorrect can be linked in place of libfec.
Achieving this goal gives [libquiet](https://github.com/quiet/quiet) a fully BSD-/MIT-licensed set of dependencies, which gives libquiet more flexibility in mobile applications.
2. libcorrect should make it easier to investigate how forward error correction works. To accomplish this, libcorrect provides tools to test the fitness of convolutional codes and their polynomials. Additionally, libcorrect should be written in a way that leads to easy understanding of these powerful algorithms. This library's roadmap includes more documentation on how these algorithms work and how to increase their computational performance.
3. libcorrect should explore further into error correction. This goal would help libquiet operate in noisier situations. One approach might be to use parts of libcorrect's Viterbi Algorithm decoder to create a [Turbo code](https://en.wikipedia.org/wiki/Turbo_code) decoder, although this is just an idea and may turn out to be prohibitively difficult.
Build
-----------
libcorrect uses CMake, which allows for out-of-source builds. To get started, make sure that you have CMake installed, and then, from libcorrect's source directory, run `mkdir build && cd build && cmake .. && make && make install`. Additionally, if you would like the libfec compatibility layer, you can run `make shim && make install`, though do be cautioned that this can overwrite an existing installation of libfec.
If you are on a host which has `<x86intrin.h>` available, then libcorrect will automatically build its SSE version as well. The SSE headers are provided under `<correct-sse.h>`. For now, it is on the caller of this code to ensure that SSE is available and can be used. libcorrect requires SSE functions up to and including SSE4.
If you have any questions or problems with libcorrect, do not hesitate to open an issue.
-----------
I'd like to thank Ryan Hitchman and Josh Gao for all of their help and rubber ducking.
A huge thank you goes to [Lucas Teske](https://github.com/racerxdl) for finding all the ways that libcorrect was broken on Windows and to [Denis Golovan](https://github.com/MageSlayer) for finding an error in the returned length of the convolutional code decoder.

View File

@ -0,0 +1,43 @@
version: '{build}'
build:
verbosity: detailed
branches:
only:
- master
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
COMPILER: cl.exe
MSVC_BAT: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
MSVC_BAT_ARCH: x86
GENERATOR: "Visual Studio 14 2015 Win64"
APPVEYOR_SAVE_CACHE_ON_ERROR: true
DLL_PATH: lib\Release\fec.dll
install:
- call "%APPVEYOR_BUILD_FOLDER%\\.appveyor-install-tools.cmd"
before_build:
- if DEFINED MSVC_BAT call "%MSVC_BAT%" %MSVC_BAT_ARCH%
- cd %APPVEYOR_BUILD_FOLDER%
build_script:
- mkdir build
- cd build
- cmake -G "%GENERATOR%" -DCMAKE_C_COMPILER=%COMPILER% -DCMAKE_CXX_COMPILER=%COMPILER% -DCMAKE_BUILD_TYPE=Release ..
- cmake --build . --config Release --target shim
- cmake --build . --config Release --target test_runners
- dumpbin /EXPORTS %DLL_PATH%
test_script:
- cd tests
- set CTEST_OUTPUT_ON_FAILURE=1
- ctest -C Release
cache:
- C:\projects\tools\ninja
- C:\projects\tools\cmake
- C:\projects\tools\llvm-installer.exe

View File

@ -0,0 +1,2 @@
add_subdirectory(convolutional)
add_subdirectory(reed-solomon)

View File

@ -0,0 +1,54 @@
include_directories("include")
add_executable(convolutional_test_runner EXCLUDE_FROM_ALL convolutional.c $<TARGET_OBJECTS:error_sim>)
target_link_libraries(convolutional_test_runner correct_static "${LIBM}")
set_target_properties(convolutional_test_runner PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
add_test(NAME convolutional_test WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" COMMAND convolutional_test_runner)
set(all_test_runners ${all_test_runners} convolutional_test_runner)
if(HAVE_SSE)
add_executable(convolutional_sse_test_runner EXCLUDE_FROM_ALL convolutional-sse.c $<TARGET_OBJECTS:error_sim_sse>)
target_link_libraries(convolutional_sse_test_runner correct_static "${LIBM}")
set_target_properties(convolutional_sse_test_runner PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
add_test(NAME convolutional_sse_test WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" COMMAND convolutional_sse_test_runner)
set(all_test_runners ${all_test_runners} convolutional_sse_test_runner)
endif()
if(HAVE_LIBFEC)
add_executable(convolutional_fec_test_runner EXCLUDE_FROM_ALL convolutional-fec.c $<TARGET_OBJECTS:error_sim_fec>)
target_link_libraries(convolutional_fec_test_runner correct_static FEC "${LIBM}")
set_target_properties(convolutional_fec_test_runner PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
add_test(NAME convolutional_fec_test WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" COMMAND convolutional_fec_test_runner)
set(all_test_runners ${all_test_runners} convolutional_fec_test_runner)
endif()
add_executable(convolutional_shim_test_runner EXCLUDE_FROM_ALL convolutional-shim.c $<TARGET_OBJECTS:error_sim_shim>)
target_link_libraries(convolutional_shim_test_runner correct_static fec_shim_static "${LIBM}")
set_target_properties(convolutional_shim_test_runner PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
add_test(NAME convolutional_shim_test WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" COMMAND convolutional_shim_test_runner)
set(all_test_runners ${all_test_runners} convolutional_shim_test_runner)
add_executable(reed_solomon_test_runner EXCLUDE_FROM_ALL reed-solomon.c rs_tester.c)
target_link_libraries(reed_solomon_test_runner correct_static "${LIBM}")
set_target_properties(reed_solomon_test_runner PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
add_test(NAME reed_solomon_test WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" COMMAND reed_solomon_test_runner)
set(all_test_runners ${all_test_runners} reed_solomon_test_runner)
if(HAVE_LIBFEC)
add_executable(reed_solomon_interop_test_runner EXCLUDE_FROM_ALL reed-solomon-fec-interop.c rs_tester.c rs_tester_fec.c)
target_link_libraries(reed_solomon_interop_test_runner correct_static FEC "${LIBM}")
set_target_properties(reed_solomon_interop_test_runner PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
add_test(NAME reed_solomon_interop_test WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" COMMAND reed_solomon_interop_test_runner)
set(all_test_runners ${all_test_runners} reed_solomon_interop_test_runner)
endif()
add_executable(reed_solomon_shim_interop_test_runner EXCLUDE_FROM_ALL reed-solomon-shim-interop.c rs_tester.c rs_tester_fec_shim.c)
target_link_libraries(reed_solomon_shim_interop_test_runner correct_static fec_shim_static "${LIBM}")
set_target_properties(reed_solomon_shim_interop_test_runner PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
add_test(NAME reed_solomon_shim_interop_test WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" COMMAND reed_solomon_shim_interop_test_runner)
set(all_test_runners ${all_test_runners} reed_solomon_shim_interop_test_runner)
add_custom_target(test_runners DEPENDS ${all_test_runners})
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS test_runners)
enable_testing()

View File

@ -0,0 +1,123 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fec.h>
#include "correct.h"
#include "correct/util/error-sim-fec.h"
size_t max_block_len = 4096;
size_t test_conv(correct_convolutional *conv, void *fec,
void (*decode)(void *, uint8_t *, size_t, uint8_t *),
conv_testbench **testbench_ptr, size_t msg_len, double eb_n0,
double bpsk_bit_energy, double bpsk_voltage) {
uint8_t *msg = malloc(max_block_len);
size_t num_errors = 0;
while (msg_len) {
size_t block_len = (max_block_len < msg_len) ? max_block_len : msg_len;
msg_len -= block_len;
for (unsigned int j = 0; j < block_len; j++) {
msg[j] = rand() % 256;
}
*testbench_ptr =
resize_conv_testbench(*testbench_ptr, conv_correct_enclen, conv, block_len);
conv_testbench *testbench = *testbench_ptr;
testbench->encoder = conv;
testbench->encode = conv_correct_encode;
testbench->decoder = fec;
testbench->decode = decode;
build_white_noise(testbench->noise, testbench->enclen, eb_n0, bpsk_bit_energy);
num_errors += test_conv_noise(testbench, msg, block_len, bpsk_voltage);
}
free(msg);
return num_errors;
}
void assert_test_result(correct_convolutional *conv, void *fec,
void (*decode)(void *, uint8_t *, size_t, uint8_t *),
conv_testbench **testbench, size_t test_length, size_t rate, size_t order,
double eb_n0, double error_rate) {
double bpsk_voltage = 1.0 / sqrt(2.0);
double bpsk_sym_energy = 2 * pow(bpsk_voltage, 2.0);
double bpsk_bit_energy = bpsk_sym_energy * rate;
size_t error_count =
test_conv(conv, fec, decode, testbench, test_length, eb_n0, bpsk_bit_energy, bpsk_voltage);
double observed_error_rate = error_count / ((double)test_length * 8);
if (observed_error_rate > error_rate) {
printf(
"test failed, expected error rate=%.2e, observed error rate=%.2e @%.1fdB for rate %zu "
"order %zu\n",
error_rate, observed_error_rate, eb_n0, rate, order);
exit(1);
} else {
printf(
"test passed, expected error rate=%.2e, observed error rate=%.2e @%.1fdB for rate %zu "
"order %zu\n",
error_rate, observed_error_rate, eb_n0, rate, order);
}
}
int main() {
srand(time(NULL));
conv_testbench *testbench = NULL;
correct_convolutional *conv;
void *fec;
uint16_t *poly;
poly = (uint16_t[]){V27POLYA, V27POLYB};
conv = correct_convolutional_create(2, 7, poly);
fec = create_viterbi27(8 * max_block_len);
assert_test_result(conv, fec, conv_fec27_decode, &testbench, 1000000, 2, 6, INFINITY, 0);
assert_test_result(conv, fec, conv_fec27_decode, &testbench, 1000000, 2, 6, 4.5, 8e-06);
assert_test_result(conv, fec, conv_fec27_decode, &testbench, 1000000, 2, 6, 4.0, 5e-05);
delete_viterbi27(fec);
correct_convolutional_destroy(conv);
printf("\n");
poly = (uint16_t[]){V29POLYA, V29POLYB};
conv = correct_convolutional_create(2, 9, poly);
fec = create_viterbi29(8 * max_block_len);
assert_test_result(conv, fec, conv_fec29_decode, &testbench, 1000000, 2, 9, INFINITY, 0);
assert_test_result(conv, fec, conv_fec29_decode, &testbench, 1000000, 2, 9, 4.5, 3e-06);
assert_test_result(conv, fec, conv_fec29_decode, &testbench, 1000000, 2, 9, 4.0, 8e-06);
delete_viterbi29(fec);
correct_convolutional_destroy(conv);
printf("\n");
poly = (uint16_t[]){V39POLYA, V39POLYB, V39POLYC};
conv = correct_convolutional_create(3, 9, poly);
fec = create_viterbi39(8 * max_block_len);
assert_test_result(conv, fec, conv_fec39_decode, &testbench, 1000000, 3, 9, INFINITY, 0);
assert_test_result(conv, fec, conv_fec39_decode, &testbench, 1000000, 3, 9, 4.5, 3e-06);
assert_test_result(conv, fec, conv_fec39_decode, &testbench, 1000000, 3, 9, 4.0, 5e-06);
delete_viterbi39(fec);
correct_convolutional_destroy(conv);
printf("\n");
poly = (uint16_t[]){V615POLYA, V615POLYB, V615POLYC, V615POLYD, V615POLYE, V615POLYF};
conv = correct_convolutional_create(6, 15, poly);
fec = create_viterbi615(8 * max_block_len);
assert_test_result(conv, fec, conv_fec615_decode, &testbench, 100000, 6, 15, INFINITY, 0);
assert_test_result(conv, fec, conv_fec615_decode, &testbench, 100000, 6, 15, 3.0, 3e-06);
assert_test_result(conv, fec, conv_fec615_decode, &testbench, 100000, 6, 15, 2.5, 1e-05);
delete_viterbi615(fec);
correct_convolutional_destroy(conv);
printf("\n");
free_scratch(testbench);
return 0;
}

View File

@ -0,0 +1,122 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "correct.h"
#include "fec_shim.h"
#include "correct/util/error-sim-shim.h"
size_t max_block_len = 4096;
size_t test_conv(correct_convolutional *conv, void *fec,
ssize_t (*decode)(void *, uint8_t *, size_t, uint8_t *),
conv_testbench **testbench_ptr, size_t msg_len, double eb_n0,
double bpsk_bit_energy, double bpsk_voltage) {
uint8_t *msg = malloc(max_block_len);
size_t num_errors = 0;
while (msg_len) {
size_t block_len = (max_block_len < msg_len) ? max_block_len : msg_len;
msg_len -= block_len;
for (unsigned int j = 0; j < block_len; j++) {
msg[j] = rand() % 256;
}
*testbench_ptr =
resize_conv_testbench(*testbench_ptr, conv_correct_enclen, conv, block_len);
conv_testbench *testbench = *testbench_ptr;
testbench->encoder = conv;
testbench->encode = conv_correct_encode;
testbench->decoder = fec;
testbench->decode = decode;
build_white_noise(testbench->noise, testbench->enclen, eb_n0, bpsk_bit_energy);
num_errors += test_conv_noise(testbench, msg, block_len, bpsk_voltage);
}
free(msg);
return num_errors;
}
void assert_test_result(correct_convolutional *conv, void *fec,
ssize_t (*decode)(void *, uint8_t *, size_t, uint8_t *),
conv_testbench **testbench, size_t test_length, size_t rate, size_t order,
double eb_n0, double error_rate) {
double bpsk_voltage = 1.0 / sqrt(2.0);
double bpsk_sym_energy = 2 * pow(bpsk_voltage, 2.0);
double bpsk_bit_energy = bpsk_sym_energy * rate;
size_t error_count =
test_conv(conv, fec, decode, testbench, test_length, eb_n0, bpsk_bit_energy, bpsk_voltage);
double observed_error_rate = error_count / ((double)test_length * 8);
if (observed_error_rate > error_rate) {
printf(
"test failed, expected error rate=%.2e, observed error rate=%.2e @%.1fdB for rate %zu "
"order %zu\n",
error_rate, observed_error_rate, eb_n0, rate, order);
exit(1);
} else {
printf(
"test passed, expected error rate=%.2e, observed error rate=%.2e @%.1fdB for rate %zu "
"order %zu\n",
error_rate, observed_error_rate, eb_n0, rate, order);
}
}
int main() {
srand(time(NULL));
conv_testbench *testbench = NULL;
correct_convolutional *conv;
void *fec;
uint16_t *poly;
poly = (uint16_t[]){V27POLYA, V27POLYB};
conv = correct_convolutional_create(2, 7, poly);
fec = create_viterbi27(8 * max_block_len);
assert_test_result(conv, fec, conv_shim27_decode, &testbench, 1000000, 2, 6, INFINITY, 0);
assert_test_result(conv, fec, conv_shim27_decode, &testbench, 1000000, 2, 6, 4.5, 8e-06);
assert_test_result(conv, fec, conv_shim27_decode, &testbench, 1000000, 2, 6, 4.0, 5e-05);
delete_viterbi27(fec);
correct_convolutional_destroy(conv);
printf("\n");
poly = (uint16_t[]){V29POLYA, V29POLYB};
conv = correct_convolutional_create(2, 9, poly);
fec = create_viterbi29(8 * max_block_len);
assert_test_result(conv, fec, conv_shim29_decode, &testbench, 1000000, 2, 9, INFINITY, 0);
assert_test_result(conv, fec, conv_shim29_decode, &testbench, 1000000, 2, 9, 4.5, 3e-06);
assert_test_result(conv, fec, conv_shim29_decode, &testbench, 1000000, 2, 9, 4.0, 8e-06);
delete_viterbi29(fec);
correct_convolutional_destroy(conv);
printf("\n");
poly = (uint16_t[]){V39POLYA, V39POLYB, V39POLYC};
conv = correct_convolutional_create(3, 9, poly);
fec = create_viterbi39(8 * max_block_len);
assert_test_result(conv, fec, conv_shim39_decode, &testbench, 1000000, 3, 9, INFINITY, 0);
assert_test_result(conv, fec, conv_shim39_decode, &testbench, 1000000, 3, 9, 4.5, 3e-06);
assert_test_result(conv, fec, conv_shim39_decode, &testbench, 1000000, 3, 9, 4.0, 9e-06);
delete_viterbi39(fec);
correct_convolutional_destroy(conv);
printf("\n");
poly = (uint16_t[]){V615POLYA, V615POLYB, V615POLYC, V615POLYD, V615POLYE, V615POLYF};
conv = correct_convolutional_create(6, 15, poly);
fec = create_viterbi615(8 * max_block_len);
assert_test_result(conv, fec, conv_shim615_decode, &testbench, 100000, 6, 15, INFINITY, 0);
assert_test_result(conv, fec, conv_shim615_decode, &testbench, 100000, 6, 15, 3.0, 2e-05);
assert_test_result(conv, fec, conv_shim615_decode, &testbench, 100000, 6, 15, 2.5, 4e-05);
delete_viterbi615(fec);
correct_convolutional_destroy(conv);
printf("\n");
free_scratch(testbench);
return 0;
}

View File

@ -0,0 +1,132 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "correct/util/error-sim-sse.h"
size_t max_block_len = 4096;
size_t test_conv(correct_convolutional_sse *conv, conv_testbench **testbench_ptr,
size_t msg_len, double eb_n0, double bpsk_bit_energy,
double bpsk_voltage) {
uint8_t *msg = malloc(max_block_len);
size_t num_errors = 0;
while (msg_len) {
size_t block_len = (max_block_len < msg_len) ? max_block_len : msg_len;
msg_len -= block_len;
for (unsigned int j = 0; j < block_len; j++) {
msg[j] = rand() % 256;
}
*testbench_ptr = resize_conv_testbench(*testbench_ptr, conv_correct_sse_enclen, conv, block_len);
conv_testbench *testbench = *testbench_ptr;
testbench->encoder = conv;
testbench->encode = conv_correct_sse_encode;
testbench->decoder = conv;
testbench->decode = conv_correct_sse_decode;
build_white_noise(testbench->noise, testbench->enclen, eb_n0, bpsk_bit_energy);
num_errors += test_conv_noise(testbench, msg, block_len, bpsk_voltage);
}
free(msg);
return num_errors;
}
void assert_test_result(correct_convolutional_sse *conv, conv_testbench **testbench,
size_t test_length, size_t rate, size_t order, double eb_n0, double error_rate) {
double bpsk_voltage = 1.0/sqrt(2.0);
double bpsk_sym_energy = 2*pow(bpsk_voltage, 2.0);
double bpsk_bit_energy = bpsk_sym_energy * rate;
size_t error_count = test_conv(conv, testbench, test_length, eb_n0, bpsk_bit_energy, bpsk_voltage);
double observed_error_rate = error_count/((double)test_length * 8);
if (observed_error_rate > error_rate) {
printf("test failed, expected error rate=%.2e, observed error rate=%.2e @%.1fdB for rate %zu order %zu\n",
error_rate, observed_error_rate, eb_n0, rate, order);
exit(1);
} else {
printf("test passed, expected error rate=%.2e, observed error rate=%.2e @%.1fdB for rate %zu order %zu\n",
error_rate, observed_error_rate, eb_n0, rate, order);
}
}
int main() {
srand(time(NULL));
conv_testbench *testbench = NULL;
correct_convolutional_sse *conv;
// n.b. the error rates below are at 5.0dB/4.5dB for order 6 polys
// and 4.5dB/4.0dB for order 7-9 polys. this can be easy to miss.
conv = correct_convolutional_sse_create(2, 6, correct_conv_r12_6_polynomial);
assert_test_result(conv, &testbench, 1000000, 2, 6, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 2, 6, 5.0, 8e-06);
assert_test_result(conv, &testbench, 1000000, 2, 6, 4.5, 3e-05);
correct_convolutional_sse_destroy(conv);
printf("\n");
conv = correct_convolutional_sse_create(2, 7, correct_conv_r12_7_polynomial);
assert_test_result(conv, &testbench, 1000000, 2, 7, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 2, 7, 4.5, 1e-05);
assert_test_result(conv, &testbench, 1000000, 2, 7, 4.0, 5e-05);
correct_convolutional_sse_destroy(conv);
printf("\n");
conv = correct_convolutional_sse_create(2, 8, correct_conv_r12_8_polynomial);
assert_test_result(conv, &testbench, 1000000, 2, 8, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 2, 8, 4.5, 5e-06);
assert_test_result(conv, &testbench, 1000000, 2, 8, 4.0, 3e-05);
correct_convolutional_sse_destroy(conv);
printf("\n");
conv = correct_convolutional_sse_create(2, 9, correct_conv_r12_9_polynomial);
assert_test_result(conv, &testbench, 1000000, 2, 9, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 2, 9, 4.5, 3e-06);
assert_test_result(conv, &testbench, 1000000, 2, 9, 4.0, 8e-06);
correct_convolutional_sse_destroy(conv);
printf("\n");
conv = correct_convolutional_sse_create(3, 6, correct_conv_r13_6_polynomial);
assert_test_result(conv, &testbench, 1000000, 3, 6, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 3, 6, 5.0, 5e-06);
assert_test_result(conv, &testbench, 1000000, 3, 6, 4.5, 2e-05);
correct_convolutional_sse_destroy(conv);
printf("\n");
conv = correct_convolutional_sse_create(3, 7, correct_conv_r13_7_polynomial);
assert_test_result(conv, &testbench, 1000000, 3, 7, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 3, 7, 4.5, 5e-06);
assert_test_result(conv, &testbench, 1000000, 3, 7, 4.0, 3e-05);
correct_convolutional_sse_destroy(conv);
printf("\n");
conv = correct_convolutional_sse_create(3, 8, correct_conv_r13_8_polynomial);
assert_test_result(conv, &testbench, 1000000, 3, 8, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 3, 8, 4.5, 4e-06);
assert_test_result(conv, &testbench, 1000000, 3, 8, 4.0, 1e-05);
correct_convolutional_sse_destroy(conv);
printf("\n");
conv = correct_convolutional_sse_create(3, 9, correct_conv_r13_9_polynomial);
assert_test_result(conv, &testbench, 1000000, 3, 9, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 3, 9, 4.5, 3e-06);
assert_test_result(conv, &testbench, 1000000, 3, 9, 4.0, 5e-06);
correct_convolutional_sse_destroy(conv);
printf("\n");
free_scratch(testbench);
return 0;
}

View File

@ -0,0 +1,133 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "correct.h"
#include "correct/util/error-sim.h"
size_t max_block_len = 4096;
size_t test_conv(correct_convolutional *conv, conv_testbench **testbench_ptr,
size_t msg_len, double eb_n0, double bpsk_bit_energy,
double bpsk_voltage) {
uint8_t *msg = malloc(max_block_len);
size_t num_errors = 0;
while (msg_len) {
size_t block_len = (max_block_len < msg_len) ? max_block_len : msg_len;
msg_len -= block_len;
for (unsigned int j = 0; j < block_len; j++) {
msg[j] = rand() % 256;
}
*testbench_ptr = resize_conv_testbench(*testbench_ptr, conv_correct_enclen, conv, block_len);
conv_testbench *testbench = *testbench_ptr;
testbench->encoder = conv;
testbench->encode = conv_correct_encode;
testbench->decoder = conv;
testbench->decode = conv_correct_decode;
build_white_noise(testbench->noise, testbench->enclen, eb_n0, bpsk_bit_energy);
num_errors += test_conv_noise(testbench, msg, block_len, bpsk_voltage);
}
free(msg);
return num_errors;
}
void assert_test_result(correct_convolutional *conv, conv_testbench **testbench,
size_t test_length, size_t rate, size_t order, double eb_n0, double error_rate) {
double bpsk_voltage = 1.0/sqrt(2.0);
double bpsk_sym_energy = 2*pow(bpsk_voltage, 2.0);
double bpsk_bit_energy = bpsk_sym_energy * rate;
size_t error_count = test_conv(conv, testbench, test_length, eb_n0, bpsk_bit_energy, bpsk_voltage);
double observed_error_rate = error_count/((double)test_length * 8);
if (observed_error_rate > error_rate) {
printf("test failed, expected error rate=%.2e, observed error rate=%.2e @%.1fdB for rate %zu order %zu\n",
error_rate, observed_error_rate, eb_n0, rate, order);
exit(1);
} else {
printf("test passed, expected error rate=%.2e, observed error rate=%.2e @%.1fdB for rate %zu order %zu\n",
error_rate, observed_error_rate, eb_n0, rate, order);
}
}
int main() {
srand(time(NULL));
conv_testbench *testbench = NULL;
correct_convolutional *conv;
// n.b. the error rates below are at 5.0dB/4.5dB for order 6 polys
// and 4.5dB/4.0dB for order 7-9 polys. this can be easy to miss.
conv = correct_convolutional_create(2, 6, correct_conv_r12_6_polynomial);
assert_test_result(conv, &testbench, 1000000, 2, 6, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 2, 6, 5.0, 5e-06);
assert_test_result(conv, &testbench, 1000000, 2, 6, 4.5, 3e-05);
correct_convolutional_destroy(conv);
printf("\n");
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
assert_test_result(conv, &testbench, 1000000, 2, 7, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 2, 7, 4.5, 1e-05);
assert_test_result(conv, &testbench, 1000000, 2, 7, 4.0, 5e-05);
correct_convolutional_destroy(conv);
printf("\n");
conv = correct_convolutional_create(2, 8, correct_conv_r12_8_polynomial);
assert_test_result(conv, &testbench, 1000000, 2, 8, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 2, 8, 4.5, 5e-06);
assert_test_result(conv, &testbench, 1000000, 2, 8, 4.0, 3e-05);
correct_convolutional_destroy(conv);
printf("\n");
conv = correct_convolutional_create(2, 9, correct_conv_r12_9_polynomial);
assert_test_result(conv, &testbench, 1000000, 2, 9, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 2, 9, 4.5, 3e-06);
assert_test_result(conv, &testbench, 1000000, 2, 9, 4.0, 1e-05);
correct_convolutional_destroy(conv);
printf("\n");
conv = correct_convolutional_create(3, 6, correct_conv_r13_6_polynomial);
assert_test_result(conv, &testbench, 1000000, 3, 6, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 3, 6, 5.0, 5e-06);
assert_test_result(conv, &testbench, 1000000, 3, 6, 4.5, 2e-05);
correct_convolutional_destroy(conv);
printf("\n");
conv = correct_convolutional_create(3, 7, correct_conv_r13_7_polynomial);
assert_test_result(conv, &testbench, 1000000, 3, 7, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 3, 7, 4.5, 5e-06);
assert_test_result(conv, &testbench, 1000000, 3, 7, 4.0, 3e-05);
correct_convolutional_destroy(conv);
printf("\n");
conv = correct_convolutional_create(3, 8, correct_conv_r13_8_polynomial);
assert_test_result(conv, &testbench, 1000000, 3, 8, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 3, 8, 4.5, 4e-06);
assert_test_result(conv, &testbench, 1000000, 3, 8, 4.0, 1e-05);
correct_convolutional_destroy(conv);
printf("\n");
conv = correct_convolutional_create(3, 9, correct_conv_r13_9_polynomial);
assert_test_result(conv, &testbench, 1000000, 3, 9, INFINITY, 0);
assert_test_result(conv, &testbench, 1000000, 3, 9, 4.5, 3e-06);
assert_test_result(conv, &testbench, 1000000, 3, 9, 4.0, 5e-06);
correct_convolutional_destroy(conv);
printf("\n");
free_scratch(testbench);
return 0;
}

View File

@ -0,0 +1,41 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include "correct.h"
void rs_correct_encode(void *encoder, uint8_t *msg, size_t msg_length,
uint8_t *msg_out);
void rs_correct_decode(void *decoder, uint8_t *encoded, size_t encoded_length,
uint8_t *erasure_locations, size_t erasure_length,
uint8_t *msg, size_t pad_length, size_t num_roots);
typedef struct {
size_t block_length;
size_t message_length;
size_t min_distance;
unsigned char *msg;
uint8_t *encoded;
int *indices;
uint8_t *corrupted_encoded;
uint8_t *erasure_locations;
unsigned char *recvmsg;
} rs_testbench;
typedef struct {
void (*encode)(void *, uint8_t *, size_t, uint8_t *);
void *encoder;
void (*decode)(void *, uint8_t *, size_t, uint8_t *, size_t, uint8_t *, size_t, size_t);
void *decoder;
} rs_test;
rs_testbench *rs_testbench_create(size_t block_length, size_t min_distance);
void rs_testbench_destroy(rs_testbench *testbench);
typedef struct {
bool output_matches;
} rs_test_run;
rs_test_run test_rs_errors(rs_test *test, rs_testbench *testbench, size_t msg_length,
size_t num_errors, size_t num_erasures);

View File

@ -0,0 +1,10 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <fec.h>
void rs_fec_encode(void *encoder, uint8_t *msg, size_t msg_length,
uint8_t *msg_out);
void rs_fec_decode(void *decoder, uint8_t *encoded, size_t encoded_length,
uint8_t *erasure_locations, size_t erasure_length,
uint8_t *msg, size_t pad_length, size_t num_roots);

View File

@ -0,0 +1,10 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "fec_shim.h"
void rs_fec_encode(void *encoder, uint8_t *msg, size_t msg_length,
uint8_t *msg_out);
void rs_fec_decode(void *decoder, uint8_t *encoded, size_t encoded_length,
uint8_t *erasure_locations, size_t erasure_length,
uint8_t *msg, size_t pad_length, size_t num_roots);

View File

@ -0,0 +1,138 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include "rs_tester.h"
#include "rs_tester_fec.h"
void print_test_type(size_t block_length, size_t message_length,
size_t num_errors, size_t num_erasures) {
printf(
"testing reed solomon block length=%zu, message length=%zu, "
"errors=%zu, erasures=%zu...",
block_length, message_length, num_errors, num_erasures);
}
void fail_test() {
printf("FAILED\n");
exit(1);
}
void pass_test() { printf("PASSED\n"); }
void run_tests(correct_reed_solomon *rs, void *fec_rs, rs_testbench *testbench,
size_t block_length, size_t test_msg_length, size_t num_errors,
size_t num_erasures, size_t num_iterations) {
// run both ways, correct->fec and fec->correct
rs_test test;
test.encode = rs_correct_encode;
test.encoder = rs;
test.decode = rs_fec_decode;
test.decoder = fec_rs;
print_test_type(block_length, test_msg_length, num_errors, num_erasures);
for (size_t i = 0; i < num_iterations; i++) {
rs_test_run run = test_rs_errors(&test, testbench, test_msg_length,
num_errors, num_erasures);
if (!run.output_matches) {
fail_test();
}
}
test.encode = rs_fec_encode;
test.encoder = fec_rs;
test.decode = rs_correct_decode;
test.decoder = rs;
for (size_t i = 0; i < num_iterations; i++) {
rs_test_run run = test_rs_errors(&test, testbench, test_msg_length,
num_errors, num_erasures);
if (!run.output_matches) {
fail_test();
}
}
pass_test();
}
int main() {
srand(time(NULL));
size_t block_length = 255;
size_t min_distance = 32;
size_t message_length = block_length - min_distance;
size_t pad_length;
void *fec_rs;
correct_reed_solomon *rs = correct_reed_solomon_create(
correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance);
rs_testbench *testbench = rs_testbench_create(block_length, min_distance);
pad_length = message_length / 2;
fec_rs = init_rs_char(8, correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance,
pad_length);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 2, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, min_distance, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 4, min_distance / 2, 20000);
free_rs_char(fec_rs);
pad_length = 0;
fec_rs = init_rs_char(8, correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance,
pad_length);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 2, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, min_distance, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 4, min_distance / 2, 20000);
free_rs_char(fec_rs);
rs_testbench_destroy(testbench);
correct_reed_solomon_destroy(rs);
min_distance = 16;
message_length = block_length - min_distance;
rs = correct_reed_solomon_create(
correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance);
testbench = rs_testbench_create(block_length, min_distance);
pad_length = message_length / 2;
fec_rs = init_rs_char(8, correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance,
pad_length);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 2, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, min_distance, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 4, min_distance / 2, 20000);
free_rs_char(fec_rs);
pad_length = 0;
fec_rs = init_rs_char(8, correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance,
pad_length);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 2, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, min_distance, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 4, min_distance / 2, 20000);
free_rs_char(fec_rs);
rs_testbench_destroy(testbench);
correct_reed_solomon_destroy(rs);
printf("test passed\n");
return 0;
}

View File

@ -0,0 +1,138 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include "rs_tester.h"
#include "rs_tester_fec_shim.h"
void print_test_type(size_t block_length, size_t message_length,
size_t num_errors, size_t num_erasures) {
printf(
"testing reed solomon block length=%zu, message length=%zu, "
"errors=%zu, erasures=%zu...",
block_length, message_length, num_errors, num_erasures);
}
void fail_test() {
printf("FAILED\n");
exit(1);
}
void pass_test() { printf("PASSED\n"); }
void run_tests(correct_reed_solomon *rs, void *fec_rs, rs_testbench *testbench,
size_t block_length, size_t test_msg_length, size_t num_errors,
size_t num_erasures, size_t num_iterations) {
// run both ways, correct->fec and fec->correct
rs_test test;
test.encode = rs_correct_encode;
test.encoder = rs;
test.decode = rs_fec_decode;
test.decoder = fec_rs;
print_test_type(block_length, test_msg_length, num_errors, num_erasures);
for (size_t i = 0; i < num_iterations; i++) {
rs_test_run run = test_rs_errors(&test, testbench, test_msg_length, num_errors,
num_erasures);
if (!run.output_matches) {
fail_test();
}
}
test.encode = rs_fec_encode;
test.encoder = fec_rs;
test.decode = rs_correct_decode;
test.decoder = rs;
for (size_t i = 0; i < num_iterations; i++) {
rs_test_run run = test_rs_errors(&test, testbench, test_msg_length, num_errors,
num_erasures);
if (!run.output_matches) {
fail_test();
}
}
pass_test();
}
int main() {
srand(time(NULL));
size_t block_length = 255;
size_t min_distance = 32;
size_t message_length = block_length - min_distance;
size_t pad_length;
void *fec_rs;
correct_reed_solomon *rs = correct_reed_solomon_create(
correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance);
rs_testbench *testbench = rs_testbench_create(block_length, min_distance);
pad_length = message_length / 2;
fec_rs = init_rs_char(8, correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance,
pad_length);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 2, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, min_distance, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 4, min_distance / 2, 20000);
free_rs_char(fec_rs);
pad_length = 0;
fec_rs = init_rs_char(8, correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance,
pad_length);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 2, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, min_distance, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 4, min_distance / 2, 20000);
free_rs_char(fec_rs);
rs_testbench_destroy(testbench);
correct_reed_solomon_destroy(rs);
min_distance = 16;
message_length = block_length - min_distance;
rs = correct_reed_solomon_create(
correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance);
testbench = rs_testbench_create(block_length, min_distance);
pad_length = message_length / 2;
fec_rs = init_rs_char(8, correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance,
pad_length);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 2, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, min_distance, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 4, min_distance / 2, 20000);
free_rs_char(fec_rs);
pad_length = 0;
fec_rs = init_rs_char(8, correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance,
pad_length);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 2, 0, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
0, min_distance, 20000);
run_tests(rs, fec_rs, testbench, block_length, message_length - pad_length,
min_distance / 4, min_distance / 2, 20000);
free_rs_char(fec_rs);
rs_testbench_destroy(testbench);
correct_reed_solomon_destroy(rs);
printf("test passed\n");
return 0;
}

View File

@ -0,0 +1,146 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include "rs_tester.h"
void print_test_type(size_t block_length, size_t message_length,
size_t num_errors, size_t num_erasures) {
printf(
"testing reed solomon block length=%zu, message length=%zu, "
"errors=%zu, erasures=%zu...",
block_length, message_length, num_errors, num_erasures);
}
void fail_test() {
printf("FAILED\n");
exit(1);
}
void pass_test() { printf("PASSED\n"); }
void run_tests(correct_reed_solomon *rs, rs_testbench *testbench,
size_t block_length, size_t test_msg_length, size_t num_errors,
size_t num_erasures, size_t num_iterations) {
rs_test test;
test.encode = rs_correct_encode;
test.decode = rs_correct_decode;
test.encoder = rs;
test.decoder = rs;
print_test_type(block_length, test_msg_length, num_errors, num_erasures);
for (size_t i = 0; i < num_iterations; i++) {
rs_test_run run = test_rs_errors(&test, testbench, test_msg_length, num_errors,
num_erasures);
if (!run.output_matches) {
fail_test();
}
}
pass_test();
}
int main() {
srand(time(NULL));
size_t block_length = 255;
size_t min_distance = 32;
size_t message_length = block_length - min_distance;
correct_reed_solomon *rs = correct_reed_solomon_create(
correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance);
rs_testbench *testbench = rs_testbench_create(block_length, min_distance);
run_tests(rs, testbench, block_length, message_length / 2, 0, 0, 20000);
run_tests(rs, testbench, block_length, message_length, 0, 0, 20000);
run_tests(rs, testbench, block_length, message_length / 2, min_distance / 2,
0, 20000);
run_tests(rs, testbench, block_length, message_length, min_distance / 2, 0,
20000);
run_tests(rs, testbench, block_length, message_length / 2, 0, min_distance,
20000);
run_tests(rs, testbench, block_length, message_length, 0, min_distance,
20000);
run_tests(rs, testbench, block_length, message_length / 2, min_distance / 4,
min_distance / 2, 20000);
run_tests(rs, testbench, block_length, message_length, min_distance / 4,
min_distance / 2, 20000);
rs_testbench_destroy(testbench);
correct_reed_solomon_destroy(rs);
min_distance = 16;
message_length = block_length - min_distance;
rs = correct_reed_solomon_create(
correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance);
testbench = rs_testbench_create(block_length, min_distance);
run_tests(rs, testbench, block_length, message_length / 2, 0, 0, 20000);
run_tests(rs, testbench, block_length, message_length, 0, 0, 20000);
run_tests(rs, testbench, block_length, message_length / 2, min_distance / 2,
0, 20000);
run_tests(rs, testbench, block_length, message_length, min_distance / 2, 0,
20000);
run_tests(rs, testbench, block_length, message_length / 2, 0, min_distance,
20000);
run_tests(rs, testbench, block_length, message_length, 0, min_distance,
20000);
run_tests(rs, testbench, block_length, message_length / 2, min_distance / 4,
min_distance / 2, 20000);
run_tests(rs, testbench, block_length, message_length, min_distance / 4,
min_distance / 2, 20000);
rs_testbench_destroy(testbench);
correct_reed_solomon_destroy(rs);
min_distance = 8;
message_length = block_length - min_distance;
rs = correct_reed_solomon_create(
correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance);
testbench = rs_testbench_create(block_length, min_distance);
run_tests(rs, testbench, block_length, message_length / 2, 0, 0, 20000);
run_tests(rs, testbench, block_length, message_length, 0, 0, 20000);
run_tests(rs, testbench, block_length, message_length / 2, min_distance / 2,
0, 20000);
run_tests(rs, testbench, block_length, message_length, min_distance / 2, 0,
20000);
run_tests(rs, testbench, block_length, message_length / 2, 0, min_distance,
20000);
run_tests(rs, testbench, block_length, message_length, 0, min_distance,
20000);
run_tests(rs, testbench, block_length, message_length / 2, min_distance / 4,
min_distance / 2, 20000);
run_tests(rs, testbench, block_length, message_length, min_distance / 4,
min_distance / 2, 20000);
rs_testbench_destroy(testbench);
correct_reed_solomon_destroy(rs);
min_distance = 4;
message_length = block_length - min_distance;
rs = correct_reed_solomon_create(
correct_rs_primitive_polynomial_ccsds, 1, 1, min_distance);
testbench = rs_testbench_create(block_length, min_distance);
run_tests(rs, testbench, block_length, message_length / 2, 0, 0, 20000);
run_tests(rs, testbench, block_length, message_length, 0, 0, 20000);
run_tests(rs, testbench, block_length, message_length / 2, min_distance / 2,
0, 20000);
run_tests(rs, testbench, block_length, message_length, min_distance / 2, 0,
20000);
run_tests(rs, testbench, block_length, message_length / 2, 0, min_distance,
20000);
run_tests(rs, testbench, block_length, message_length, 0, min_distance,
20000);
run_tests(rs, testbench, block_length, message_length / 2, min_distance / 4,
min_distance / 2, 20000);
run_tests(rs, testbench, block_length, message_length, min_distance / 4,
min_distance / 2, 20000);
rs_testbench_destroy(testbench);
correct_reed_solomon_destroy(rs);
printf("test passed\n");
return 0;
}

View File

@ -0,0 +1,102 @@
#include "rs_tester.h"
void shuffle(int *a, size_t len) {
for (size_t i = 0; i < len - 2; i++) {
size_t j = rand() % (len - i) + i;
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
void rs_correct_encode(void *encoder, uint8_t *msg, size_t msg_length,
uint8_t *msg_out) {
correct_reed_solomon_encode((correct_reed_solomon *)encoder, msg,
msg_length, msg_out);
}
void rs_correct_decode(void *decoder, uint8_t *encoded, size_t encoded_length,
uint8_t *erasure_locations, size_t erasure_length,
uint8_t *msg, size_t pad_length, size_t num_roots) {
correct_reed_solomon_decode_with_erasures(
(correct_reed_solomon *)decoder, encoded, encoded_length,
erasure_locations, erasure_length, msg);
}
rs_testbench *rs_testbench_create(size_t block_length, size_t min_distance) {
rs_testbench *testbench = calloc(1, sizeof(rs_testbench));
size_t message_length = block_length - min_distance;
testbench->message_length = message_length;
testbench->block_length = block_length;
testbench->min_distance = min_distance;
testbench->msg = calloc(message_length, sizeof(unsigned char));
testbench->encoded = malloc(block_length * sizeof(uint8_t));
testbench->indices = malloc(block_length * sizeof(int));
testbench->corrupted_encoded = malloc(block_length * sizeof(uint8_t));
testbench->erasure_locations = malloc(min_distance * sizeof(uint8_t));
testbench->recvmsg = malloc(sizeof(unsigned char) * message_length);
return testbench;
}
void rs_testbench_destroy(rs_testbench *testbench) {
free(testbench->msg);
free(testbench->encoded);
free(testbench->indices);
free(testbench->corrupted_encoded);
free(testbench->erasure_locations);
free(testbench->recvmsg);
free(testbench);
}
rs_test_run test_rs_errors(rs_test *test, rs_testbench *testbench, size_t msg_length,
size_t num_errors, size_t num_erasures) {
rs_test_run run;
run.output_matches = false;
if (msg_length > testbench->message_length) {
return run;
}
for (size_t i = 0; i < msg_length; i++) {
testbench->msg[i] = rand() % 256;
}
size_t block_length = msg_length + testbench->min_distance;
size_t pad_length = testbench->message_length - msg_length;
test->encode(test->encoder, testbench->msg, msg_length, testbench->encoded);
memcpy(testbench->corrupted_encoded, testbench->encoded, block_length);
for (int i = 0; i < block_length; i++) {
testbench->indices[i] = i;
}
shuffle(testbench->indices, block_length);
for (unsigned int i = 0; i < num_erasures; i++) {
int index = testbench->indices[i];
uint8_t corruption_mask = (rand() % 255) + 1;
testbench->corrupted_encoded[index] ^= corruption_mask;
testbench->erasure_locations[i] = index;
}
for (unsigned int i = 0; i < num_errors; i++) {
int index = testbench->indices[i + num_erasures];
uint8_t corruption_mask = (rand() % 255) + 1;
testbench->corrupted_encoded[index] ^= corruption_mask;
}
test->decode(test->decoder, testbench->corrupted_encoded, block_length,
testbench->erasure_locations, num_erasures,
testbench->recvmsg, pad_length, testbench->min_distance);
run.output_matches = (bool)(memcmp(testbench->msg, testbench->recvmsg, msg_length) == 0);
return run;
}

View File

@ -0,0 +1,30 @@
#include "rs_tester_fec.h"
void rs_fec_encode(void *encoder, uint8_t *msg, size_t msg_length,
uint8_t *msg_out) {
// XXX make sure that pad length used to build encoder corresponds to this
// msg_length
memcpy(msg_out, msg, msg_length);
encode_rs_char(encoder, msg_out, msg_out + msg_length);
}
void rs_fec_decode(void *decoder, uint8_t *encoded, size_t encoded_length,
uint8_t *erasure_locations, size_t erasure_length,
uint8_t *msg, size_t pad_length, size_t num_roots) {
// XXX make sure that pad length used to build decoder corresponds to this
// encoded_length
if (erasure_length) {
static size_t locations_len = 0;
static int *locations = NULL;
if (locations_len < erasure_length) {
locations = realloc(locations, erasure_length * sizeof(int));
locations_len = erasure_length;
}
for (size_t i = 0; i < erasure_length; i++) {
locations[i] = (unsigned int)(erasure_locations[i]) + pad_length;
}
decode_rs_char(decoder, encoded, locations, erasure_length);
} else {
decode_rs_char(decoder, encoded, NULL, 0);
}
memcpy(msg, encoded, encoded_length - num_roots);
}

View File

@ -0,0 +1,26 @@
#include "rs_tester_fec_shim.h"
void rs_fec_encode(void *encoder, uint8_t *msg, size_t msg_length,
uint8_t *msg_out) {
// XXX make sure that pad length used to build encoder corresponds to this
// msg_length
memcpy(msg_out, msg, msg_length);
encode_rs_char(encoder, msg_out, msg_out + msg_length);
}
void rs_fec_decode(void *decoder, uint8_t *encoded, size_t encoded_length,
uint8_t *erasure_locations, size_t erasure_length,
uint8_t *msg, size_t pad_length, size_t num_roots) {
// XXX make sure that pad length used to build decoder corresponds to this
// encoded_length
if (erasure_length) {
int *locations = malloc(erasure_length * sizeof(int));
for (size_t i = 0; i < erasure_length; i++) {
locations[i] = (unsigned int)(erasure_locations[i]) + pad_length;
}
decode_rs_char(decoder, encoded, locations, erasure_length);
free(locations);
} else {
decode_rs_char(decoder, encoded, NULL, 0);
}
memcpy(msg, encoded, encoded_length - num_roots);
}

View File

@ -0,0 +1,29 @@
add_executable(rs_find_primitive_poly EXCLUDE_FROM_ALL find_rs_primitive_poly.c)
target_link_libraries(rs_find_primitive_poly correct_static)
set(all_tools ${all_tools} rs_find_primitive_poly)
if(HAVE_LIBFEC)
add_executable(conv_find_libfec_poly EXCLUDE_FROM_ALL find_conv_libfec_poly.c)
target_link_libraries(conv_find_libfec_poly correct_static fec)
set(all_tools ${all_tools} conv_find_libfec_poly)
endif()
if(HAVE_SSE)
add_executable(conv_find_optim_poly EXCLUDE_FROM_ALL find_conv_optim_poly.c $<TARGET_OBJECTS:error_sim_sse>)
target_link_libraries(conv_find_optim_poly correct_static)
set(all_tools ${all_tools} conv_find_optim_poly)
add_executable(conv_find_optim_poly_annealing EXCLUDE_FROM_ALL find_conv_optim_poly_annealing.c $<TARGET_OBJECTS:error_sim_sse>)
target_link_libraries(conv_find_optim_poly_annealing correct_static)
set(all_tools ${all_tools} conv_find_optim_poly_annealing)
else()
add_executable(conv_find_optim_poly EXCLUDE_FROM_ALL find_conv_optim_poly.c $<TARGET_OBJECTS:error_sim>)
target_link_libraries(conv_find_optim_poly correct_static)
set(all_tools ${all_tools} conv_find_optim_poly)
add_executable(conv_find_optim_poly_annealing EXCLUDE_FROM_ALL find_conv_optim_poly_annealing.c $<TARGET_OBJECTS:error_sim>)
target_link_libraries(conv_find_optim_poly_annealing correct_static)
set(all_tools ${all_tools} conv_find_optim_poly_annealing)
endif()
add_custom_target(tools DEPENDS ${all_tools})

View File

@ -0,0 +1,279 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <stddef.h>
#include <assert.h>
#include <correct.h>
#include <fec.h>
// this program allows us to find all of the polynomials that come with libfec
// this way, we can provide compatibility with libfec-encoded streams and vice versa
// we can do this without directly copy-pasting from libfec's source, thanks
// to this finder
typedef struct {
void *vit;
int update_len;
int (*init)(void *, int);
int (*update)(void *, unsigned char *, int);
int (*chainback)(void *, unsigned char *, unsigned int, unsigned int);
} libfec_decoder_t;
void byte2bit(uint8_t *bytes, uint8_t *bits, size_t n_bits) {
unsigned char cmask = 0x80;
for (size_t i = 0; i < n_bits; i++) {
bits[i] = (bytes[i/8] & cmask) ? 255 : 0;
cmask >>= 1;
if (!cmask) {
cmask = 0x80;
}
}
}
correct_convolutional_polynomial_t *resize_poly_list(correct_convolutional_polynomial_t *polys, size_t cap) {
polys = realloc(polys, cap * sizeof(correct_convolutional_polynomial_t));
return polys;
}
void find_poly_coeff(size_t rate, size_t order, uint8_t *msg, size_t msg_len, libfec_decoder_t libfec, correct_convolutional_polynomial_t **polys_dest, size_t *polys_len, size_t search_coeff) {
// find a single coefficient of an unknown convolutional polynomial
// we are given a payload to encode, and we'll test all possible coefficients
// to see which ones yield correct decodings by libfec, which has some
// unknown polynomial "baked in"
// temp poly (this will be the one we search with)
correct_convolutional_polynomial_t *poly = malloc(rate * sizeof(correct_convolutional_polynomial_t));
// what's the largest coefficient value we'll test?
correct_convolutional_polynomial_t maxcoeff = (1 << order) - 1;
// note that we start about half way in
// this sum asks that we have the
// a) highest order bit set
// b) lowest order bit set
// we're only interested in coefficient values for which this is
// true because if it weren't, the coefficient would actually be
// of a smaller order than its supposed given order
correct_convolutional_polynomial_t startcoeff = (1 << (order - 1)) + 1;
// the values of this don't really matter except for the coeff we're searching for
// but just to be safe, we set them all
for (size_t i = 0; i < rate; i++) {
poly[i] = startcoeff;
}
// create a dummy encoder so that we can find how long the resulting encoded value is
correct_convolutional *conv_dummy = correct_convolutional_create(rate, order, poly);
size_t enclen_bits = correct_convolutional_encode_len(conv_dummy, msg_len);
size_t enclen = (enclen_bits % 8) ? (enclen_bits / 8 + 1) : enclen_bits / 8;
correct_convolutional_destroy(conv_dummy);
// compact encoded format (this comes from libcorrect)
uint8_t *encoded = malloc(enclen * sizeof(uint8_t));
// soft encoded format (this goes to libfec, one byte per bit)
uint8_t *encoded_bits = malloc(enclen * 8 * sizeof(uint8_t));
// resulting decoded message which we'll compare to our given payload
uint8_t *msg_cmp = malloc(msg_len * sizeof(uint8_t));
// we keep a list of coefficients which yielded correct decodings
// there could be 0, 1, or more than 1, and we'll return all of them
// we'll dynamically resize this as we go
size_t polys_cap = 1;
*polys_len = 0;
correct_convolutional_polynomial_t *polys = NULL;
polys = resize_poly_list(polys, polys_cap);
// iteration constants -- we go by 2 because we want the lowest order bit to
// stay set
for (correct_convolutional_polynomial_t i = startcoeff; i <= maxcoeff; i += 2) {
poly[search_coeff] = i;
correct_convolutional *conv = correct_convolutional_create(rate, order, poly);
correct_convolutional_encode(conv, (uint8_t*)msg, msg_len, encoded);
byte2bit(encoded, encoded_bits, enclen);
// now erase all the bits we're not searching for
for (size_t i = 0; i < msg_len * 8; i++) {
for (size_t j = 0; j < rate; j++) {
if (j != search_coeff) {
// 128 is a soft erasure
encoded_bits[i * rate + j] = 128;
}
}
}
libfec.init(libfec.vit, 0);
libfec.update(libfec.vit, encoded_bits, libfec.update_len);
libfec.chainback(libfec.vit, msg_cmp, 8 * msg_len, 0);
correct_convolutional_destroy(conv);
if (memcmp(msg_cmp, msg, msg_len) == 0) {
// match found
// resize list to make room
if (*polys_len == polys_cap) {
polys = resize_poly_list(polys, polys_cap * 2);
polys_cap *= 2;
}
polys[*polys_len] = i;
*polys_len = *polys_len + 1;
}
}
polys = resize_poly_list(polys, *polys_len);
*polys_dest = polys;
free(poly);
free(msg_cmp);
free(encoded);
free(encoded_bits);
}
// we choose 2 bytes because we need a payload that's longer than
// the shift register under test. since that includes an order 15
// s.r., we need at least 15 bits.
size_t msg_len = 2;
void find_poly(size_t rate, size_t order, libfec_decoder_t libfec, correct_convolutional_polynomial_t *poly) {
// find the complete set of coefficients that are "baked in" to
// one particular method of libfec
// most of this method is described by find_poly_coeff
// for each coeff we want to find, we'll generate random 2-byte payloads and give
// them to find_poly_coeff. If find_poly_coeff returns an empty list, we
// try again. If it returns a nonempty list, then we find the intersection of
// all the coefficient values find_poly_coeff has given us so far (we start
// with the complete set). we are finished when only one coeff value remains
// we perform this process for each coeff e.g. 6 times for a rate 1/6 polynomial
uint8_t msg[msg_len];
// this is the list returned to us by find_poly_coeff
correct_convolutional_polynomial_t *polys;
// the list's length is written here
size_t polys_len;
printf("rate 1/%zu order %zu poly:", rate, order);
for (size_t search_coeff = 0; search_coeff < rate; search_coeff++) {
correct_convolutional_polynomial_t *fit = NULL;
size_t fit_len = 0;
size_t fit_cap = 0;
bool done = false;
while (!done) {
for (size_t i = 0; i < msg_len; i++) {
msg[i] = rand() % 256;
}
find_poly_coeff(rate, order, msg, msg_len, libfec, &polys, &polys_len, search_coeff);
if (polys_len == 0) {
// skip if none fit (this is a special case)
continue;
}
if (fit_len == 0) {
// the very first intersection
// we'll just copy the list handed to us
fit_cap = polys_len;
fit_len = polys_len;
fit = resize_poly_list(fit, fit_cap);
for (size_t i = 0; i < polys_len; i++) {
fit[i] = polys[i];
}
} else {
// find intersection
ptrdiff_t polys_iter = 0;
ptrdiff_t fit_iter = 0;
ptrdiff_t new_fit_iter = 0;
// the lists generated by find_poly_coeff are sorted
// so we just retain the sorted property and walk both
while (polys_iter < polys_len && fit_iter < fit_len) {
if (polys[polys_iter] < fit[fit_iter]) {
polys_iter++;
} else if (polys[polys_iter] > fit[fit_iter]) {
fit_iter++;
} else {
fit[new_fit_iter] = fit[fit_iter];
polys_iter++;
fit_iter++;
new_fit_iter++;
}
}
// if new_fit_iter is 0 here then we don't intersect at all
// in this case we have to restart the search for this coeff
if (new_fit_iter != 0) {
fit_len = new_fit_iter;
} else {
free(fit);
fit = NULL;
fit_cap = 0;
fit_len = 0;
}
}
free(polys);
if (fit_len == 1) {
poly[search_coeff] = fit[0];
if (order <= 9) {
printf(" %04o", fit[0]);
} else {
printf(" %06o", fit[0]);
}
done = true;
}
}
free(fit);
}
printf("\n");
}
int main() {
libfec_decoder_t libfec;
srand(time(NULL));
setbuf(stdout, NULL);
correct_convolutional_polynomial_t poly[6];
libfec.vit = create_viterbi27(8 * msg_len);
libfec.update_len = 8 * msg_len + 6;
libfec.init = init_viterbi27;
libfec.update = update_viterbi27_blk;
libfec.chainback = chainback_viterbi27;
find_poly(2, 7, libfec, poly);
delete_viterbi27(libfec.vit);
libfec.vit = create_viterbi29(8 * msg_len);
libfec.update_len = 8 * msg_len + 8;
libfec.init = init_viterbi29;
libfec.update = update_viterbi29_blk;
libfec.chainback = chainback_viterbi29;
find_poly(2, 9, libfec, poly);
delete_viterbi29(libfec.vit);
libfec.vit = create_viterbi39(8 * msg_len);
libfec.update_len = 8 * msg_len + 8;
libfec.init = init_viterbi39;
libfec.update = update_viterbi39_blk;
libfec.chainback = chainback_viterbi39;
find_poly(3, 9, libfec, poly);
delete_viterbi39(libfec.vit);
libfec.vit = create_viterbi615(8 * msg_len);
libfec.update_len = 8 * msg_len + 14;
libfec.init = init_viterbi615;
libfec.update = update_viterbi615_blk;
libfec.chainback = chainback_viterbi615;
find_poly(6, 15, libfec, poly);
delete_viterbi615(libfec.vit);
return 0;
}

View File

@ -0,0 +1,330 @@
#include <stdbool.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stddef.h>
#include <limits.h>
#include <pthread.h>
#if HAVE_SSE
#include "correct/util/error-sim-sse.h"
typedef correct_convolutional_sse conv_t;
static conv_t*(*conv_create)(size_t, size_t, const uint16_t *) = correct_convolutional_sse_create;
static void(*conv_destroy)(conv_t *) = correct_convolutional_sse_destroy;
static size_t(*conv_enclen)(void *, size_t) = conv_correct_sse_enclen;
static void(*conv_encode)(void *, uint8_t *, size_t, uint8_t *) = conv_correct_sse_encode;
static void(*conv_decode)(void *, uint8_t *, size_t, uint8_t *) = conv_correct_sse_decode;
#else
#include "correct/util/error-sim.h"
typedef correct_convolutional conv_t;
static conv_t*(*conv_create)(size_t, size_t, const uint16_t *) = correct_convolutional_create;
static void(*conv_destroy)(conv_t *) = correct_convolutional_destroy;
static size_t(*conv_enclen)(void *, size_t) = conv_correct_enclen;
static void(*conv_encode)(void *, uint8_t *, size_t, uint8_t *) = conv_correct_encode;
static void(*conv_decode)(void *, uint8_t *, size_t, uint8_t *) = conv_correct_decode;
#endif
typedef struct {
conv_t *conv;
correct_convolutional_polynomial_t *poly;
} conv_tester_t;
typedef struct {
int *distances;
float cost;
correct_convolutional_polynomial_t *poly;
} conv_result_t;
int compare_conv_results(const void *avoid, const void *bvoid) {
const conv_result_t *a = (const conv_result_t *)avoid;
const conv_result_t *b = (const conv_result_t *)bvoid;
if (a->cost > b->cost) {
return 1;
}
return -1;
}
typedef struct {
size_t rate;
size_t order;
conv_result_t *items;
size_t items_len;
conv_testbench *scratch;
uint8_t *msg;
size_t msg_len;
size_t test_offset;
double bpsk_voltage;
} exhaustive_thread_args;
void *search_exhaustive_thread(void *vargs) {
exhaustive_thread_args *args = (exhaustive_thread_args *)vargs;
conv_t *conv;
for (size_t i = 0; i < args->items_len; i++) {
conv = conv_create(args->rate, args->order, args->items[i].poly);
args->scratch->encode = conv_encode;
args->scratch->encoder = conv;
args->scratch->decode = conv_decode;
args->scratch->decoder = conv;
args->items[i].distances[args->test_offset] += test_conv_noise(args->scratch, args->msg, args->msg_len, args->bpsk_voltage);
conv_destroy(conv);
}
pthread_exit(NULL);
}
void search_exhaustive(size_t rate, size_t order,
size_t n_bytes, uint8_t *msg,
conv_testbench **scratches, size_t num_scratches,
float *weights,
conv_result_t *items,
size_t items_len, double bpsk_voltage) {
exhaustive_thread_args *args = malloc(num_scratches * sizeof(exhaustive_thread_args));
pthread_t *threads = malloc(num_scratches * sizeof(pthread_t));
for (size_t i = 0; i < num_scratches; i++) {
args[i].rate = rate;
args[i].order = order;
args[i].items = items;
args[i].items_len = items_len;
args[i].scratch = scratches[i];
args[i].msg = msg;
args[i].msg_len = n_bytes;
args[i].test_offset = i;
args[i].bpsk_voltage = bpsk_voltage;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[i], &attr, search_exhaustive_thread, &args[i]);
}
for (size_t i = 0; i < num_scratches; i++) {
pthread_join(threads[i], NULL);
}
free(args);
free(threads);
}
void search_exhaustive_init(conv_result_t *items, size_t items_len,
size_t num_scratches) {
for (size_t i = 0; i < items_len; i++) {
for (size_t j = 0; j < num_scratches; j++) {
items[i].distances[j] = 0;
}
}
}
void search_exhaustive_fin(conv_result_t *items, size_t items_len,
float *weights, size_t weights_len) {
for (size_t i = 0; i < items_len; i++) {
items[i].cost = 0;
for (size_t j = 0; j < weights_len; j++) {
items[i].cost += weights[j] * items[i].distances[j];
}
}
qsort(items, items_len, sizeof(conv_result_t), compare_conv_results);
}
const size_t max_block_len = 16384;
const size_t max_msg_len = 50000000;
void test(size_t rate, size_t order,
conv_tester_t start, conv_testbench **scratches,
size_t num_scratches, float *weights,
size_t n_bytes, double *eb_n0,
double bpsk_bit_energy, size_t n_iter,
double bpsk_voltage) {
uint8_t *msg = malloc(max_block_len * sizeof(uint8_t));
correct_convolutional_polynomial_t maxcoeff = (1 << order) - 1;
correct_convolutional_polynomial_t startcoeff = (1 << (order - 1)) + 1;
size_t num_polys = (maxcoeff - startcoeff) / 2 + 1;
size_t convs_len = 1;
for (size_t i = 0; i < rate; i++) {
convs_len *= num_polys;
}
conv_result_t *exhaustive = malloc(convs_len * sizeof(conv_result_t));
correct_convolutional_polynomial_t *iter_poly = malloc(rate * sizeof(correct_convolutional_polynomial_t));
for (size_t i = 0; i < rate; i++) {
iter_poly[i] = startcoeff;
}
// init exhaustive with all polys
for (size_t i = 0; i < convs_len; i++) {
exhaustive[i].poly = malloc(rate * sizeof(correct_convolutional_polynomial_t));
exhaustive[i].distances = calloc(num_scratches, sizeof(int));
exhaustive[i].cost = 0;
memcpy(exhaustive[i].poly, iter_poly, rate * sizeof(correct_convolutional_polynomial_t));
// this next loop adds 2 with "carry"
for (size_t j = 0; j < rate; j++) {
if (iter_poly[j] < maxcoeff) {
iter_poly[j] += 2;
// no more carries to propagate
break;
} else {
iter_poly[j] = startcoeff;
}
}
}
free(iter_poly);
while (convs_len > 20) {
size_t bytes_remaining = n_bytes;
// call init(), which sets all the error metrics to 0 for our new run
search_exhaustive_init(exhaustive, convs_len, num_scratches);
while (bytes_remaining) {
// in order to keep memory usage constant, we separate the msg into
// blocks and send each one through
// each time we do this, we have to calculate a new noise for each
// testbench
size_t block_len = (max_block_len < bytes_remaining) ? max_block_len : bytes_remaining;
bytes_remaining -= block_len;
for (unsigned int j = 0; j < block_len; j++) {
msg[j] = rand() % 256;
}
for (size_t i = 0; i < num_scratches; i++) {
scratches[i] = resize_conv_testbench(scratches[i], conv_enclen, start.conv, block_len);
build_white_noise(scratches[i]->noise, scratches[i]->enclen, eb_n0[i], bpsk_bit_energy);
}
search_exhaustive(rate, order,
block_len, msg, scratches, num_scratches, weights,
exhaustive, convs_len, bpsk_voltage);
}
// call fin(), which calculates a cost metric for all of the distances
// added by our msg block iterations and then sorts by this metric
search_exhaustive_fin(exhaustive, convs_len, weights, num_scratches);
// decide parameters for next loop iter
// if we've reduced to 20 or fewer items, we're going to just select
// those and declare the test done
size_t new_convs_len = (convs_len / 2) < 20 ? 20 : convs_len / 2;
// normally we'll double the message length each time we halve
// the number of entries so that each iter takes roughly the
// same time but has twice the resolution of the previous run.
//
// however, if we've reached max_msg_len, then we assume that
// the error stats collected are likely converged to whatever
// final value they'll take, and adding more length will not
// help us get better metrics. if we're at that point, then
// we just select the top 20 items and declare them winners
if (n_bytes >= max_msg_len) {
// converged case
new_convs_len = 20;
} else {
// increase our error metric resolution next run
n_bytes *= 2;
n_bytes = (n_bytes < max_msg_len) ? n_bytes : max_msg_len;
}
for (size_t i = new_convs_len; i < convs_len; i++) {
// these entries lost, free their memory here
free(exhaustive[i].poly);
free(exhaustive[i].distances);
}
convs_len = new_convs_len;
printf("exhaustive run: %zu items remain\n", convs_len);
}
for (size_t i = 0; i < convs_len; i++) {
for (size_t j = 0; j < rate; j++) {
printf(" %06o", exhaustive[i].poly[j]);
}
printf(":");
for (size_t j = 0; j < num_scratches; j++) {
printf(" %.2e@%.1fdB", exhaustive[i].distances[j]/((float)n_bytes * 8), eb_n0[j]);
}
printf("\n");
}
for (size_t i = 0; i < convs_len; i++) {
free(exhaustive[i].poly);
free(exhaustive[i].distances);
}
free(exhaustive);
free(msg);
}
int main(int argc, char **argv) {
srand(time(NULL));
size_t rate, order, n_bytes, n_iter;
sscanf(argv[1], "%zu", &rate);
sscanf(argv[2], "%zu", &order);
sscanf(argv[3], "%zu", &n_bytes);
sscanf(argv[4], "%zu", &n_iter);
double bpsk_voltage = 1.0/sqrt(2.0);
double bpsk_sym_energy = 2*pow(bpsk_voltage, 2.0);
double bpsk_bit_energy = bpsk_sym_energy/1.0;
bpsk_bit_energy = bpsk_sym_energy * rate; // rate bits transmitted for every input bit
correct_convolutional_polynomial_t maxcoeff = (1 << order) - 1;
correct_convolutional_polynomial_t startcoeff = (1 << (order - 1)) + 1;
conv_tester_t start;
start.poly = malloc(rate * sizeof(correct_convolutional_polynomial_t));
for (size_t i = 0; i < rate; i++) {
start.poly[i] = ((maxcoeff - startcoeff) / 2) + startcoeff + 1;
}
start.conv = conv_create(rate, order, start.poly);
size_t num_scratches = 4;
float *weights;
conv_testbench **scratches = malloc(num_scratches * sizeof(conv_testbench *));
double *eb_n0;
for (size_t i = 0; i < num_scratches; i++) {
scratches[i] = resize_conv_testbench(NULL, conv_enclen, start.conv, max_block_len);
}
switch (order) {
case 6:
eb_n0 = (double[]){6.0, 5.5, 5.0, 4.5};
weights = (float[]){8000, 400, 20, 1};
break;
case 7:
eb_n0 = (double[]){5.5, 5.0, 4.5, 4.0};
weights = (float[]){8000, 400, 20, 1};
break;
case 8:
case 9:
eb_n0 = (double[]){5.0, 4.5, 4.0, 3.5};
weights = (float[]){8000, 400, 20, 1};
break;
default:
eb_n0 = (double[]){4.5, 4.0, 3.5, 3.0};
weights = (float[]){8000, 400, 20, 1};
}
test(rate, order, start, scratches, num_scratches, weights, n_bytes, eb_n0, bpsk_bit_energy, n_iter, bpsk_voltage);
free(start.poly);
conv_destroy(start.conv);
for (size_t i = 0; i < num_scratches; i++) {
free_scratch(scratches[i]);
}
free(scratches);
return 0;
}

View File

@ -0,0 +1,350 @@
#include <stdbool.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stddef.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#if HAVE_SSE
#include "correct/util/error-sim-sse.h"
typedef correct_convolutional_sse conv_t;
static conv_t*(*conv_create)(size_t, size_t, const uint16_t *) = correct_convolutional_sse_create;
static void(*conv_destroy)(conv_t *) = correct_convolutional_sse_destroy;
static size_t(*conv_enclen)(void *, size_t) = conv_correct_sse_enclen;
static void(*conv_encode)(void *, uint8_t *, size_t, uint8_t *) = conv_correct_sse_encode;
static void(*conv_decode)(void *, uint8_t *, size_t, uint8_t *) = conv_correct_sse_decode;
#else
#include "correct/util/error-sim.h"
typedef correct_convolutional conv_t;
static conv_t*(*conv_create)(size_t, size_t, const uint16_t *) = correct_convolutional_create;
static void(*conv_destroy)(conv_t *) = correct_convolutional_destroy;
static size_t(*conv_enclen)(void *, size_t) = conv_correct_enclen;
static void(*conv_encode)(void *, uint8_t *, size_t, uint8_t *) = conv_correct_encode;
static void(*conv_decode)(void *, uint8_t *, size_t, uint8_t *) = conv_correct_decode;
#endif
typedef struct {
conv_t *conv;
correct_convolutional_polynomial_t *poly;
} conv_tester_t;
void shuffle(int *a, size_t len) {
for (size_t i = 0; i < len - 2; i++) {
size_t j = rand() % (len - i) + i;
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
int rand_geo(float p, int max) {
int geo = 1;
while (geo < max) {
if (rand() / (float)RAND_MAX > p) {
geo++;
} else {
break;
}
}
return geo;
}
void next_neighbor(correct_convolutional_polynomial_t *start,
correct_convolutional_polynomial_t *neighbor, size_t rate, size_t order) {
int coeffs[rate * (order - 2)];
for (int i = 0; i < rate * (order - 2); i++) {
coeffs[i] = i;
}
shuffle(coeffs, rate * (order - 2));
memcpy(neighbor, start, rate * sizeof(correct_convolutional_polynomial_t));
size_t nflips = rand_geo(0.4, rate * (order - 2));
for (int i = 0; i < nflips; i++) {
ptrdiff_t index = coeffs[i] / (order - 2);
// decide which bit to flip
// we avoid the edge bits to prevent creating a degenerate poly
neighbor[index] ^= 1 << (coeffs[i] % (order - 2) + 1);
}
}
bool accept(float cost_a, float cost_b, double temperature) {
if (cost_b < cost_a) {
return true;
}
float p = (float)(rand()) / (float)(RAND_MAX);
return exp((cost_a - cost_b) / (cost_a * temperature)) > p;
}
typedef struct {
size_t rate;
size_t order;
correct_convolutional_polynomial_t *poly;
unsigned int distance;
conv_testbench *scratch;
size_t msg_len;
double eb_n0;
double bpsk_voltage;
double bpsk_bit_energy;
} thread_args;
const size_t max_block_len = 16384;
void *find_cost_thread(void *vargs) {
thread_args *args = (thread_args *)vargs;
conv_t *conv;
uint8_t *msg = malloc(max_block_len);
conv = conv_create(args->rate, args->order, args->poly);
args->distance = 0;
conv_testbench *scratch = args->scratch;
size_t bytes_remaining = args->msg_len;
while (bytes_remaining) {
// in order to keep memory usage constant, we separate the msg into
// blocks and send each one through
// each time we do this, we have to calculate a new noise for each
// testbench
size_t block_len = (max_block_len < bytes_remaining) ? max_block_len : bytes_remaining;
bytes_remaining -= block_len;
for (unsigned int j = 0; j < block_len; j++) {
msg[j] = rand() % 256;
}
scratch = resize_conv_testbench(scratch, conv_enclen, conv, block_len);
scratch->encode = conv_encode;
scratch->encoder = conv;
scratch->decode = conv_decode;
scratch->decoder = conv;
build_white_noise(scratch->noise, scratch->enclen, args->eb_n0, args->bpsk_bit_energy);
args->distance += test_conv_noise(scratch, msg, block_len, args->bpsk_voltage);
}
conv_destroy(conv);
free(msg);
pthread_exit(NULL);
}
float find_cost(size_t rate, size_t order, correct_convolutional_polynomial_t *poly, size_t msg_len,
conv_testbench **scratches, size_t num_scratches, float *weights, double *eb_n0,
double bpsk_voltage, double bpsk_bit_energy) {
thread_args *args = malloc(num_scratches * sizeof(thread_args));
pthread_t *threads = malloc(num_scratches * sizeof(pthread_t));
for (size_t i = 0; i < num_scratches; i++) {
args[i].rate = rate;
args[i].order = order;
args[i].poly = poly;
args[i].scratch = scratches[i];
args[i].msg_len = msg_len;
args[i].eb_n0 = eb_n0[i];
args[i].bpsk_voltage = bpsk_voltage;
args[i].bpsk_bit_energy = bpsk_bit_energy;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[i], &attr, find_cost_thread, &args[i]);
}
for (size_t i = 0; i < num_scratches; i++) {
pthread_join(threads[i], NULL);
}
float cost = 0;
printf("poly:");
for (size_t i = 0; i < rate; i++) {
printf(" %06o", poly[i]);
}
printf(" error:");
for (size_t i = 0; i < num_scratches; i++) {
cost += weights[i] * args[i].distance;
printf(" %.2e@%.1fdB", (args[i].distance / (float)(msg_len * 8)), eb_n0[i]);
}
printf("\n");
free(args);
free(threads);
return cost;
}
static bool terminated = false;
void sig_handler(int sig) {
if (sig == SIGINT || sig == SIGTERM || sig == SIGHUP) {
if (!terminated) {
terminated = true;
printf("terminating after current poly\n");
}
}
}
void search_simulated_annealing(size_t rate, size_t order, size_t n_steps, conv_tester_t *start,
size_t n_bytes, conv_testbench **scratches, size_t num_scratches,
float *weights, double start_temperature, double cooling_factor,
double *eb_n0, double bpsk_voltage, double bpsk_bit_energy) {
// perform simulated annealing to find the optimal polynomial
float cost = find_cost(rate, order, start->poly, n_bytes, scratches, num_scratches, weights,
eb_n0, bpsk_voltage, bpsk_bit_energy);
correct_convolutional_polynomial_t *neighbor_poly =
malloc(rate * sizeof(correct_convolutional_polynomial_t));
correct_convolutional_polynomial_t *state =
malloc(rate * sizeof(correct_convolutional_polynomial_t));
correct_convolutional_polynomial_t *best =
malloc(rate * sizeof(correct_convolutional_polynomial_t));
float best_cost = cost;
memcpy(state, start->poly, rate * sizeof(correct_convolutional_polynomial_t));
memcpy(best, start->poly, rate * sizeof(correct_convolutional_polynomial_t));
double temperature = start_temperature;
for (size_t i = 0; i < n_steps; i++) {
next_neighbor(state, neighbor_poly, rate, order);
float neighbor_cost =
find_cost(rate, order, neighbor_poly, n_bytes, scratches, num_scratches, weights, eb_n0,
bpsk_voltage, bpsk_bit_energy);
if (accept(cost, neighbor_cost, temperature)) {
// we're moving to our neighbor's house
memcpy(state, neighbor_poly, rate * sizeof(correct_convolutional_polynomial_t));
cost = neighbor_cost;
} else {
// actually where we live now is nice
}
if (cost < best_cost) {
best_cost = cost;
memcpy(best, state, rate * sizeof(correct_convolutional_polynomial_t));
}
temperature *= cooling_factor;
if (terminated) {
break;
}
}
printf("last state:");
for (size_t i = 0; i < rate; i++) {
printf(" %06o", state[i]);
}
printf("\n");
printf("best state:");
for (size_t i = 0; i < rate; i++) {
printf(" %06o", best[i]);
}
memcpy(start->poly, best, rate * sizeof(correct_convolutional_polynomial_t));
free(state);
free(best);
free(neighbor_poly);
}
void test_sa(size_t rate, size_t order, conv_tester_t start, conv_testbench **scratches,
size_t num_scratches, float *weights, size_t n_bytes, double *eb_n0,
double bpsk_bit_energy, size_t n_iter, double bpsk_voltage) {
for (size_t i = 0; i < n_iter; i++) {
double temperature = (i == 0) ? 0.5 : 250;
double cooling_factor = (i == 0) ? 0.985 : 0.95;
size_t n_steps = (i == 0) ? 500 : 100;
search_simulated_annealing(rate, order, n_steps, &start, n_bytes, scratches, num_scratches,
weights, temperature, cooling_factor, eb_n0, bpsk_voltage,
bpsk_bit_energy);
}
}
int main(int argc, char **argv) {
srand(time(NULL));
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
signal(SIGHUP, sig_handler);
size_t rate, order, n_bytes, n_iter;
sscanf(argv[1], "%zu", &rate);
sscanf(argv[2], "%zu", &order);
sscanf(argv[3], "%zu", &n_bytes);
sscanf(argv[4], "%zu", &n_iter);
double bpsk_voltage = 1.0 / sqrt(2.0);
double bpsk_sym_energy = 2 * pow(bpsk_voltage, 2.0);
double bpsk_bit_energy = bpsk_sym_energy / 1.0;
bpsk_bit_energy = bpsk_sym_energy * rate; // rate bits transmitted for every input bit
// correct_convolutional_polynomial_t maxcoeff = (1 << order) - 1;
correct_convolutional_polynomial_t startcoeff = (1 << (order - 1)) + 1;
conv_tester_t start;
start.poly = malloc(rate * sizeof(correct_convolutional_polynomial_t));
for (size_t i = 0; i < rate; i++) {
start.poly[i] = ((rand() % (1 << (order - 2))) << 1) + startcoeff;
}
start.conv = conv_create(rate, order, start.poly);
size_t num_scratches = 4;
float *weights;
conv_testbench **scratches = malloc(num_scratches * sizeof(conv_testbench *));
double *eb_n0;
for (size_t i = 0; i < num_scratches; i++) {
scratches[i] = resize_conv_testbench(NULL, conv_enclen, start.conv, max_block_len);
}
switch (order) {
case 6:
eb_n0 = (double[]){6.0, 5.5, 5.0, 4.5};
weights = (float[]){8000, 400, 20, 1};
break;
case 7:
case 8:
eb_n0 = (double[]){5.5, 5.0, 4.5, 4.0};
weights = (float[]){8000, 400, 20, 1};
break;
case 9:
case 10:
eb_n0 = (double[]){5.0, 4.5, 4.0, 3.5};
weights = (float[]){8000, 400, 20, 1};
break;
case 11:
case 12:
case 13:
eb_n0 = (double[]){4.5, 4.0, 3.5, 3.0};
weights = (float[]){8000, 400, 20, 1};
break;
default:
eb_n0 = (double[]){3.5, 3.0, 2.5, 2.0};
weights = (float[]){8000, 400, 20, 1};
}
test_sa(rate, order, start, scratches, num_scratches, weights, n_bytes, eb_n0, bpsk_bit_energy,
n_iter, bpsk_voltage);
free(start.poly);
conv_destroy(start.conv);
for (size_t i = 0; i < num_scratches; i++) {
free_scratch(scratches[i]);
}
free(scratches);
return 0;
}

View File

@ -0,0 +1,51 @@
#include "correct/reed-solomon.h"
size_t block_size = 255;
int power_max = 8;
// visit all of the elements from the poly
bool trypoly(field_operation_t poly, field_logarithm_t *log) {
memset(log, 0, block_size + 1);
field_operation_t element = 1;
log[0] = (field_logarithm_t)0;
for (field_operation_t i = 1; i < block_size + 1; i++) {
element = element * 2;
element = (element > block_size) ? (element ^ poly) : element;
if (log[element] != 0) {
return false;
}
log[element] = (field_logarithm_t)i;
}
return true;
}
int main() {
field_logarithm_t *log = malloc((block_size + 1) * sizeof(field_logarithm_t));
for (field_operation_t i = (block_size + 1); i < (block_size + 1) << 1; i++) {
if (trypoly(i, log)) {
printf("0x%x valid: ", i);
field_operation_t poly = i;
int power = power_max;
while(poly) {
if (poly & (block_size + 1)) {
if (power > 1) {
printf("x^%d", power);
} else if (power) {
printf("x");
} else {
printf("1");
}
if (poly & block_size) {
printf(" + ");
}
}
power--;
poly <<= 1;
poly &= (block_size << 1) + 1;
}
printf("\n");
}
}
free(log);
return 0;
}

View File

@ -0,0 +1,11 @@
add_library(error_sim OBJECT error-sim.c)
add_library(error_sim_shim OBJECT error-sim.c error-sim-shim.c)
if(HAVE_LIBFEC)
add_library(error_sim_fec OBJECT error-sim.c error-sim-fec.c)
endif()
if(HAVE_SSE)
add_library(error_sim_sse OBJECT error-sim.c error-sim-sse.c)
endif()

View File

@ -0,0 +1,29 @@
#include "correct/util/error-sim-fec.h"
void conv_fec27_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
init_viterbi27(conv_v, 0);
update_viterbi27_blk(conv_v, soft, soft_len / 2 - 2);
size_t n_decoded_bits = (soft_len / 2) - 8;
chainback_viterbi27(conv_v, msg, n_decoded_bits, 0);
}
void conv_fec29_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
init_viterbi29(conv_v, 0);
update_viterbi29_blk(conv_v, soft, soft_len / 2 - 2);
size_t n_decoded_bits = (soft_len / 2) - 10;
chainback_viterbi29(conv_v, msg, n_decoded_bits, 0);
}
void conv_fec39_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
init_viterbi39(conv_v, 0);
update_viterbi39_blk(conv_v, soft, soft_len / 3 - 2);
size_t n_decoded_bits = (soft_len / 3) - 10;
chainback_viterbi39(conv_v, msg, n_decoded_bits, 0);
}
void conv_fec615_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
init_viterbi615(conv_v, 0);
update_viterbi615_blk(conv_v, soft, soft_len / 6 - 2);
size_t n_decoded_bits = (soft_len / 6) - 16;
chainback_viterbi615(conv_v, msg, n_decoded_bits, 0);
}

View File

@ -0,0 +1,33 @@
#include "correct/util/error-sim-shim.h"
ssize_t conv_shim27_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
init_viterbi27(conv_v, 0);
update_viterbi27_blk(conv_v, soft, soft_len / 2 - 2);
size_t n_decoded_bits = (soft_len / 2) - 8;
chainback_viterbi27(conv_v, msg, n_decoded_bits, 0);
return (n_decoded_bits % 8) ? (n_decoded_bits / 8) + 1 : n_decoded_bits / 8;
}
ssize_t conv_shim29_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
init_viterbi29(conv_v, 0);
update_viterbi29_blk(conv_v, soft, soft_len / 2 - 2);
size_t n_decoded_bits = (soft_len / 2) - 10;
chainback_viterbi29(conv_v, msg, n_decoded_bits, 0);
return (n_decoded_bits % 8) ? (n_decoded_bits / 8) + 1 : n_decoded_bits / 8;
}
ssize_t conv_shim39_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
init_viterbi39(conv_v, 0);
update_viterbi39_blk(conv_v, soft, soft_len / 3 - 2);
size_t n_decoded_bits = (soft_len / 3) - 10;
chainback_viterbi39(conv_v, msg, n_decoded_bits, 0);
return (n_decoded_bits % 8) ? (n_decoded_bits / 8) + 1 : n_decoded_bits / 8;
}
ssize_t conv_shim615_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
init_viterbi615(conv_v, 0);
update_viterbi615_blk(conv_v, soft, soft_len / 6 - 2);
size_t n_decoded_bits = (soft_len / 6) - 16;
chainback_viterbi615(conv_v, msg, n_decoded_bits, 0);
return (n_decoded_bits % 8) ? (n_decoded_bits / 8) + 1 : n_decoded_bits / 8;
}

View File

@ -0,0 +1,13 @@
#include "correct/util/error-sim-sse.h"
size_t conv_correct_sse_enclen(void *conv_v, size_t msg_len) {
return correct_convolutional_sse_encode_len((correct_convolutional_sse *)conv_v, msg_len);
}
void conv_correct_sse_encode(void *conv_v, uint8_t *msg, size_t msg_len, uint8_t *encoded) {
correct_convolutional_sse_encode((correct_convolutional_sse *)conv_v, msg, msg_len, encoded);
}
ssize_t conv_correct_sse_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
return correct_convolutional_sse_decode_soft((correct_convolutional_sse *)conv_v, soft, soft_len, msg);
}

View File

@ -0,0 +1,188 @@
#include "correct/util/error-sim.h"
size_t distance(uint8_t *a, uint8_t *b, size_t len) {
size_t dist = 0;
for (size_t i = 0; i < len; i++) {
if (a[i] != b[i]) {
}
dist += popcount((unsigned int)a[i] ^ (unsigned int)b[i]);
}
return dist;
}
void gaussian(double *res, size_t n_res, double sigma) {
for (size_t i = 0; i < n_res; i += 2) {
// compute using polar method of box muller
double s, u, v;
while (true) {
u = (double)(rand())/(double)RAND_MAX;
v = (double)(rand())/(double)RAND_MAX;
s = pow(u, 2.0) + pow(v, 2.0);
if (s > DBL_EPSILON && s < 1) {
break;
}
}
double base = sqrt((-2.0 * log(s))/s);
double z0 = u * base;
res[i] = z0 * sigma;
if (i + 1 < n_res) {
double z1 = v * base;
res[i + 1] = z1 * sigma;
}
}
}
void encode_bpsk(uint8_t *msg, double *voltages, size_t n_syms, double bpsk_voltage) {
uint8_t mask = 0x80;
for (size_t i = 0; i < n_syms; i++) {
voltages[i] = msg[i/8] & mask ? bpsk_voltage : -bpsk_voltage;
mask >>= 1;
if (!mask) {
mask = 0x80;
}
}
}
void byte2bit(uint8_t *bytes, uint8_t *bits, size_t n_bits) {
unsigned char cmask = 0x80;
for (size_t i = 0; i < n_bits; i++) {
bits[i] = (bytes[i/8] & cmask) ? 255 : 0;
cmask >>= 1;
if (!cmask) {
cmask = 0x80;
}
}
}
void decode_bpsk(uint8_t *soft, uint8_t *msg, size_t n_syms) {
uint8_t mask = 0x80;
for (size_t i = 0; i < n_syms; i++) {
uint8_t bit = soft[i] > 127 ? 1 : 0;
if (bit) {
msg[i/8] |= mask;
}
mask >>= 1;
if (!mask) {
mask = 0x80;
}
}
}
void decode_bpsk_soft(double *voltages, uint8_t *soft, size_t n_syms, double bpsk_voltage) {
for (size_t i = 0; i < n_syms; i++) {
double rel = voltages[i]/bpsk_voltage;
if (rel > 1) {
soft[i] = 255;
} else if (rel < -1) {
soft[i] = 0;
} else {
soft[i] = (uint8_t)(127.5 + 127.5 * rel);
}
}
}
double log2amp(double l) {
return pow(10.0, l/10.0);
}
double amp2log(double a) {
return 10.0 * log10(a);
}
double sigma_for_eb_n0(double eb_n0, double bpsk_bit_energy) {
// eb/n0 is the ratio of bit energy to noise energy
// eb/n0 is expressed in dB so first we convert to amplitude
double eb_n0_amp = log2amp(eb_n0);
// now the conversion. sigma^2 = n0/2 = ((eb/n0)^-1 * eb)/2 = eb/(2 * (eb/n0))
return sqrt(bpsk_bit_energy/(double)(2.0 * eb_n0_amp));
}
void build_white_noise(double *noise, size_t n_syms, double eb_n0, double bpsk_bit_energy) {
double sigma = sigma_for_eb_n0(eb_n0, bpsk_bit_energy);
gaussian(noise, n_syms, sigma);
}
void add_white_noise(double *signal, double *noise, size_t n_syms) {
const double sqrt_2 = sqrt(2);
for (size_t i = 0; i < n_syms; i++) {
// we want to add the noise in to the signal
// but we can't add them directly, because they're expressed as magnitudes
// and the signal is real valued while the noise is complex valued
// we'll assume that the noise is exactly half real, half imaginary
// which means it forms a 90-45-45 triangle in the complex plane
// that means that the magnitude we have here is sqrt(2) * the real valued portion
// so, we'll divide by sqrt(2)
// (we are effectively throwing away the complex portion)
signal[i] += noise[i]/sqrt_2;
}
}
conv_testbench *resize_conv_testbench(conv_testbench *scratch, size_t (*enclen_f)(void *, size_t), void *enc, size_t msg_len) {
if (!scratch) {
scratch = calloc(1, sizeof(conv_testbench));
}
scratch->msg_out = realloc(scratch->msg_out, msg_len);
size_t enclen = enclen_f(enc, msg_len);
size_t enclen_bytes = (enclen % 8) ? (enclen/8 + 1) : enclen/8;
scratch->enclen = enclen;
scratch->enclen_bytes = enclen_bytes;
scratch->encoded = realloc(scratch->encoded, enclen_bytes);
scratch->v = realloc(scratch->v, enclen * sizeof(double));
scratch->corrupted = realloc(scratch->corrupted, enclen * sizeof(double));
scratch->noise = realloc(scratch->noise, enclen * sizeof(double));
scratch->soft = realloc(scratch->soft, enclen);
return scratch;
}
void free_scratch(conv_testbench *scratch) {
free(scratch->msg_out);
free(scratch->encoded);
free(scratch->v);
free(scratch->corrupted);
free(scratch->soft);
free(scratch->noise);
free(scratch);
}
int test_conv_noise(conv_testbench *scratch, uint8_t *msg, size_t n_bytes,
double bpsk_voltage) {
scratch->encode(scratch->encoder, msg, n_bytes, scratch->encoded);
encode_bpsk(scratch->encoded, scratch->v, scratch->enclen, bpsk_voltage);
memcpy(scratch->corrupted, scratch->v, scratch->enclen * sizeof(double));
add_white_noise(scratch->corrupted, scratch->noise, scratch->enclen);
decode_bpsk_soft(scratch->corrupted, scratch->soft, scratch->enclen, bpsk_voltage);
memset(scratch->msg_out, 0, n_bytes);
ssize_t decode_len = scratch->decode(scratch->decoder, scratch->soft, scratch->enclen, scratch->msg_out);
if (decode_len != n_bytes) {
printf("expected to decode %zu bytes, decoded %zu bytes instead\n", n_bytes, decode_len);
exit(1);
}
return distance((uint8_t*)msg, scratch->msg_out, n_bytes);
}
size_t conv_correct_enclen(void *conv_v, size_t msg_len) {
return correct_convolutional_encode_len((correct_convolutional *)conv_v, msg_len);
}
void conv_correct_encode(void *conv_v, uint8_t *msg, size_t msg_len, uint8_t *encoded) {
correct_convolutional_encode((correct_convolutional *)conv_v, msg, msg_len, encoded);
}
ssize_t conv_correct_decode(void *conv_v, uint8_t *soft, size_t soft_len, uint8_t *msg) {
return correct_convolutional_decode_soft((correct_convolutional *)conv_v, soft, soft_len, msg);
}

View File

@ -37,6 +37,32 @@
#endif
#endif
const char* OPENGL_VERSIONS_GLSL[] = {
"#version 120",
"#version 300 es",
"#version 120"
};
const int OPENGL_VERSIONS_MAJOR[] = {
3,
3,
2
};
const int OPENGL_VERSIONS_MINOR[] = {
0,
1,
1
};
const bool OPENGL_VERSIONS_IS_ES[] = {
false,
true,
false
};
#define OPENGL_VERSION_COUNT (sizeof(OPENGL_VERSIONS_GLSL) / sizeof(char*))
namespace core {
ConfigManager configManager;
ModuleManager moduleManager;
@ -52,7 +78,7 @@ namespace core {
gui::waterfall.setViewOffset(0);
gui::waterfall.setViewBandwidth(effectiveSr);
sigpath::signalPath.setSampleRate(effectiveSr);
gui::mainWindow.setViewBandwidthSlider(effectiveSr);
gui::mainWindow.setViewBandwidthSlider(1.0);
}
};
@ -180,6 +206,7 @@ int sdrpp_main(int argc, char *argv[]) {
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
defConfig["moduleInstances"]["Network Sink"] = "network_sink";
defConfig["moduleInstances"]["Radio"] = "radio";
@ -258,26 +285,6 @@ int sdrpp_main(int argc, char *argv[]) {
if (options::opts.serverMode) { return server_main(); }
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) {
return 1;
}
#ifdef __APPLE__
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
// GL 3.0 + GLSL 120
const char* glsl_version = "#version 120";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#endif
core::configManager.acquire();
int winWidth = core::configManager.conf["windowSize"]["w"];
int winHeight = core::configManager.conf["windowSize"]["h"];
@ -291,13 +298,51 @@ int sdrpp_main(int argc, char *argv[]) {
return 1;
}
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) {
return 1;
}
#ifdef __APPLE__
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
// Create window with graphics context
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
core::window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
if (core::window == NULL)
return 1;
glfwMakeContextCurrent(core::window);
#else
const char* glsl_version = "#version 120";
GLFWmonitor* monitor = NULL;
for (int i = 0; i < OPENGL_VERSION_COUNT; i++) {
glsl_version = OPENGL_VERSIONS_GLSL[i];
glfwWindowHint(GLFW_CLIENT_API, OPENGL_VERSIONS_IS_ES[i] ? GLFW_OPENGL_ES_API : GLFW_OPENGL_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_VERSIONS_MAJOR[i]);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_VERSIONS_MINOR[i]);
// Create window with graphics context
monitor = glfwGetPrimaryMonitor();
core::window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
if (core::window == NULL) {
spdlog::info("OpenGL {0}.{1} {2}was not supported", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? "ES ": "");
continue;
}
spdlog::info("Using OpenGL {0}.{1}{2}", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? " ES": "");
glfwMakeContextCurrent(core::window);
break;
}
#endif
// Add callback for max/min if GLFW supports it
#if (GLFW_VERSION_MAJOR == 3) && (GLFW_VERSION_MINOR >= 3)
if (maximized) {
glfwMaximizeWindow(core::window);

View File

@ -17,6 +17,8 @@ namespace sdrpp_credits {
"Paulo Matias",
"Raov",
"Cam K.",
"Shuyuan Liu",
"Syne Ardwin (WI9SYN)",
"Szymon Zakrent",
"Tobias Mädel",
"Zimm"
@ -33,16 +35,23 @@ namespace sdrpp_credits {
};
const char* patrons[] = {
"Bob Logan",
"Christian Häusler",
"Croccydile",
"Dale L Puckett (K0HYD)",
"Daniele D'Agnelli",
"EB3FRN",
"Eric Johnson",
"W4IPA",
"Flinger Films",
"Kezza",
"Lee Donaghy",
".lozenge. (Hank Hill)",
"ON4MU",
"Passion-Radio.com",
"Scanner School",
"SignalsEverywhere",
"Syne Ardwin (WI9SYN)"
"Syne Ardwin (WI9SYN)",
"W4IPA"
};
const int contributorCount = sizeof(contributors) / sizeof(char*);

View File

@ -80,7 +80,7 @@ namespace dsp {
if (count_r < 0) { return -1; }
if (count_l != count_r) {
spdlog::warn("ChannelsToStereo block size missmatch");
spdlog::warn("ChannelsToStereo block size mismatch");
}
volk_32f_x2_interleave_32fc((lv_32fc_t*)out.writeBuf, _in_left->readBuf, _in_right->readBuf, count_l);

View File

@ -171,7 +171,7 @@ namespace dsp {
_c_2T = _c_1T;
_c_1T = _c_0T;
// Perfrom interpolation the same way as for float values
// Perform interpolation the same way as for float values
if (i < 7) {
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&_p_0T, (lv_32fc_t*)&delay[i], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
}

161
core/src/dsp/compression.h Normal file
View File

@ -0,0 +1,161 @@
#pragma once
#include <dsp/block.h>
namespace dsp {
class DynamicRangeCompressor : public generic_block<DynamicRangeCompressor> {
public:
DynamicRangeCompressor() {}
enum PCMType {
PCM_TYPE_I8,
PCM_TYPE_I16,
PCM_TYPE_F32
};
DynamicRangeCompressor(stream<complex_t>* in, PCMType pcmType) { init(in, pcmType); }
void init(stream<complex_t>* in, PCMType pcmType) {
_in = in;
_pcmType = pcmType;
generic_block<DynamicRangeCompressor>::registerInput(_in);
generic_block<DynamicRangeCompressor>::registerOutput(&out);
generic_block<DynamicRangeCompressor>::_block_init = true;
}
void setInput(stream<complex_t>* in) {
assert(generic_block<DynamicRangeCompressor>::_block_init);
std::lock_guard<std::mutex> lck(generic_block<DynamicRangeCompressor>::ctrlMtx);
generic_block<DynamicRangeCompressor>::tempStop();
generic_block<DynamicRangeCompressor>::unregisterInput(_in);
_in = in;
generic_block<DynamicRangeCompressor>::registerInput(_in);
generic_block<DynamicRangeCompressor>::tempStart();
}
void setPCMType(PCMType pcmType) {
assert(generic_block<DynamicRangeCompressor>::_block_init);
std::lock_guard<std::mutex> lck(generic_block<DynamicRangeCompressor>::ctrlMtx);
_pcmType = pcmType;
}
int run() {
int count = _in->read();
if (count < 0) { return -1; }
float* scaler = (float*)out.writeBuf;
void* dataBuf = &out.writeBuf[4];
// If no dynamic range compression is to be done, just pass the data to the output with a null scaler
if (_pcmType == PCM_TYPE_F32) {
*scaler = 0;
memcpy(dataBuf, _in->readBuf, count * sizeof(complex_t));
_in->flush();
if (!out.swap(4 + (count * sizeof(complex_t)))) { return -1; }
return count;
}
// Find maximum value
complex_t val;
float absre;
float absim;
float maxVal = 0;
for (int i = 0; i < count; i++) {
val = _in->readBuf[i];
absre = fabsf(val.re);
absim = fabsf(val.im);
if (absre > maxVal) { maxVal = absre; }
if (absim > maxVal) { maxVal = absim; }
}
// Convert to the right type and send it out (sign bit determins pcm type)
if (_pcmType == PCM_TYPE_I8) {
*scaler = maxVal;
volk_32f_s32f_convert_8i((int8_t*)dataBuf, (float*)_in->readBuf, 128.0f / maxVal, count * 2);
_in->flush();
if (!out.swap(4 + (count * sizeof(int8_t) * 2))) { return -1; }
}
else if (_pcmType == PCM_TYPE_I16) {
*scaler = -maxVal;
volk_32f_s32f_convert_16i((int16_t*)dataBuf, (float*)_in->readBuf, 32768.0f / maxVal, count * 2);
_in->flush();
if (!out.swap(4 + (count * sizeof(int16_t) * 2))) { return -1; }
}
else {
_in->flush();
}
return count;
}
stream<uint8_t> out;
private:
stream<complex_t>* _in;
PCMType _pcmType;
};
class DynamicRangeDecompressor : public generic_block<DynamicRangeDecompressor> {
public:
DynamicRangeDecompressor() {}
DynamicRangeDecompressor(stream<uint8_t>* in) { init(in); }
void init(stream<uint8_t>* in) {
_in = in;
generic_block<DynamicRangeDecompressor>::registerInput(_in);
generic_block<DynamicRangeDecompressor>::registerOutput(&out);
generic_block<DynamicRangeDecompressor>::_block_init = true;
}
void setInput(stream<uint8_t>* in) {
assert(generic_block<DynamicRangeDecompressor>::_block_init);
std::lock_guard<std::mutex> lck(generic_block<DynamicRangeDecompressor>::ctrlMtx);
generic_block<DynamicRangeDecompressor>::tempStop();
generic_block<DynamicRangeDecompressor>::unregisterInput(_in);
_in = in;
generic_block<DynamicRangeDecompressor>::registerInput(_in);
generic_block<DynamicRangeDecompressor>::tempStart();
}
int run() {
int count = _in->read();
if (count < 0) { return -1; }
float* scaler = (float*)_in->readBuf;
void* dataBuf = &_in->readBuf[4];
// If the scaler is null, data is F32
if (*scaler == 0) {
memcpy(out.writeBuf, dataBuf, count - 4);
_in->flush();
if (!out.swap((count - 4) / sizeof(complex_t))) { return -1; }
return count;
}
// Convert back to f32 from the pcm type
float absScale = fabsf(*scaler);
if (*scaler > 0) {
spdlog::warn("{0}", absScale);
int outCount = (count - 4) / (sizeof(int8_t) * 2);
volk_8i_s32f_convert_32f((float*)out.writeBuf, (int8_t*)dataBuf, 128.0f / absScale, outCount * 2);
_in->flush();
if (!out.swap(outCount)) { return -1; }
}
else {
int outCount = (count - 4) / (sizeof(int16_t) * 2);
volk_16i_s32f_convert_32f((float*)out.writeBuf, (int16_t*)dataBuf, 32768.0f / absScale, outCount * 2);
_in->flush();
if (!out.swap(outCount)) { return -1; }
}
return count;
}
stream<complex_t> out;
private:
stream<uint8_t>* _in;
};
}

View File

@ -73,4 +73,74 @@ namespace dsp {
};
class DCBlocker : public generic_block<DCBlocker> {
public:
DCBlocker() {}
DCBlocker(stream<float>* in, float rate) { init(in, rate); }
void init(stream<float>* in, float rate) {
_in = in;
correctionRate = rate;
offset = 0;
generic_block<DCBlocker>::registerInput(_in);
generic_block<DCBlocker>::registerOutput(&out);
generic_block<DCBlocker>::_block_init = true;
}
void setInput(stream<float>* in) {
assert(generic_block<DCBlocker>::_block_init);
std::lock_guard<std::mutex> lck(generic_block<DCBlocker>::ctrlMtx);
generic_block<DCBlocker>::tempStop();
generic_block<DCBlocker>::unregisterInput(_in);
_in = in;
generic_block<DCBlocker>::registerInput(_in);
generic_block<DCBlocker>::tempStart();
}
void setCorrectionRate(float rate) {
correctionRate = rate;
}
int run() {
int count = _in->read();
if (count < 0) { return -1; }
if (bypass) {
memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
_in->flush();
if (!out.swap(count)) { return -1; }
return count;
}
for (int i = 0; i < count; i++) {
out.writeBuf[i] = _in->readBuf[i] - offset;
offset = offset + (out.writeBuf[i] * correctionRate);
}
_in->flush();
if (!out.swap(count)) { return -1; }
return count;
}
stream<float> out;
// TEMPORARY FOR DEBUG PURPOSES
bool bypass = false;
float offset;
private:
stream<float>* _in;
float correctionRate = 0.00001;
};
}

View File

@ -8,9 +8,10 @@
#include <dsp/pll.h>
#include <dsp/clock_recovery.h>
#include <dsp/math.h>
#include <dsp/convertion.h>
#include <dsp/conversion.h>
#include <dsp/audio.h>
#include <dsp/stereo_fm.h>
#include <dsp/correction.h>
#define FAST_ATAN2_COEF1 FL_M_PI / 4.0f
#define FAST_ATAN2_COEF2 3.0f * FAST_ATAN2_COEF1
@ -371,11 +372,11 @@ namespace dsp {
};
class MSKDemod : public generic_hier_block<MSKDemod> {
class FSKDemod : public generic_hier_block<FSKDemod> {
public:
MSKDemod() {}
FSKDemod() {}
MSKDemod(stream<complex_t>* input, float sampleRate, float deviation, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
FSKDemod(stream<complex_t>* input, float sampleRate, float deviation, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
init(input, sampleRate, deviation, baudRate, omegaGain, muGain, omegaRelLimit);
}
@ -391,47 +392,47 @@ namespace dsp {
recov.init(&demod.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
out = &recov.out;
generic_hier_block<MSKDemod>::registerBlock(&demod);
generic_hier_block<MSKDemod>::registerBlock(&recov);
generic_hier_block<MSKDemod>::_block_init = true;
generic_hier_block<FSKDemod>::registerBlock(&demod);
generic_hier_block<FSKDemod>::registerBlock(&recov);
generic_hier_block<FSKDemod>::_block_init = true;
}
void setInput(stream<complex_t>* input) {
assert((generic_hier_block<MSKDemod>::_block_init));
assert((generic_hier_block<FSKDemod>::_block_init));
demod.setInput(input);
}
void setSampleRate(float sampleRate) {
assert(generic_hier_block<MSKDemod>::_block_init);
generic_hier_block<MSKDemod>::tempStop();
assert(generic_hier_block<FSKDemod>::_block_init);
generic_hier_block<FSKDemod>::tempStop();
_sampleRate = sampleRate;
demod.setSampleRate(_sampleRate);
recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
generic_hier_block<MSKDemod>::tempStart();
generic_hier_block<FSKDemod>::tempStart();
}
void setDeviation(float deviation) {
assert(generic_hier_block<MSKDemod>::_block_init);
assert(generic_hier_block<FSKDemod>::_block_init);
_deviation = deviation;
demod.setDeviation(deviation);
}
void setBaudRate(float baudRate, float omegaRelLimit) {
assert(generic_hier_block<MSKDemod>::_block_init);
assert(generic_hier_block<FSKDemod>::_block_init);
_baudRate = baudRate;
_omegaRelLimit = omegaRelLimit;
recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
}
void setMMGains(float omegaGain, float myGain) {
assert(generic_hier_block<MSKDemod>::_block_init);
assert(generic_hier_block<FSKDemod>::_block_init);
_omegaGain = omegaGain;
_muGain = myGain;
recov.setGains(_omegaGain, _muGain);
}
void setOmegaRelLimit(float omegaRelLimit) {
assert(generic_hier_block<MSKDemod>::_block_init);
assert(generic_hier_block<FSKDemod>::_block_init);
_omegaRelLimit = omegaRelLimit;
recov.setOmegaRelLimit(_omegaRelLimit);
}
@ -450,6 +451,105 @@ namespace dsp {
float _omegaRelLimit;
};
class GFSKDemod : public generic_hier_block<GFSKDemod> {
public:
GFSKDemod() {}
GFSKDemod(stream<complex_t>* input, float sampleRate, float deviation, float rrcAlpha, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
init(input, sampleRate, deviation, rrcAlpha, baudRate, omegaGain, muGain, omegaRelLimit);
}
void init(stream<complex_t>* input, float sampleRate, float deviation, float rrcAlpha, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
_sampleRate = sampleRate;
_deviation = deviation;
_rrcAlpha = rrcAlpha;
_baudRate = baudRate;
_omegaGain = omegaGain;
_muGain = muGain;
_omegaRelLimit = omegaRelLimit;
demod.init(input, _sampleRate, _deviation);
rrc.init(31, _sampleRate, _baudRate, _rrcAlpha);
fir.init(&demod.out, &rrc);
recov.init(&fir.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
out = &recov.out;
generic_hier_block<GFSKDemod>::registerBlock(&demod);
generic_hier_block<GFSKDemod>::registerBlock(&fir);
generic_hier_block<GFSKDemod>::registerBlock(&recov);
generic_hier_block<GFSKDemod>::_block_init = true;
}
void setInput(stream<complex_t>* input) {
assert((generic_hier_block<GFSKDemod>::_block_init));
demod.setInput(input);
}
void setSampleRate(float sampleRate) {
assert(generic_hier_block<GFSKDemod>::_block_init);
generic_hier_block<GFSKDemod>::tempStop();
_sampleRate = sampleRate;
demod.setSampleRate(_sampleRate);
recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
rrc.setSampleRate(_sampleRate);
fir.updateWindow(&rrc);
generic_hier_block<GFSKDemod>::tempStart();
}
void setDeviation(float deviation) {
assert(generic_hier_block<GFSKDemod>::_block_init);
_deviation = deviation;
demod.setDeviation(deviation);
}
void setRRCAlpha(float rrcAlpha) {
assert(generic_hier_block<GFSKDemod>::_block_init);
_rrcAlpha = rrcAlpha;
rrc.setAlpha(_rrcAlpha);
fir.updateWindow(&rrc);
}
void setBaudRate(float baudRate, float omegaRelLimit) {
assert(generic_hier_block<GFSKDemod>::_block_init);
_baudRate = baudRate;
_omegaRelLimit = omegaRelLimit;
generic_hier_block<GFSKDemod>::tempStop();
recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
rrc.setBaudRate(_baudRate);
fir.updateWindow(&rrc);
generic_hier_block<GFSKDemod>::tempStart();
}
void setMMGains(float omegaGain, float myGain) {
assert(generic_hier_block<GFSKDemod>::_block_init);
_omegaGain = omegaGain;
_muGain = myGain;
recov.setGains(_omegaGain, _muGain);
}
void setOmegaRelLimit(float omegaRelLimit) {
assert(generic_hier_block<GFSKDemod>::_block_init);
_omegaRelLimit = omegaRelLimit;
recov.setOmegaRelLimit(_omegaRelLimit);
}
stream<float>* out = NULL;
private:
FloatFMDemod demod;
RRCTaps rrc;
FIR<float> fir;
MMClockRecovery<float> recov;
float _sampleRate;
float _deviation;
float _rrcAlpha;
float _baudRate;
float _omegaGain;
float _muGain;
float _omegaRelLimit;
};
template<int ORDER, bool OFFSET>
class PSKDemod : public generic_hier_block<PSKDemod<ORDER, OFFSET>> {
public:

View File

@ -64,7 +64,8 @@ namespace dsp {
}
if constexpr (std::is_same_v<T, complex_t> || std::is_same_v<T, stereo_t>) {
volk_32fc_x2_add_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)_a->readBuf, (lv_32fc_t*)_b->readBuf, a_count);
// TODO: Switch this out for volk_32fc_x2_add_32fc with a check for old volk versions that don't have it (eg. Ubuntu 18.04 that has volk 1.3)
volk_32f_x2_add_32f((float*)out.writeBuf, (float*)_a->readBuf, (float*)_b->readBuf, a_count * 2);
}
else {
volk_32f_x2_add_32f(out.writeBuf, _a->readBuf, _b->readBuf, a_count);

View File

@ -173,7 +173,7 @@ namespace dsp {
int element = readBits(19, 6, _in->readBuf);
// If we've skipped or are on a non image element and there's data avilable, send it
// If we've skipped or are on a non image element and there's data available, send it
if ((element < lastElement || element > 55) && newImageData) {
newImageData = false;
for (int i = 0; i < 20; i++) {

View File

@ -65,6 +65,54 @@ namespace dsp {
};
template <class T>
class StreamDoubler : public generic_block<StreamDoubler<T>> {
public:
StreamDoubler() {}
StreamDoubler(stream<T>* in) { init(in); }
void init(stream<T>* in) {
_in = in;
generic_block<StreamDoubler>::registerInput(_in);
generic_block<StreamDoubler>::registerOutput(&outA);
generic_block<StreamDoubler>::registerOutput(&outB);
generic_block<StreamDoubler>::_block_init = true;
}
void setInput(stream<T>* in) {
assert(generic_block<StreamDoubler>::_block_init);
std::lock_guard<std::mutex> lck(generic_block<StreamDoubler>::ctrlMtx);
generic_block<StreamDoubler>::tempStop();
generic_block<StreamDoubler>::unregisterInput(_in);
_in = in;
generic_block<StreamDoubler>::registerInput(_in);
generic_block<StreamDoubler>::tempStart();
}
stream<T> outA;
stream<T> outB;
private:
int run() {
int count = _in->read();
if (count < 0) { return -1; }
memcpy(outA.writeBuf, _in->readBuf, count * sizeof(T));
memcpy(outB.writeBuf, _in->readBuf, count * sizeof(T));
_in->flush();
if (!outA.swap(count)) { return -1; }
if (!outB.swap(count)) { return -1; }
return count;
}
stream<T>* _in;
};
// NOTE: I'm not proud of this, it's BAD and just taken from the previous DSP, but it works...
template <class T>
@ -74,7 +122,7 @@ namespace dsp {
Reshaper(stream<T>* in, int keep, int skip) { init(in, keep, skip); }
// NOTE: For some reason, the base class destrcutor doesn't get called.... this is a temporary fix I guess
// NOTE: For some reason, the base class destructor doesn't get called.... this is a temporary fix I guess
// I also don't check for _block_init for the exact sample reason, something's weird
~Reshaper() {
if (!generic_block<Reshaper<T>>::_block_init) { return; }

View File

@ -46,6 +46,7 @@ namespace dsp {
if (running) { return; }
xlator.start();
resamp.start();
running = true;
}
void stop() {
@ -53,6 +54,7 @@ namespace dsp {
if (!running) { return; }
xlator.stop();
resamp.stop();
running = false;
}
void setInSampleRate(float inSampleRate) {

View File

@ -35,7 +35,7 @@ namespace icons {
bool load(std::string resDir) {
if (!std::filesystem::is_directory(resDir)) {
spdlog::error("Inavlid resource directory: {0}", resDir);
spdlog::error("Invalid resource directory: {0}", resDir);
return false;
}

View File

@ -176,7 +176,7 @@ void MainWindow::init() {
gui::freqSelect.frequencyChanged = false;
sigpath::sourceManager.tune(frequency);
gui::waterfall.setCenterFrequency(frequency);
bw = gui::waterfall.getBandwidth();
bw = 1.0;
gui::waterfall.vfoFreqChanged = false;
gui::waterfall.centerFreqMoved = false;
gui::waterfall.selectFirstVFO();
@ -188,6 +188,7 @@ void MainWindow::init() {
gui::waterfall.setFFTHeight(fftHeight);
tuningMode = core::configManager.conf["centerTuning"] ? tuner::TUNER_MODE_CENTER : tuner::TUNER_MODE_NORMAL;
gui::waterfall.VFOMoveSingleClick = (tuningMode == tuner::TUNER_MODE_CENTER);
core::configManager.release();
@ -213,6 +214,11 @@ void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) {
MainWindow* _this = (MainWindow*)ctx;
std::lock_guard<std::mutex> lck(_this->fft_mtx);
// Check if the count is valid
if (count > _this->fftSize) {
return;
}
// Apply window
volk_32fc_32f_multiply_32fc((lv_32fc_t*)_this->fft_in, (lv_32fc_t*)samples, sigpath::signalPath.fftTaps, count);
@ -392,6 +398,7 @@ void MainWindow::draw() {
ImGui::PushID(ImGui::GetID("sdrpp_ena_st_btn"));
if (ImGui::ImageButton(icons::CENTER_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
tuningMode = tuner::TUNER_MODE_NORMAL;
gui::waterfall.VFOMoveSingleClick = false;
core::configManager.acquire();
core::configManager.conf["centerTuning"] = false;
core::configManager.release(true);
@ -402,6 +409,7 @@ void MainWindow::draw() {
ImGui::PushID(ImGui::GetID("sdrpp_dis_st_btn"));
if (ImGui::ImageButton(icons::NORMAL_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
tuningMode = tuner::TUNER_MODE_CENTER;
gui::waterfall.VFOMoveSingleClick = true;
tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::freqSelect.frequency);
core::configManager.acquire();
core::configManager.conf["centerTuning"] = true;
@ -520,6 +528,8 @@ void MainWindow::draw() {
firstMenuRender = true;
}
ImGui::Checkbox("WF Single Click", &gui::waterfall.VFOMoveSingleClick);
ImGui::Spacing();
}
@ -593,8 +603,15 @@ void MainWindow::draw() {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0));
ImGui::Text("Zoom");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, gui::waterfall.getBandwidth(), 1000.0, "")) {
gui::waterfall.setViewBandwidth(bw);
if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, 1.0, 0.0, "")) {
double factor = (double)bw * (double)bw;
// Map 0.0 -> 1.0 to 1000.0 -> bandwidth
double wfBw = gui::waterfall.getBandwidth();
double delta = wfBw - 1000.0;
double finalBw = std::min<double>(1000.0 + (factor * delta), wfBw);
gui::waterfall.setViewBandwidth(finalBw);
if (vfo != NULL) {
gui::waterfall.setViewOffset(vfo->centerOffset); // center vfo on screen
}
@ -657,9 +674,9 @@ void MainWindow::setFFTSize(int size) {
gui::waterfall.setRawFFTSize(fftSize);
sigpath::signalPath.setFFTSize(fftSize);
fftwf_destroy_plan(fftwPlan);
fftwf_free(fft_in);
fftwf_free(fft_out);
fftwf_destroy_plan(fftwPlan);
fft_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize);
fft_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize);

View File

@ -2,6 +2,7 @@
#include <gui/widgets/bandplan.h>
#include <gui/gui.h>
#include <core.h>
#include <gui/style.h>
namespace bandplanmenu {
int bandplanId;
@ -44,8 +45,7 @@ namespace bandplanmenu {
}
ImGui::PopItemWidth();
ImGui::Text("Position");
ImGui::SameLine();
ImGui::LeftLabel("Position");
ImGui::SetNextItemWidth(menuColumnWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##_bandplan_pos_", &bandPlanPos, bandPlanPosTxt)) {
gui::waterfall.setBandPlanPos(bandPlanPos);

View File

@ -6,6 +6,7 @@
#include <gui/gui.h>
#include <gui/main_window.h>
#include <signal_path/signal_path.h>
#include <gui/style.h>
namespace displaymenu {
bool showWaterfall;
@ -19,6 +20,9 @@ namespace displaymenu {
int fftRate = 20;
const int FFTSizes[] = {
524288,
262144,
131072,
65536,
32768,
16384,
@ -28,7 +32,10 @@ namespace displaymenu {
1024
};
const char* FFTSizesStr = "65536\0"
const char* FFTSizesStr = "524288\0"
"262144\0"
"131072\0"
"65536\0"
"32768\0"
"16384\0"
"8192\0"
@ -105,8 +112,7 @@ namespace displaymenu {
core::configManager.release(true);
}
ImGui::Text("FFT Framerate");
ImGui::SameLine();
ImGui::LeftLabel("FFT Framerate");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) {
fftRate = std::max<int>(1, fftRate);
@ -116,8 +122,7 @@ namespace displaymenu {
core::configManager.release(true);
}
ImGui::Text("FFT Size");
ImGui::SameLine();
ImGui::LeftLabel("FFT Size");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##sdrpp_fft_size", &fftSizeId, FFTSizesStr)) {
gui::mainWindow.setFFTSize(FFTSizes[fftSizeId]);
@ -126,8 +131,7 @@ namespace displaymenu {
core::configManager.release(true);
}
ImGui::Text("FFT Window");
ImGui::SameLine();
ImGui::LeftLabel("FFT Window");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##sdrpp_fft_window", &selectedWindow, "Rectangular\0Blackman\0")) {
gui::mainWindow.setFFTWindow(selectedWindow);
@ -137,8 +141,7 @@ namespace displaymenu {
}
if (colorMapNames.size() > 0) {
ImGui::Text("Color Map");
ImGui::SameLine();
ImGui::LeftLabel("Color Map");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##_sdrpp_color_map_sel", &colorMapId, colorMapNamesTxt.c_str())) {
colormaps::Map map = colormaps::maps[colorMapNames[colorMapId]];

Some files were not shown because too many files have changed in this diff Show More