mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-07-09 18:45:22 +02:00
Compare commits
129 Commits
Author | SHA1 | Date | |
---|---|---|---|
f539cfad32 | |||
ab1a482352 | |||
35fc973a65 | |||
db8eb47422 | |||
d4795cb369 | |||
75249fe849 | |||
d1aab6f25d | |||
afe13969a7 | |||
47e5c589bc | |||
3795e7cf23 | |||
d18a019abf | |||
4d7b46b1a8 | |||
cf3e024fb9 | |||
8deb018684 | |||
33b9b53328 | |||
011e7ca409 | |||
a27a46350e | |||
3fd4294afa | |||
4b2f3e4f60 | |||
82cacf14bb | |||
0fffa7d45a | |||
9555dda8a4 | |||
44ad0a1a7e | |||
c515ddb811 | |||
ea0d905177 | |||
1be3092c7d | |||
f29f77bbdc | |||
91d960da3c | |||
dd083b6634 | |||
b4e2875acd | |||
4ea9b96397 | |||
8c15e78315 | |||
94b7676ca5 | |||
83fc20cacc | |||
fcd759654c | |||
73393e36c6 | |||
c36034dbb8 | |||
a9d92d3a8e | |||
8666aa07e7 | |||
a37a2d3b36 | |||
dc31539f6e | |||
4564475821 | |||
163e35727c | |||
40d21b3321 | |||
fb9bbcb158 | |||
c0801a8aed | |||
dc43e76405 | |||
8b2f019ce4 | |||
13bafa002a | |||
90ebe373df | |||
5b7fc417ff | |||
b4213ea049 | |||
26fa23c8f5 | |||
c2da150d43 | |||
48f6642c70 | |||
efd3c47a6c | |||
187fc2cb9e | |||
c9a78d5dc6 | |||
b7c95de8cc | |||
bc0de50ba6 | |||
049fc77ff6 | |||
9ee0951874 | |||
841c31daf4 | |||
d9b061ef89 | |||
ce48257706 | |||
fe09a30279 | |||
daa26e8e97 | |||
318e57dc3d | |||
b74e2d37a5 | |||
9d34c6a8c1 | |||
2184e15e44 | |||
bd427d23b3 | |||
5a2b0c9d79 | |||
a208d4078e | |||
f56113aae4 | |||
11c6377c7f | |||
22acf33c01 | |||
ec6a258958 | |||
012903fbf4 | |||
e48b5c6035 | |||
0a54b92c58 | |||
7c1d1cad22 | |||
e101c1ebd7 | |||
68b3eb9b21 | |||
c579aca1db | |||
c31e260425 | |||
fa75a3a176 | |||
af59144f5b | |||
04ce4c3583 | |||
ba6d9f7202 | |||
49fd49b4d6 | |||
99441ea2eb | |||
dbd734a0b5 | |||
f572d12936 | |||
37bf9c46c5 | |||
aa9a2a8b46 | |||
124f3368d4 | |||
51d084c20e | |||
101674379d | |||
61bbb4a374 | |||
2d8bbef12a | |||
4109ea73b3 | |||
a7d8ec8a2d | |||
6036300ecb | |||
9cf3ba79f3 | |||
dcda74c0cf | |||
4f16c6bf90 | |||
73daca9116 | |||
2fd91459fd | |||
8502bae236 | |||
f01cb4af9f | |||
53cb328c2c | |||
9a2794a0dd | |||
c84371ba52 | |||
dbf1fb790f | |||
271d1f9240 | |||
b2ce47d975 | |||
6ab59ad3c5 | |||
3516baa042 | |||
1465fb784f | |||
42918908a0 | |||
9f0e050d1b | |||
dcc17cdee7 | |||
9eb3ef0500 | |||
ffc20f5fee | |||
6144f3a3f9 | |||
a3bda1b8fd | |||
2a6c742a51 | |||
604a559b54 |
67
.github/workflows/build_all.yml
vendored
67
.github/workflows/build_all.yml
vendored
@ -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
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,3 +11,5 @@ build/
|
||||
root_dev/
|
||||
Folder.DotSettings.user
|
||||
CMakeSettings.json
|
||||
poggers_decoder
|
||||
m17_decoder/libcorrect
|
107
CMakeLists.txt
107
CMakeLists.txt
@ -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)
|
||||
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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",
|
||||
|
@ -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)
|
47
core/libcorrect/.appveyor-install-tools.cmd
Normal file
47
core/libcorrect/.appveyor-install-tools.cmd
Normal 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
1
core/libcorrect/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build
|
12
core/libcorrect/.travis.yml
Normal file
12
core/libcorrect/.travis.yml
Normal 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
|
102
core/libcorrect/CMakeLists.txt
Normal file
102
core/libcorrect/CMakeLists.txt
Normal 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
12
core/libcorrect/LICENSE
Normal 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
38
core/libcorrect/README.md
Normal file
@ -0,0 +1,38 @@
|
||||
[libcorrect](https://github.com/quiet/libcorrect)
|
||||
===========
|
||||
[](https://travis-ci.org/quiet/libcorrect)
|
||||
[](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.
|
||||
|
||||
|
||||
|
||||
|
43
core/libcorrect/appveyor.yml
Normal file
43
core/libcorrect/appveyor.yml
Normal 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
|
2
core/libcorrect/src/CMakeLists.txt
Normal file
2
core/libcorrect/src/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
add_subdirectory(convolutional)
|
||||
add_subdirectory(reed-solomon)
|
54
core/libcorrect/tests/CMakeLists.txt
Normal file
54
core/libcorrect/tests/CMakeLists.txt
Normal 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()
|
123
core/libcorrect/tests/convolutional-fec.c
Normal file
123
core/libcorrect/tests/convolutional-fec.c
Normal 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;
|
||||
}
|
122
core/libcorrect/tests/convolutional-shim.c
Normal file
122
core/libcorrect/tests/convolutional-shim.c
Normal 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;
|
||||
}
|
132
core/libcorrect/tests/convolutional-sse.c
Normal file
132
core/libcorrect/tests/convolutional-sse.c
Normal 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;
|
||||
}
|
133
core/libcorrect/tests/convolutional.c
Normal file
133
core/libcorrect/tests/convolutional.c
Normal 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;
|
||||
}
|
41
core/libcorrect/tests/include/rs_tester.h
Normal file
41
core/libcorrect/tests/include/rs_tester.h
Normal 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);
|
10
core/libcorrect/tests/include/rs_tester_fec.h
Normal file
10
core/libcorrect/tests/include/rs_tester_fec.h
Normal 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);
|
10
core/libcorrect/tests/include/rs_tester_fec_shim.h
Normal file
10
core/libcorrect/tests/include/rs_tester_fec_shim.h
Normal 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);
|
138
core/libcorrect/tests/reed-solomon-fec-interop.c
Normal file
138
core/libcorrect/tests/reed-solomon-fec-interop.c
Normal 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;
|
||||
}
|
138
core/libcorrect/tests/reed-solomon-shim-interop.c
Normal file
138
core/libcorrect/tests/reed-solomon-shim-interop.c
Normal 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;
|
||||
}
|
146
core/libcorrect/tests/reed-solomon.c
Normal file
146
core/libcorrect/tests/reed-solomon.c
Normal 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;
|
||||
}
|
102
core/libcorrect/tests/rs_tester.c
Normal file
102
core/libcorrect/tests/rs_tester.c
Normal 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;
|
||||
}
|
30
core/libcorrect/tests/rs_tester_fec.c
Normal file
30
core/libcorrect/tests/rs_tester_fec.c
Normal 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);
|
||||
}
|
26
core/libcorrect/tests/rs_tester_fec_shim.c
Normal file
26
core/libcorrect/tests/rs_tester_fec_shim.c
Normal 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);
|
||||
}
|
29
core/libcorrect/tools/CMakeLists.txt
Normal file
29
core/libcorrect/tools/CMakeLists.txt
Normal 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})
|
279
core/libcorrect/tools/find_conv_libfec_poly.c
Normal file
279
core/libcorrect/tools/find_conv_libfec_poly.c
Normal 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;
|
||||
}
|
330
core/libcorrect/tools/find_conv_optim_poly.c
Normal file
330
core/libcorrect/tools/find_conv_optim_poly.c
Normal 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;
|
||||
}
|
350
core/libcorrect/tools/find_conv_optim_poly_annealing.c
Normal file
350
core/libcorrect/tools/find_conv_optim_poly_annealing.c
Normal 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;
|
||||
}
|
51
core/libcorrect/tools/find_rs_primitive_poly.c
Normal file
51
core/libcorrect/tools/find_rs_primitive_poly.c
Normal 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;
|
||||
}
|
11
core/libcorrect/util/CMakeLists.txt
Normal file
11
core/libcorrect/util/CMakeLists.txt
Normal 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()
|
29
core/libcorrect/util/error-sim-fec.c
Normal file
29
core/libcorrect/util/error-sim-fec.c
Normal 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);
|
||||
}
|
33
core/libcorrect/util/error-sim-shim.c
Normal file
33
core/libcorrect/util/error-sim-shim.c
Normal 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;
|
||||
}
|
13
core/libcorrect/util/error-sim-sse.c
Normal file
13
core/libcorrect/util/error-sim-sse.c
Normal 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);
|
||||
}
|
188
core/libcorrect/util/error-sim.c
Normal file
188
core/libcorrect/util/error-sim.c
Normal 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);
|
||||
}
|
@ -32,9 +32,16 @@ void ConfigManager::load(json def, bool lock) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::ifstream file(path.c_str());
|
||||
file >> conf;
|
||||
file.close();
|
||||
}
|
||||
catch (std::exception e) {
|
||||
spdlog::error("Config file '{0}' is corrupted, resetting it", path);
|
||||
conf = def;
|
||||
save(false);
|
||||
}
|
||||
if (lock) { mtx.unlock(); }
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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*);
|
||||
|
@ -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);
|
||||
|
@ -53,7 +53,7 @@ namespace dsp {
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
if (!out.swap(outCount)) { return -1; }
|
||||
if (outCount > 0 && !out.swap(outCount)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -217,7 +217,7 @@ namespace dsp {
|
||||
memcpy(delay, &_in->readBuf[count - 7], 7 * sizeof(T));
|
||||
|
||||
_in->flush();
|
||||
if (!out.swap(outCount)) { return -1; }
|
||||
if (outCount > 0 && !out.swap(outCount)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
|
161
core/src/dsp/compression.h
Normal file
161
core/src/dsp/compression.h
Normal 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;
|
||||
|
||||
};
|
||||
}
|
@ -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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -64,7 +64,7 @@ namespace dsp {
|
||||
if (bitsRead >= _frameLen) {
|
||||
if (!out.swap((bitsRead / 8) + ((bitsRead % 8) > 0))) { return -1; }
|
||||
bitsRead = -1;
|
||||
nextBitIsStartOfFrame = true;
|
||||
if (allowSequential) { nextBitIsStartOfFrame = true; }
|
||||
}
|
||||
|
||||
continue;
|
||||
@ -106,6 +106,8 @@ namespace dsp {
|
||||
return count;
|
||||
}
|
||||
|
||||
bool allowSequential = true;
|
||||
|
||||
stream<uint8_t> out;
|
||||
|
||||
private:
|
||||
|
@ -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,42 +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<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);
|
||||
}
|
||||
@ -445,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:
|
||||
|
@ -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);
|
||||
|
@ -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++) {
|
||||
|
@ -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; }
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user