18 Commits

Author SHA1 Message Date
08f3a7d201 make the modulation field of a baseband recording 'IQ' instead of 'Unknown'
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 6s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
2024-10-07 14:39:45 +02:00
9ce62f8885 add whitelist for plutosdr-like devices 2024-10-07 14:10:23 +02:00
caeaa2d46c add kcsdr_source to the readme
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 8s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 6s
Build Binaries / build_android (push) Failing after 7s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-10-02 19:15:13 +02:00
7ae030a3a6 fix fobossdr_source module missing on MacOS as described in #1485 2024-10-02 18:58:48 +02:00
1b27379a3d add beginning of kcsdr source 2024-10-02 18:57:05 +02:00
e52123038e Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 5s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-09-28 03:32:14 +02:00
ec8c60111d uncomment lines that copy the fobossdr source module for windows packaging 2024-09-28 03:32:07 +02:00
f61799cf5f Merge pull request #1482 from AlexandreRouma/fobos_test
Enable the FobosSDR source on windows and linux
2024-09-28 02:44:04 +02:00
17eccf5156 enabled fobos source on windows 2024-09-14 15:05:12 +02:00
e835c8dd9a disable fobos source on windows 2024-09-14 15:04:07 +02:00
acb1be121c enable fobossdr source 2024-09-14 14:48:37 +02:00
0fa89614bb disabled problematic fobossdr source 2024-09-14 14:47:10 +02:00
256affd918 fixed fobossdr CI 2024-09-14 02:06:34 +02:00
e80cdbf248 fix CI + fix fobossdr_source cmakelist + prepare for fobos source on windows 2024-09-14 00:18:31 +02:00
75e66226c3 finish fobossdr source + add rigexpert to hardware donor list + fix source module default instantiation 2024-09-13 23:02:30 +02:00
c2f0e756a5 add fobossdr_source module and fix network sink crash when the given hostname is invalid 2024-09-11 21:57:12 +02:00
79dd5bdcbb add beginning of DAB decoder and add missing hardware donors to the credits 2024-09-10 15:33:22 +02:00
6dce28345c add missing stddef includes 2024-08-26 22:37:18 +02:00
37 changed files with 4036 additions and 34 deletions

View File

@ -71,14 +71,17 @@ jobs:
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
- name: Install libperseus-sdr
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake -DCMAKE_BUILD_TYPE=Release "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
- name: Install librfnm
run: git clone https://github.com/AlexandreRouma/librfnm ; cd librfnm ; mkdir build ; cd build ; cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
run: git clone https://github.com/AlexandreRouma/librfnm ; cd librfnm ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
- name: Install libfobos
run: git clone https://github.com/AlexandreRouma/libfobos ; cd libfobos ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON
run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
- name: Build
working-directory: ${{runner.workspace}}/build
@ -127,12 +130,15 @@ jobs:
- name: Install librfnm
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
- name: Install libfobos
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
- name: Install more recent librtlsdr
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build
working-directory: ${{runner.workspace}}/build
@ -181,12 +187,15 @@ jobs:
- name: Install librfnm
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
- name: Install libfobos
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
- name: Install more recent librtlsdr
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build
working-directory: ${{runner.workspace}}/build

View File

@ -15,9 +15,11 @@ option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)
option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF)
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF)
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF)
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
option(OPT_BUILD_KCSDR_SOURCE "Build KCSDR Source Module (Dependencies: libkcsdr)" OFF)
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
@ -43,6 +45,7 @@ option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: port
# Decoders
option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF)
option(OPT_BUILD_DAB_DECODER "Build the DAB/DAB+ decoder (no dependencies required)" OFF)
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
option(OPT_BUILD_KG_SSTV_DECODER "Build the KG SSTV (KG-STV) decoder module (no dependencies required)" OFF)
option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (Dependencies: codec2)" OFF)
@ -142,6 +145,10 @@ if (OPT_BUILD_FILE_SOURCE)
add_subdirectory("source_modules/file_source")
endif (OPT_BUILD_FILE_SOURCE)
if (OPT_BUILD_FOBOSSDR_SOURCE)
add_subdirectory("source_modules/fobossdr_source")
endif (OPT_BUILD_FOBOSSDR_SOURCE)
if (OPT_BUILD_HACKRF_SOURCE)
add_subdirectory("source_modules/hackrf_source")
endif (OPT_BUILD_HACKRF_SOURCE)
@ -154,6 +161,10 @@ if (OPT_BUILD_HERMES_SOURCE)
add_subdirectory("source_modules/hermes_source")
endif (OPT_BUILD_HERMES_SOURCE)
if (OPT_BUILD_KCSDR_SOURCE)
add_subdirectory("source_modules/kcsdr_source")
endif (OPT_BUILD_KCSDR_SOURCE)
if (OPT_BUILD_LIMESDR_SOURCE)
add_subdirectory("source_modules/limesdr_source")
endif (OPT_BUILD_LIMESDR_SOURCE)
@ -242,6 +253,10 @@ if (OPT_BUILD_ATV_DECODER)
add_subdirectory("decoder_modules/atv_decoder")
endif (OPT_BUILD_ATV_DECODER)
if (OPT_BUILD_DAB_DECODER)
add_subdirectory("decoder_modules/dab_decoder")
endif (OPT_BUILD_DAB_DECODER)
if (OPT_BUILD_FALCON9_DECODER)
add_subdirectory("decoder_modules/falcon9_decoder")
endif (OPT_BUILD_FALCON9_DECODER)

View File

@ -173,16 +173,20 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
defConfig["moduleInstances"]["File Source"]["module"] = "file_source";
defConfig["moduleInstances"]["File Source"]["enabled"] = true;
defConfig["moduleInstances"]["FobosSDR Source"]["module"] = "fobossdr_source";
defConfig["moduleInstances"]["FobosSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["HackRF Source"]["module"] = "hackrf_source";
defConfig["moduleInstances"]["HackRF Source"]["enabled"] = true;
defConfig["moduleInstances"]["Harogic Source"]["module"] = "harogic_source";
defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true;
defConfig["moduleInstances"]["Hermes Source"]["module"] = "hermes_source";
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["RFNM Source"]["module"] = "rfnm_source";
defConfig["moduleInstances"]["RFNM Source"]["enabled"] = true;
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
@ -195,8 +199,12 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true;
defConfig["moduleInstances"]["SDR++ Server Source"]["module"] = "sdrpp_server_source";
defConfig["moduleInstances"]["SDR++ Server Source"]["enabled"] = true;
defConfig["moduleInstances"]["Spectran HTTP Source"]["module"] = "spectran_http_source";
defConfig["moduleInstances"]["Spectran HTTP Source"]["enabled"] = true;
defConfig["moduleInstances"]["SpyServer Source"]["module"] = "spyserver_source";
defConfig["moduleInstances"]["SpyServer Source"]["enabled"] = true;
defConfig["moduleInstances"]["USRP Source"]["module"] = "usrp_source";
defConfig["moduleInstances"]["USRP Source"]["enabled"] = true;
defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
defConfig["moduleInstances"]["Network Sink"] = "network_sink";

View File

@ -37,9 +37,12 @@ namespace sdrpp_credits {
const char* hardwareDonators[] = {
"Aaronia AG",
"Airspy",
"Alex 4Z5LV",
"Analog Devices",
"CaribouLabs",
"Deepace",
"Ettus Research",
"Harogic",
"Howard Su",
"MicroPhase",
"Microtelecom",
@ -47,6 +50,7 @@ namespace sdrpp_credits {
"Nuand",
"RFNM",
"RFspace",
"RigExpert",
"RTL-SDRblog",
"SDRplay"
};

View File

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.13)
project(dab_decoder)
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
include(${SDRPP_MODULE_CMAKE})
target_include_directories(dab_decoder PRIVATE "src/")
if (MSVC)
# Lib path
target_include_directories(dab_decoder PRIVATE "C:/Program Files/codec2/include/")
target_link_directories(dab_decoder PRIVATE "C:/Program Files/codec2/lib")
target_link_libraries(dab_decoder PRIVATE libcodec2)
elseif (ANDROID)
target_include_directories(dab_decoder PUBLIC
/sdr-kit/${ANDROID_ABI}/include/codec2
)
target_link_libraries(dab_decoder PUBLIC
/sdr-kit/${ANDROID_ABI}/lib/libcodec2.so
)
else ()
find_package(PkgConfig)
pkg_check_modules(LIBCODEC2 REQUIRED codec2)
target_include_directories(dab_decoder PRIVATE ${LIBCODEC2_INCLUDE_DIRS})
target_link_directories(dab_decoder PRIVATE ${LIBCODEC2_LIBRARY_DIRS})
target_link_libraries(dab_decoder PRIVATE ${LIBCODEC2_LIBRARIES})
# Include it because for some reason pkgconfig doesn't look here?
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_include_directories(dab_decoder PRIVATE "/usr/local/include")
endif()
endif ()

View File

@ -0,0 +1,280 @@
#pragma once
#include <dsp/processor.h>
#include <utils/flog.h>
#include <fftw3.h>
#include "dab_phase_sym.h"
namespace dab {
class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
public:
CyclicSync() {}
// TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
// Computer the number of samples for the symbol and its cyclic prefix
symbolSamps = round(samplerate * symbolLength);
prefixSamps = round(samplerate * cyclicPrefixLength);
// Allocate and clear the delay buffer
delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
dsp::buffer::clear(delayBuf, symbolSamps);
// Allocate and clear the history buffer
histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
dsp::buffer::clear(histBuf, prefixSamps);
// Compute the delay input addresses
delayBufInput = &delayBuf[symbolSamps];
// Compute the correlation AGC configuration
this->agcRate = agcRate;
agcRateInv = 1.0f - agcRate;
base_type::init(in);
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
base_type::tempStart();
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
// Copy the data into the normal delay buffer
memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
// Flush the input stream
base_type::_in->flush();
// Do cross-correlation
for (int i = 0; i < count; i++) {
// Get the current history slot
dsp::complex_t* slot = &histBuf[histId++];
// Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
histId %= prefixSamps;
// Kick out last value from the correlation
corr -= *slot;
// Save input value and compute the new prodct
dsp::complex_t val = delayBuf[i];
dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
// Add the new value to the correlation
*slot = prod;
// Add the new value to the history buffer
corr += prod;
// Compute sample amplitude
float rcorr = corr.amplitude();
// If a high enough peak is reached, reset the symbol counter
if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
peakCorr = rcorr;
peakLCorr = lastCorr;
samplesSincePeak = 0;
}
// If this is the sample right after the peak, save it
if (samplesSincePeak == 1) {
peakRCorr = rcorr;
}
// Write the sample to the output
out.writeBuf[samplesSincePeak++] = val;
// If the end of the symbol is reached, send it off
if (samplesSincePeak >= symbolSamps) {
if (!out.swap(symbolSamps)) {
return -1;
}
samplesSincePeak = 0;
peakCorr = 0;
}
// Update the average correlation
lastCorr = rcorr;
// Update the average correlation value
avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
}
// Move unused data
memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
return count;
}
protected:
int symbolSamps;
int prefixSamps;
int histId = 0;
dsp::complex_t* histBuf;
dsp::complex_t* delayBuf;
dsp::complex_t* delayBufInput;
dsp::complex_t corr = { 0.0f, 0.0f };
int samplesSincePeak = 0;
float lastCorr = 0.0f;
float peakCorr = 0.0f;
float peakLCorr = 0.0f;
float peakRCorr = 0.0f;
// Note only required for DAB
float avgCorr = 0.0f;
float agcRate;
float agcRateInv;
};
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
public:
FrameFreqSync() {}
FrameFreqSync(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) { init(in, agcRate); }
void init(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) {
// Allocate buffers
amps = dsp::buffer::alloc<float>(2048);
conjRef = dsp::buffer::alloc<dsp::complex_t>(2048);
corrIn = (dsp::complex_t*)fftwf_alloc_complex(2048);
corrOut = (dsp::complex_t*)fftwf_alloc_complex(2048);
// Copy the phase reference
memcpy(conjRef, DAB_PHASE_SYM_CONJ, 2048 * sizeof(dsp::complex_t));
// Plan the FFT computation
plan = fftwf_plan_dft_1d(2048, (fftwf_complex*)corrIn, (fftwf_complex*)corrOut, FFTW_FORWARD, FFTW_ESTIMATE);
// Compute the correlation AGC configuration
this->agcRate = agcRate;
agcRateInv = 1.0f - agcRate;
base_type::init(in);
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
base_type::tempStart();
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
// Apply frequency shift
lv_32fc_t phase = lv_cmake(1.0f, 0.0f);
lv_32fc_t phaseDelta = lv_cmake(cos(offset), sin(offset));
#if VOLK_VERSION >= 030100
volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
#else
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
#endif
// Compute the amplitude amplitude of all samples
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)_in->readBuf, 2048);
// Compute the average signal level by adding up all values
float level = 0.0f;
volk_32f_accumulator_s32f(&level, amps, 2048);
// Detect a frame sync condition
if (level < avgLvl * 0.5f) {
// Reset symbol counter
sym = 1;
// Update the average level
avgLvl = agcRate*level + agcRateInv*avgLvl;
// Flush the input stream and return
base_type::_in->flush();
return count;
}
// Update the average level
avgLvl = agcRate*level + agcRateInv*avgLvl;
// Handle phase reference
if (sym == 1) {
// Output the symbols (DEBUG ONLY)
memcpy(corrIn, _in->readBuf, 2048 * sizeof(dsp::complex_t));
fftwf_execute(plan);
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
int outCount = 0;
dsp::complex_t pi4 = { cos(3.1415926535*0.25), sin(3.1415926535*0.25) };
for (int i = -767; i < 768; i++) {
if (!i) { continue; }
int cid0 = ((i-1) >= 0) ? (i-1) : 2048+(i-1);
int cid1 = (i >= 0) ? i : 2048+i;;
out.writeBuf[outCount++] = pi4 * (corrOut[cid1] * corrOut[cid0].conj()) * (1.0f/(amps[cid0]*amps[cid0]));
}
out.swap(outCount);
// Multiply the samples with the conjugated phase reference signal
volk_32fc_x2_multiply_32fc((lv_32fc_t*)corrIn, (lv_32fc_t*)_in->readBuf, (lv_32fc_t*)conjRef, 2048);
// Compute the FFT of the product
fftwf_execute(plan);
// Compute the amplitude of the bins
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
// Locate highest power bin
uint32_t peakId;
volk_32f_index_max_32u(&peakId, amps, 2048);
// Obtain the value of the bins next to the peak
float peakL = amps[(peakId + 2047) % 2048];
float peakR = amps[(peakId + 1) % 2048];
// Compute the integer frequency offset
float offInt = (peakId < 1024) ? (float)peakId : ((float)peakId - 2048.0f);
// Compute the frequency offset in rad/samp
float off = 3.1415926535f * (offInt + ((peakR - peakL) / (peakR + peakL))) * (1.0f / 1024.0f);
// Run control loop
offset -= 0.1f*off;
flog::debug("Offset: {} Hz, Error: {} Hz, Avg Level: {}", offset * (0.5f/3.1415926535f)*2.048e6, off * (0.5f/3.1415926535f)*2.048e6, avgLvl);
}
// Increment the symbol counter
sym++;
// Flush the input stream and return
base_type::_in->flush();
return count;
}
protected:
fftwf_plan plan;
float* amps;
dsp::complex_t* conjRef;
dsp::complex_t* corrIn;
dsp::complex_t* corrOut;
int sym;
float offset = 0.0f;
float avgLvl = 0.0f;
float agcRate;
float agcRateInv;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,163 @@
#include <imgui.h>
#include <config.h>
#include <core.h>
#include <gui/style.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <filesystem>
#include <dsp/stream.h>
#include <dsp/buffer/reshaper.h>
#include <dsp/multirate/rational_resampler.h>
#include <dsp/sink/handler_sink.h>
#include <fstream>
#include <chrono>
#include "dab_dsp.h"
#include <gui/widgets/constellation_diagram.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "dab_decoder",
/* Description: */ "DAB/DAB+ Decoder for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
ConfigManager config;
#define INPUT_SAMPLE_RATE 2.048e6
#define VFO_BANDWIDTH 1.6e6
class M17DecoderModule : public ModuleManager::Instance {
public:
M17DecoderModule(std::string name) {
this->name = name;
file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary);
// Load config
config.acquire();
config.release(true);
// Initialize VFO
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
vfo->setSnapInterval(250);
// Initialize DSP here
csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE);
ffsync.init(&csync.out);
ns.init(&ffsync.out, handler, this);
// Start DSO Here
csync.start();
ffsync.start();
ns.start();
gui::menu.registerEntry(name, menuHandler, this, this);
}
~M17DecoderModule() {
gui::menu.removeEntry(name);
// Stop DSP Here
if (enabled) {
csync.stop();
ffsync.stop();
ns.stop();
sigpath::vfoManager.deleteVFO(vfo);
}
sigpath::sinkManager.unregisterStream(name);
}
void postInit() {}
void enable() {
double bw = gui::waterfall.getBandwidth();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
vfo->setSnapInterval(250);
// Set Input of demod here
csync.setInput(vfo->output);
// Start DSP here
csync.start();
ffsync.start();
ns.start();
enabled = true;
}
void disable() {
// Stop DSP here
csync.stop();
ffsync.stop();
ns.stop();
sigpath::vfoManager.deleteVFO(vfo);
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
static void menuHandler(void* ctx) {
M17DecoderModule* _this = (M17DecoderModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }
_this->constDiagram.draw();
if (!_this->enabled) { style::endDisabled(); }
}
std::ofstream file;
static void handler(dsp::complex_t* data, int count, void* ctx) {
M17DecoderModule* _this = (M17DecoderModule*)ctx;
//_this->file.write((char*)data, count * sizeof(dsp::complex_t));
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
_this->constDiagram.releaseBuffer();
}
std::string name;
bool enabled = true;
dab::CyclicSync csync;
dab::FrameFreqSync ffsync;
dsp::sink::Handler<dsp::complex_t> ns;
ImGui::ConstellationDiagram constDiagram;
// DSP Chain
VFOManager::VFO* vfo;
};
MOD_EXPORT void _INIT_() {
// Create default recording directory
json def = json({});
config.setPath(core::args["root"].s() + "/dab_decoder_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new M17DecoderModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (M17DecoderModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -0,0 +1,34 @@
0123456789
--- ---
0*4
1*5
2*6
1*5
2*6 = L + 3*7 - 0*4
3*7
2*6
3*7 = L + 4*8 - 1*5
4*8
3*7
4*8 = L + 5*9 - 2*6
5*9
0*5
1*6
2*7
1*6
2*7
3*8
2*7
3*8
4*9
=> Use same technique to cache the interpolation results

View File

@ -23,9 +23,9 @@ SDRPP_MOD_INFO{
/* Max instances */ -1
};
#define INPUT_BANDWIDTH 138e3
#define INPUT_SAMPLE_RATE 250e3
#define INPUT_BAUDRATE 125e3
#define INPUT_BANDWIDTH 600e3
#define INPUT_SAMPLE_RATE 1000e3
#define INPUT_BAUDRATE 500e3
#define SYMBOL_DIAG_RATE 30
#define SYMBOL_DIAG_COUNT 1024

View File

@ -1,5 +1,6 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "dsp/processor.h"
extern "C" {

View File

@ -1,6 +1,7 @@
#pragma once
#include "dsp/processor.h"
#include <stdint.h>
#include <stddef.h>
namespace ryfi {
// Synchronization word.

View File

@ -1,5 +1,6 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
namespace ryfi {
/**

View File

@ -72,6 +72,7 @@ namespace ryfi {
uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE];
int pktExpected = 0;
int pktRead = 0;
int valid = 0;
while (true) {
// Read a frame
@ -80,6 +81,7 @@ namespace ryfi {
// Deserialize the frame
Frame::deserialize(rs.out.readBuf, frame);
valid++;
// Flush the stream
rs.out.flush();
@ -93,11 +95,12 @@ namespace ryfi {
// If the frames aren't consecutive
int frameRead = 0;
if (frame.counter != expectedCounter) {
flog::warn("Lost at least {} frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000);
flog::warn("Lost at least {} frames after {} valid frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000, valid);
// Cancel the partial packet if there was one
pktExpected = 0;
pktRead = 0;
valid = 1;
// If this frame is not an idle frame or continuation frame
if (frame.firstPacket != PKT_OFFS_NONE) {

View File

@ -1,5 +1,6 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "dsp/processor.h"
extern "C" {

View File

@ -35,10 +35,20 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -35,10 +35,20 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -35,10 +35,20 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -35,10 +35,20 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -61,6 +61,16 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Fix missing .pc file for codec2
echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc
echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc
@ -76,7 +86,7 @@ echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
# Generate package

View File

@ -35,10 +35,20 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -35,10 +35,20 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -35,10 +35,20 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -35,10 +35,20 @@ make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -35,6 +35,7 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/fobossdr_source/fobossdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib

View File

@ -24,6 +24,9 @@ cp 'C:/Program Files/PothosSDR/bin/bladeRF.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/file_source/Release/file_source.dll sdrpp_windows_x64/modules/
cp $build_dir/source_modules/fobossdr_source/Release/fobossdr_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/RigExpert/Fobos/bin/fobos.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/hackrf_source/Release/hackrf_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/

View File

@ -168,10 +168,9 @@ public:
writer.setSamplerate(samplerate);
// Open file
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
std::string extension = ".wav";
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, recMode, vfoName) + extension);
if (!writer.open(expandedPath)) {
flog::error("Failed to open file for recording: {0}", expandedPath);
return;
@ -452,7 +451,7 @@ private:
{ RADIO_IFACE_MODE_RAW, "RAW" }
};
std::string genFileName(std::string templ, std::string type, std::string name) {
std::string genFileName(std::string templ, int mode, std::string name) {
// Get data
time_t now = time(0);
tm* ltm = localtime(&now);
@ -462,6 +461,9 @@ private:
freq += gui::waterfall.vfos[name]->generalOffset;
}
// Select the recording type string
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
// Format to string
char freqStr[128];
char hourStr[128];
@ -470,7 +472,7 @@ private:
char dayStr[128];
char monStr[128];
char yearStr[128];
const char* modeStr = "Unknown";
const char* modeStr = (recMode == RECORDER_MODE_AUDIO) ? "Unknown" : "IQ";
sprintf(freqStr, "%.0lfHz", freq);
sprintf(hourStr, "%02d", ltm->tm_hour);
sprintf(minStr, "%02d", ltm->tm_min);

View File

@ -324,9 +324,11 @@ Modules in beta are still included in releases for the most part but not enabled
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
| fobossdr_source | Beta | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
| harogic_source | Unfinished | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | |
| harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | |
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
| kcsdr_source | Unfinished | libkcsdr | OPT_BUILD_KCSDR_SOURCE | ⛔ | ⛔ | ⛔ |
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
| network_source | Unfinished | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | ⛔ |
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
@ -339,9 +341,9 @@ Modules in beta are still included in releases for the most part but not enabled
| sdrpp_server_source | Working | - | OPT_BUILD_SDRPP_SERVER_SOURCE | ✅ | ✅ | ✅ |
| soapy_source | Deprecated | soapysdr | OPT_BUILD_SOAPY_SOURCE | ⛔ | ⛔ | ⛔ |
| spectran_source | Unfinished | RTSA Suite | OPT_BUILD_SPECTRAN_SOURCE | ⛔ | ⛔ | ⛔ |
| spectran_http_source | Beta | - | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅ | ✅ | |
| spectran_http_source | Beta | - | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅ | ✅ | |
| spyserver_source | Working | - | OPT_BUILD_SPYSERVER_SOURCE | ✅ | ✅ | ✅ |
| usrp_source | Beta | libuhd | OPT_BUILD_USRP_SOURCE | ⛔ | ⛔ | |
| usrp_source | Beta | libuhd | OPT_BUILD_USRP_SOURCE | ⛔ | ⛔ | |
## Sinks
@ -358,6 +360,7 @@ Modules in beta are still included in releases for the most part but not enabled
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:|
| atv_decoder | Unfinished | - | OPT_BUILD_ATV_DECODER | ⛔ | ⛔ | ⛔ |
| dab_decoder | Unfinished | - | OPT_BUILD_DAB_DECODER | ⛔ | ⛔ | ⛔ |
| falcon9_decoder | Unfinished | ffplay | OPT_BUILD_FALCON9_DECODER | ⛔ | ⛔ | ⛔ |
| kgsstv_decoder | Unfinished | - | OPT_BUILD_KGSSTV_DECODER | ⛔ | ⛔ | ⛔ |
| m17_decoder | Working | - | OPT_BUILD_M17_DECODER | ⛔ | ✅ | ⛔ |

View File

@ -217,14 +217,19 @@ private:
}
void startServer() {
if (modeId == SINK_MODE_TCP) {
listener = net::listen(hostname, port);
if (listener) {
listener->acceptAsync(clientHandler, this);
try {
if (modeId == SINK_MODE_TCP) {
listener = net::listen(hostname, port);
if (listener) {
listener->acceptAsync(clientHandler, this);
}
}
else {
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
}
}
else {
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
catch (const std::exception& e) {
flog::error("Failed to open socket: {}", e.what());
}
}

View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.13)
project(fobossdr_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
if (MSVC)
# Lib path
target_link_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/lib/")
target_include_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/include/")
target_link_libraries(fobossdr_source PRIVATE fobos)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBFOBOS REQUIRED libfobos)
target_include_directories(fobossdr_source PRIVATE ${LIBFOBOS_INCLUDE_DIRS})
target_link_directories(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARY_DIRS})
target_link_libraries(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARIES})
endif ()

View File

@ -0,0 +1,560 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <utils/optionlist.h>
#include <atomic>
#include <fobos.h>
SDRPP_MOD_INFO{
/* Name: */ "fobossdr_source",
/* Description: */ "FobosSDR Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
ConfigManager config;
#define CONCAT(a, b) ((std::string(a) + b).c_str())
// Work around for the fobos API not including
#define FOBOS_LNA_GAIN_MIN 1
#define FOBOS_LNA_GAIN_MAX 3
#define FOBOS_VGA_GAIN_MIN 0
#define FOBOS_VGA_GAIN_MAX 31
class FobosSDRSourceModule : public ModuleManager::Instance {
public:
FobosSDRSourceModule(std::string name) {
this->name = name;
sampleRate = 50000000.0;
// Initialize the DDC
ddc.init(&ddcIn, 50e6, 50e6, 50e6, 0.0);
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &ddc.out;
// Refresh devices
refresh();
// Select device from config
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
select(devSerial);
sigpath::sourceManager.registerSource("FobosSDR", &handler);
}
~FobosSDRSourceModule() {
// Nothing to do
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
enum Port {
PORT_RF,
PORT_HF1,
PORT_HF2
};
private:
std::string getBandwdithScaled(double bw) {
char buf[1024];
if (bw >= 1000000.0) {
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
}
else if (bw >= 1000.0) {
sprintf(buf, "%.1lfKHz", bw / 1000.0);
}
else {
sprintf(buf, "%.1lfHz", bw);
}
return std::string(buf);
}
void refresh() {
devices.clear();
// Get device list
char serials[1024];
memset(serials, 0, sizeof(serials));
int devCount = fobos_rx_list_devices(serials);
if (devCount < 0) {
flog::error("Failed to get device list: {}", devCount);
return;
}
// If no device, give up
if (!devCount) { return; }
// Generate device entries
const char* _serials = serials;
int index = 0;
while (*_serials) {
// Read serial until space
std::string serial = "";
while (*_serials) {
// Get a character
char c = *(_serials++);
// If it's a space, we're done
if (c == ' ') { break; }
// Otherwise, add it to the string
serial += c;
}
// Create entry
devices.define(serial, serial, index++);
}
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Get the ID in the list
int id = devices.keyId(serial);
selectedDevId = devices[id];
// Open the device
fobos_dev_t* dev;
int err = fobos_rx_open(&dev, selectedDevId);
if (err) {
flog::error("Failed to open device: {}", err);
return;
}
// Get a list of supported samplerates
double srList[128];
unsigned int srCount;
err = fobos_rx_get_samplerates(dev, srList, &srCount);
if (err) {
flog::error("Failed to get samplerate list: {}", err);
return;
}
// Generate samplerate list
samplerates.clear();
for (int i = 0; i < srCount; i++) {
std::string str = getBandwdithScaled(srList[i]);
samplerates.define(srList[i], str, srList[i]);
}
// Add some custom samplerates
samplerates.define(5e6, "5.0MHz", 5e6);
samplerates.define(2.5e6, "2.5MHz", 2.5e6);
samplerates.define(1.25e6, "1.25MHz", 1.25e6);
// Define the ports
ports.clear();
ports.define("rf", "RF", PORT_RF);
ports.define("hf1", "HF1", PORT_HF1);
ports.define("hf2", "HF2", PORT_HF2);
// Define clock sources
clockSources.clear();
clockSources.define("internal", "Internal", 0);
clockSources.define("external", "External", 1);
// Close the device
fobos_rx_close(dev);
// Save serial number
selectedSerial = serial;
devId = id;
// Load default options
sampleRate = 50e6;
srId = samplerates.valueId(sampleRate);
port = PORT_RF;
portId = ports.valueId(port);
clkSrcId = clockSources.nameId("Internal");
lnaGain = 0;
vgaGain = 0;
// Load config
config.acquire();
if (config.conf["devices"][selectedSerial].contains("samplerate")) {
int desiredSr = config.conf["devices"][selectedSerial]["samplerate"];
if (samplerates.keyExists(desiredSr)) {
srId = samplerates.keyId(desiredSr);
sampleRate = samplerates[srId];
}
}
if (config.conf["devices"][selectedSerial].contains("port")) {
std::string desiredPort = config.conf["devices"][selectedSerial]["port"];
if (ports.keyExists(desiredPort)) {
portId = ports.keyId(desiredPort);
port = ports[portId];
}
}
if (config.conf["devices"][selectedSerial].contains("clkSrc")) {
std::string desiredClkSrc = config.conf["devices"][selectedSerial]["clkSrc"];
if (clockSources.keyExists(desiredClkSrc)) {
clkSrcId = clockSources.keyId(desiredClkSrc);
}
}
if (config.conf["devices"][selectedSerial].contains("lnaGain")) {
lnaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["lnaGain"], FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX);
}
if (config.conf["devices"][selectedSerial].contains("vgaGain")) {
vgaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["vgaGain"], FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX);
}
config.release();
// Update the samplerate
core::setInputSampleRate(sampleRate);
}
static void menuSelected(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("FobosSDRSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
flog::info("FobosSDRSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
if (_this->running) { return; }
// Open the device
int err = fobos_rx_open(&_this->openDev, _this->selectedDevId);
if (err) {
flog::error("Failed to open device: {}", err);
return;
}
// Get the selected port
_this->port = _this->ports[_this->portId];
// Configure the device
double actualSr, actualFreq;
fobos_rx_set_samplerate(_this->openDev, (_this->sampleRate >= 50e6) ? _this->sampleRate : 50e6, &actualSr);
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
fobos_rx_set_direct_sampling(_this->openDev, _this->port != PORT_RF);
fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
// Configure the DDC
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
// Set the frequency
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
}
else if (_this->port == PORT_RF) {
// Set the frequency
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
// Configure and start the DDC for decimation only
_this->ddc.setInSamplerate(actualSr);
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
_this->ddc.setOffset(0.0);
_this->ddc.start();
}
else {
// Configure and start the DDC
_this->ddc.setInSamplerate(actualSr);
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
_this->ddc.setOffset(_this->freq);
_this->ddc.start();
}
// Compute buffer size (Lower than usual, but it's a workaround for their API having broken streaming)
_this->bufferSize = _this->sampleRate / 400.0;
// Start streaming
err = fobos_rx_start_sync(_this->openDev, _this->bufferSize);
if (err) {
flog::error("Failed to start stream: {}", err);
return;
}
// Start worker
_this->run = true;
_this->workerThread = std::thread(&FobosSDRSourceModule::worker, _this);
_this->running = true;
flog::info("FobosSDRSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop worker
_this->run = false;
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
_this->ddc.out.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->ddc.out.clearWriteStop();
}
else {
_this->ddcIn.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->ddcIn.clearWriteStop();
}
// Stop streaming
fobos_rx_stop_sync(_this->openDev);
// Stop the DDC
_this->ddc.stop();
// Close the device
fobos_rx_close(_this->openDev);
flog::info("FobosSDRSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
if (_this->running) {
if (_this->port == PORT_RF) {
double actual; // Dummy, don't care
fobos_rx_set_frequency(_this->openDev, freq, &actual);
}
else {
_this->ddc.setOffset(freq);
}
}
_this->freq = freq;
flog::info("FobosSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_fobossdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
config.acquire();
config.conf["device"] = _this->selectedSerial;
config.release(true);
}
if (SmGui::Combo(CONCAT("##_fobossdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->sampleRate);
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->samplerates.key(_this->srId);
config.release(true);
}
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_fobossdr_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
SmGui::LeftLabel("Antenna Port");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_fobossdr_port_", _this->name), &_this->portId, _this->ports.txt)) {
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["port"] = _this->ports.key(_this->portId);
config.release(true);
}
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("Clock Source");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_fobossdr_clk_", _this->name), &_this->clkSrcId, _this->clockSources.txt)) {
if (_this->running) {
fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["clkSrc"] = _this->clockSources.key(_this->clkSrcId);
config.release(true);
}
}
if (_this->port == PORT_RF) {
SmGui::LeftLabel("LNA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_fobossdr_lna_gain_", _this->name), &_this->lnaGain, FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX)) {
if (_this->running) {
fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["lnaGain"] = _this->lnaGain;
config.release(true);
}
}
SmGui::LeftLabel("VGA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_fobossdr_vga_gain_", _this->name), &_this->vgaGain, FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX)) {
if (_this->running) {
fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["vgaGain"] = _this->vgaGain;
config.release(true);
}
}
}
}
void worker() {
// Select different processing depending on the mode
if (port == PORT_RF && sampleRate >= 50e6) {
while (run) {
// Read samples
unsigned int sampCount = 0;
int err = fobos_rx_read_sync(openDev, (float*)ddc.out.writeBuf, &sampCount);
if (err) { break; }
// Send out samples to the core
if (!ddc.out.swap(sampCount)) { break; }
}
}
else if (port == PORT_RF) {
while (run) {
// Read samples
unsigned int sampCount = 0;
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
if (err) { break; }
// Send samples to the DDC
if (!ddcIn.swap(sampCount)) { break; }
}
}
else if (port == PORT_HF1) {
while (run) {
// Read samples
unsigned int sampCount = 0;
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
if (err) { break; }
// Null out the HF2 samples
for (int i = 0; i < sampCount; i++) {
ddcIn.writeBuf[i].im = 0.0f;
}
// Send samples to the DDC
if (!ddcIn.swap(sampCount)) { break; }
}
}
else if (port == PORT_HF2) {
while (run) {
// Read samples
unsigned int sampCount = 0;
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
if (err) { break; }
// Null out the HF2 samples
for (int i = 0; i < sampCount; i++) {
ddcIn.writeBuf[i].re = 0.0f;
}
// Send samples to the DDC
if (!ddcIn.swap(sampCount)) { break; }
}
}
}
std::string name;
bool enabled = true;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
OptionList<std::string, int> devices;
OptionList<int, double> samplerates;
OptionList<std::string, Port> ports;
OptionList<std::string, int> clockSources;
int devId = 0;
int srId = 0;
int portId = 0;
int clkSrcId = 0;
Port port;
int lnaGain = 0;
int vgaGain = 0;
std::string selectedSerial;
int selectedDevId;
fobos_dev_t* openDev;
int bufferSize;
std::thread workerThread;
std::atomic<bool> run = false;
dsp::stream<dsp::complex_t> ddcIn;
dsp::channel::RxVFO ddc;
};
MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(core::args["root"].s() + "/fobossdr_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new FobosSDRSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (FobosSDRSourceModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -0,0 +1 @@
vendor/*

View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.13)
project(kcsdr_source)
file(GLOB SRC "src/*.cpp" "src/*.c")
include(${SDRPP_MODULE_CMAKE})
target_link_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10/x64/DLL")
target_include_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10")
target_link_libraries(kcsdr_source PRIVATE FTD3XX)

View File

@ -0,0 +1,209 @@
#include "kcsdr.h"
#include <string.h>
#include "../vendor/FTD3XXLibrary_1.3.0.10/FTD3XX.h"
#include <stdio.h>
#include <stddef.h>
#define KCSDR_PKT_EMPTY_LEN 0x0C
#define KCSDR_COMMAND_PIPE 0x02
#define KCSDR_RX_DATA_PIPE 0x83
#define KCSDR_TX_DATA_PIPE 0x03
struct kcsdr {
FT_HANDLE ft;
};
#pragma pack(push, 1)
struct kcsdr_packet {
uint8_t zeros0[4];
uint8_t length;
uint8_t zeros1[2];
uint8_t hex_eighty;
uint32_t command;
uint8_t data[188];
};
typedef struct kcsdr_packet kcsdr_packet_t;
#pragma pack(pop)
enum kcsdr_command {
CMD_NOT_USED_0x00 = 0x00,
CMD_SET_PORT = 0x01,
CMD_SET_FREQUENCY = 0x02,
CMD_SET_ATTENUATION = 0x03,
CMD_SET_AMPLIFIER = 0x04,
CMD_SET_BANDWIDTH = 0x05,
CMD_START = 0x06,
CMD_STOP = 0x07,
CMD_SET_EXT_AMP = 0x08,
CMD_START_REMOTE = 0x09,
CMD_STOP_REMOTE = 0x0A
};
typedef enum kcsdr_command kcsdr_command_t;
int kcsdr_send_command(kcsdr_t* dev, kcsdr_direction_t dir, kcsdr_command_t cmd, const uint8_t* data, int len) {
Sleep(50);
// Create an empty packet
kcsdr_packet_t pkt;
memset(&pkt, 0, sizeof(kcsdr_packet_t));
// Fill out the packet info
pkt.length = len + KCSDR_PKT_EMPTY_LEN;
pkt.hex_eighty = 0x80; // Whatever the fuck that is
pkt.command = (uint32_t)cmd | (uint32_t)dir;
// Copy the data if there is some
if (len) { memcpy(pkt.data, data, len); }
// Dump the bytes
uint8_t* dump = (uint8_t*)&pkt;
printf("Sending:");
for (int i = 0; i < pkt.length; i++) {
printf(" %02X", dump[i]);
}
printf("\n");
// Send the command to endpoint 0
int sent;
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_COMMAND_PIPE, (uint8_t*)&pkt, sizeof(kcsdr_packet_t), &sent, NULL);
if (err != FT_OK) {
return -err;
}
printf("Sent %d bytes (%d)\n", sent, err);
Sleep(50);
// Flush existing commands
FT_FlushPipe(dev->ft, KCSDR_COMMAND_PIPE);
return -(int)err;
}
int kcsdr_list_devices(kcsdr_info_t** devices) {
// Generate a list of FTDI devices
int ftdiDevCount = 0;
FT_STATUS err = FT_CreateDeviceInfoList(&ftdiDevCount);
if (err != FT_OK) {
return -1;
}
// If no device was found, return nothing
if (!ftdiDevCount) {
*devices = NULL;
return 0;
}
// Get said device list
FT_DEVICE_LIST_INFO_NODE* list = malloc(ftdiDevCount * sizeof(FT_DEVICE_LIST_INFO_NODE));
err = FT_GetDeviceInfoList(list, &ftdiDevCount);
if (err != FT_OK) {
return -1;
}
// Allocate the device info list
*devices = malloc(ftdiDevCount * sizeof(kcsdr_info_t));
// Find all KC908s
int kcCount = 0;
for (int i = 0; i < ftdiDevCount; i++) {
strcpy((*devices)[kcCount++].serial, list[i].SerialNumber);
}
// Free the FTDI list
free(list);
return kcCount;
}
void kcsdr_free_device_list(kcsdr_info_t* devices) {
// Free the list
if (devices) { free(devices); }
}
int kcsdr_open(kcsdr_t** dev, const char* serial) {
// Attempt to open the device using the serial number
FT_HANDLE ft;
FT_STATUS err = FT_Create(serial, FT_OPEN_BY_SERIAL_NUMBER, &ft);
if (err != FT_OK) {
return -1;
}
// Set the timeouts for the data pipes
FT_SetPipeTimeout(ft, KCSDR_RX_DATA_PIPE, 1000);
FT_SetPipeTimeout(ft, KCSDR_TX_DATA_PIPE, 1000);
// Allocate the device object
*dev = malloc(sizeof(kcsdr_t));
// Fill out the device object
(*dev)->ft = ft;
// Put device into remote control mode
return kcsdr_send_command(*dev, KCSDR_DIR_RX, CMD_START_REMOTE, NULL, 0);
}
void kcsdr_close(kcsdr_t* dev) {
// Put device back in normal mode
kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_STOP_REMOTE, NULL, 0);
// Close the device
FT_Close(dev->ft);
// Free the device object
free(dev);
}
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port) {
// Send SET_PORT command
return kcsdr_send_command(dev, dir, CMD_SET_PORT, &port, 1);
}
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq) {
// Send SET_FREQUENCY command
return kcsdr_send_command(dev, dir, CMD_SET_FREQUENCY, (uint8_t*)&freq, 8);
}
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att) {
// Send SET_ATTENUATION command
return kcsdr_send_command(dev, dir, CMD_SET_ATTENUATION, &att, 1);
}
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain) {
// Send SET_AMPLIFIER command
return kcsdr_send_command(dev, dir, CMD_SET_AMPLIFIER, &gain, 1);
}
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain) {
// Send CMD_SET_EXT_AMP command
return kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_SET_EXT_AMP, &gain, 1);
}
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate) {
// Set SET_BANDWIDTH command
return kcsdr_send_command(dev, dir, CMD_SET_BANDWIDTH, (uint8_t*)&samplerate, 4);
}
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir) {
// Send START command
return kcsdr_send_command(dev, dir, CMD_START, NULL, 0);
}
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir) {
// Send STOP command
return kcsdr_send_command(dev, dir, CMD_STOP, NULL, 0);
}
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count) {
// Receive samples (TODO: Endpoint might be 0x81)
int received;
FT_STATUS err = FT_ReadPipeEx(dev->ft, KCSDR_RX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &received, NULL);
return (err == FT_OK) ? received / (2*sizeof(uint16_t)) : -(int)err;
}
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count) {
// Transmit samples
int sent;
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_TX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &sent, NULL);
return (err == FT_OK) ? sent / (2*sizeof(uint16_t)) : -(int)err;
}

View File

@ -0,0 +1,150 @@
#pragma once
#include <stdint.h>
#define KCSDR_SERIAL_LEN 16
#define KCSDR_MAX_PORTS 6
// Detect C++
#ifdef __cplusplus
extern "C" {
#endif
/**
* KCSDR Device.
*/
struct kcsdr;
typedef struct kcsdr kcsdr_t;
/**
* Device Information
*/
struct kcsdr_info {
char serial[KCSDR_SERIAL_LEN+1];
};
typedef struct kcsdr_info kcsdr_info_t;
/**
* RF Direction.
*/
enum kcsdr_direction {
KCSDR_DIR_RX = 0x00,
KCSDR_DIR_TX = 0x80
};
typedef enum kcsdr_direction kcsdr_direction_t;
/**
* Get a list of KCSDR devices on the system.
* @param devices Pointer to an array of device info.
* @return Number of devices found or error code.
*/
int kcsdr_list_devices(kcsdr_info_t** devices);
/**
* Free a device list returned by `kcsdr_list_devices()`.
* @param devices Device list to free.
*/
void kcsdr_free_device_list(kcsdr_info_t* devices);
/**
* Open a KCSDR device.
* @param dev Newly open device.
* @param serial Serial number of the device to open as returned in the device list.
* @return 0 on success, error code otherwise.
*/
int kcsdr_open(kcsdr_t** dev, const char* serial);
/**
* Close a KCSDR device.
* @param dev Device to be closed.
*/
void kcsdr_close(kcsdr_t* dev);
/**
* Select the RF port.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @param port RF port number to select.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port);
/**
* Set the center frequency.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param freq Frequency in Hz.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq);
/**
* Set the attenuation.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param samplerate Attenuation in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att);
/**
* Set the internal amplifier gain.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param gain Gain in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain);
/**
* Set the external amplifier gain.
* @param dev Device to control.
* @param gain Gain in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain);
/**
* Set the samplerate.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param samplerate Samplerate in Hz.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate);
/**
* Start streaming samples.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @return 0 on success, error code otherwise.
*/
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir);
/**
* Stop streaming samples.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @return 0 on success, error code otherwise.
*/
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir);
/**
* Receive a buffer of samples.
* @param samples Sample buffer.
* @param count Number of complex samples.
* @return Number of samples received.
*/
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count);
/**
* Transmit a buffer of samples.
* @param samples Sample buffer.
* @param count Number of complex samples.
* @return Number of samples transmitted.
*/
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count);
// Detect C++
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,324 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <utils/optionlist.h>
#include "kcsdr.h"
#include <atomic>
SDRPP_MOD_INFO{
/* Name: */ "kcsdr_source",
/* Description: */ "KCSDR Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
#define CONCAT(a, b) ((std::string(a) + b).c_str())
class KCSDRSourceModule : public ModuleManager::Instance {
public:
KCSDRSourceModule(std::string name) {
this->name = name;
sampleRate = 2000000.0;
samplerates.define(40e6, "40MHz", 40e6);
samplerates.define(35e6, "35MHz", 35e6);
samplerates.define(30e6, "30MHz", 30e6);
samplerates.define(25e6, "25MHz", 25e6);
samplerates.define(20e6, "20MHz", 20e6);
samplerates.define(15e6, "15MHz", 15e6);
samplerates.define(10e6, "10MHz", 10e6);
samplerates.define(5e6, "5MHz", 5e6);
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &stream;
// Refresh devices
refresh();
// Select first (TODO: Select from config)
select("");
sigpath::sourceManager.registerSource("KCSDR", &handler);
}
~KCSDRSourceModule() {
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
void refresh() {
devices.clear();
// Get device list
kcsdr_info_t* list;
int count = kcsdr_list_devices(&list);
if (count < 0) {
flog::error("Failed to list devices: {}", count);
return;
}
// Create list
for (int i = 0; i < count; i++) {
devices.define(list[i].serial, list[i].serial, list[i].serial);
}
// Free the device list
kcsdr_free_device_list(list);
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Get the menu ID
devId = devices.keyId(serial);
// TODO
// Update the samplerate
core::setInputSampleRate(sampleRate);
// Save serial number
selectedSerial = serial;
}
static void menuSelected(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("KCSDRSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
flog::info("KCSDRSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) { return; }
// If no serial is given, do nothing
if (_this->selectedSerial.empty()) { return; }
// Open the device
int err = kcsdr_open(&_this->openDev, _this->selectedSerial.c_str());
if (err) {
flog::error("Failed to open device: {}", err);
return;
}
// Configure the device
kcsdr_set_port(_this->openDev, KCSDR_DIR_RX, 0);
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, _this->freq);
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
kcsdr_set_samplerate(_this->openDev, KCSDR_DIR_RX, _this->sampleRate);
// Start the stream
kcsdr_start(_this->openDev, KCSDR_DIR_RX);
// Start worker
_this->run = true;
_this->workerThread = std::thread(&KCSDRSourceModule::worker, _this);
_this->running = true;
flog::info("KCSDRSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop worker
_this->run = false;
_this->stream.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->stream.clearWriteStop();
// Stop streaming
kcsdr_stop(_this->openDev, KCSDR_DIR_RX);
// Close the device
kcsdr_close(_this->openDev);
flog::info("KCSDRSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) {
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, freq);
}
_this->freq = freq;
flog::info("KCSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_kcsdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
if (SmGui::Combo(CONCAT("##_kcsdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_kcsdr_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
// SmGui::LeftLabel("RX Port");
// SmGui::FillWidth();
// if (SmGui::Combo(CONCAT("##_kcsdr_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
// if (_this->running) {
// // TODO
// }
// // TODO: Save
// }
SmGui::LeftLabel("Attenuation");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_att_", _this->name), &_this->att, 0, 31)) {
if (_this->running) {
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
}
// TODO: Save
}
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_gain_", _this->name), &_this->gain, 0, 31)) {
if (_this->running) {
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
}
// TODO: Save
}
SmGui::LeftLabel("External Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_ext_gain_", _this->name), &_this->extGain, 0, 31)) {
if (_this->running) {
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
}
// TODO: Save
}
}
void worker() {
// Compute the buffer size
int bufferSize = 0x4000/4;//sampleRate / 200;
// Allocate the sample buffer
int16_t* samps = dsp::buffer::alloc<int16_t>(bufferSize*2);
// Loop
while (run) {
// Read samples
int count = kcsdr_rx(openDev, samps, bufferSize);
if (!count) { continue; }
if (count < 0) {
flog::debug("Failed to read samples: {}", count);
break;
}
// Convert the samples to float
volk_16i_s32f_convert_32f((float*)stream.writeBuf, samps, 8192.0f, count*2);
// Send out the samples
if (!stream.swap(count)) { break; }
}
// Free the sample buffer
dsp::buffer::free(samps);
}
std::string name;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
OptionList<std::string, std::string> devices;
OptionList<int, double> samplerates;
int devId = 0;
int srId = 0;
int att = 0;
int gain = 30;
int extGain = 1;
int portId = 0;
std::string selectedSerial;
kcsdr_t* openDev;
std::thread workerThread;
std::atomic<bool> run = false;
};
MOD_EXPORT void _INIT_() {
// Nothing here
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new KCSDRSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (KCSDRSourceModule*)instance;
}
MOD_EXPORT void _END_() {
// Nothing here
}

View File

@ -23,6 +23,12 @@ SDRPP_MOD_INFO{
ConfigManager config;
const std::vector<const char*> deviceWhiteList = {
"PlutoSDR",
"ANTSDR",
"LibreSDR"
};
class PlutoSDRSourceModule : public ModuleManager::Instance {
public:
PlutoSDRSourceModule(std::string name) {
@ -130,7 +136,14 @@ private:
std::string duri = iio_context_info_get_uri(info);
// If the device is not a plutosdr, don't include it
if (desc.find("PlutoSDR") == std::string::npos) {
bool isPluto = false;
for (const auto type : deviceWhiteList) {
if (desc.find(type) != std::string::npos) {
isPluto = true;
break;
}
}
if (!isPluto) {
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
continue;
}