mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 02:39:11 +01:00 
			
		
		
		
	Compare commits
	
		
			31 Commits
		
	
	
		
			dab_experi
			...
			new_source
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6891d0bb0f | ||
| 
						 | 
					b835d07573 | ||
| 
						 | 
					69161253e8 | ||
| 
						 | 
					5ab3428b90 | ||
| 
						 | 
					7f002f6276 | ||
| 
						 | 
					a728403a3f | ||
| 
						 | 
					0f1d2da3b7 | ||
| 
						 | 
					6d0b65c27f | ||
| 
						 | 
					f640cdcb6a | ||
| 
						 | 
					80a90e13d9 | ||
| 
						 | 
					3982db73d3 | ||
| 
						 | 
					bd64f07a20 | ||
| 
						 | 
					c9950d9331 | ||
| 
						 | 
					9bc609f4e4 | ||
| 
						 | 
					bcc8e20e66 | ||
| 
						 | 
					b07e828fed | ||
| 
						 | 
					bd24a4a5eb | ||
| 
						 | 
					fe4a7b32a7 | ||
| 
						 | 
					0e1ab29b5d | ||
| 
						 | 
					fbbafddd3d | ||
| 
						 | 
					1cbc8ec6f5 | ||
| 
						 | 
					9f65e3ec71 | ||
| 
						 | 
					08f3a7d201 | ||
| 
						 | 
					9ce62f8885 | ||
| 
						 | 
					caeaa2d46c | ||
| 
						 | 
					7ae030a3a6 | ||
| 
						 | 
					1b27379a3d | ||
| 
						 | 
					e52123038e | ||
| 
						 | 
					ec8c60111d | ||
| 
						 | 
					f61799cf5f | ||
| 
						 | 
					e835c8dd9a | 
@@ -19,6 +19,7 @@ option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: li
 | 
			
		||||
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)
 | 
			
		||||
@@ -160,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)
 | 
			
		||||
 
 | 
			
		||||
@@ -183,6 +183,8 @@ int sdrpp_main(int argc, char* argv[]) {
 | 
			
		||||
    defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
 | 
			
		||||
    defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["Network Source"]["module"] = "network_source";
 | 
			
		||||
    defConfig["moduleInstances"]["Network Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
 | 
			
		||||
    defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
 | 
			
		||||
@@ -230,12 +232,19 @@ int sdrpp_main(int argc, char* argv[]) {
 | 
			
		||||
 | 
			
		||||
    defConfig["modules"] = json::array();
 | 
			
		||||
 | 
			
		||||
    defConfig["offsetMode"] = (int)0; // Off
 | 
			
		||||
    defConfig["offset"] = 0.0;
 | 
			
		||||
    defConfig["offsets"]["SpyVerter"] = 120000000.0;
 | 
			
		||||
    defConfig["offsets"]["Ham-It-Up"] = 125000000.0;
 | 
			
		||||
    defConfig["offsets"]["MMDS S-band (1998MHz)"] = -1998000000.0;
 | 
			
		||||
    defConfig["offsets"]["DK5AV X-Band"] = -6800000000.0;
 | 
			
		||||
    defConfig["offsets"]["Ku LNB (9750MHz)"] = -9750000000.0;
 | 
			
		||||
    defConfig["offsets"]["Ku LNB (10700MHz)"] = -10700000000.0;
 | 
			
		||||
 | 
			
		||||
    defConfig["selectedOffset"] = "None";
 | 
			
		||||
    defConfig["manualOffset"] = 0.0;
 | 
			
		||||
    defConfig["showMenu"] = true;
 | 
			
		||||
    defConfig["showWaterfall"] = true;
 | 
			
		||||
    defConfig["source"] = "";
 | 
			
		||||
    defConfig["decimationPower"] = 0;
 | 
			
		||||
    defConfig["decimation"] = 1;
 | 
			
		||||
    defConfig["iqCorrection"] = false;
 | 
			
		||||
    defConfig["invertIQ"] = false;
 | 
			
		||||
 | 
			
		||||
@@ -316,12 +325,18 @@ int sdrpp_main(int argc, char* argv[]) {
 | 
			
		||||
 | 
			
		||||
    // Remove unused elements
 | 
			
		||||
    auto items = core::configManager.conf.items();
 | 
			
		||||
    auto newConf = core::configManager.conf;
 | 
			
		||||
    bool configCorrected = false;
 | 
			
		||||
    for (auto const& item : items) {
 | 
			
		||||
        if (!defConfig.contains(item.key())) {
 | 
			
		||||
            flog::info("Unused key in config {0}, repairing", item.key());
 | 
			
		||||
            core::configManager.conf.erase(item.key());
 | 
			
		||||
            newConf.erase(item.key());
 | 
			
		||||
            configCorrected = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (configCorrected) {
 | 
			
		||||
        core::configManager.conf = newConf;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update to new module representation in config if needed
 | 
			
		||||
    for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,113 +5,120 @@
 | 
			
		||||
#include <gui/main_window.h>
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <utils/optionlist.h>
 | 
			
		||||
#include <gui/dialogs/dialog_box.h>
 | 
			
		||||
 | 
			
		||||
namespace sourcemenu {
 | 
			
		||||
    int offsetMode = 0;
 | 
			
		||||
    int sourceId = 0;
 | 
			
		||||
    double customOffset = 0.0;
 | 
			
		||||
    double effectiveOffset = 0.0;
 | 
			
		||||
    int decimationPower = 0;
 | 
			
		||||
    EventHandler<std::string> sourcesChangedHandler;
 | 
			
		||||
    EventHandler<std::string> sourceUnregisterHandler;
 | 
			
		||||
    OptionList<std::string, std::string> sources;
 | 
			
		||||
    std::string selectedSource;
 | 
			
		||||
 | 
			
		||||
    int decimId = 0;
 | 
			
		||||
    OptionList<int, int> decimations;
 | 
			
		||||
 | 
			
		||||
    bool iqCorrection = false;
 | 
			
		||||
    bool invertIQ = false;
 | 
			
		||||
 | 
			
		||||
    EventHandler<std::string> sourceRegisteredHandler;
 | 
			
		||||
    EventHandler<std::string> sourceUnregisterHandler;
 | 
			
		||||
    EventHandler<std::string> sourceUnregisteredHandler;
 | 
			
		||||
    int offsetId = 0;
 | 
			
		||||
    double manualOffset = 0.0;
 | 
			
		||||
    std::string selectedOffset;
 | 
			
		||||
    double effectiveOffset = 0.0;
 | 
			
		||||
    OptionList<std::string, double> offsets;
 | 
			
		||||
    std::map<std::string, double> namedOffsets;
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> sourceNames;
 | 
			
		||||
    std::string sourceNamesTxt;
 | 
			
		||||
    std::string selectedSource;
 | 
			
		||||
    bool showAddOffsetDialog = false;
 | 
			
		||||
    char newOffsetName[1024];
 | 
			
		||||
    double newOffset = 0.0;
 | 
			
		||||
 | 
			
		||||
    bool showDelOffsetDialog = false;
 | 
			
		||||
    std::string delOffsetName = "";
 | 
			
		||||
 | 
			
		||||
    // Offset IDs
 | 
			
		||||
    enum {
 | 
			
		||||
        OFFSET_MODE_NONE,
 | 
			
		||||
        OFFSET_MODE_CUSTOM,
 | 
			
		||||
        OFFSET_MODE_SPYVERTER,
 | 
			
		||||
        OFFSET_MODE_HAM_IT_UP,
 | 
			
		||||
        OFFSET_MODE_MMDS_SB_1998,
 | 
			
		||||
        OFFSET_MODE_DK5AV_XB,
 | 
			
		||||
        OFFSET_MODE_KU_LNB_9750,
 | 
			
		||||
        OFFSET_MODE_KU_LNB_10700,
 | 
			
		||||
        _OFFSET_MODE_COUNT
 | 
			
		||||
        OFFSET_ID_NONE,
 | 
			
		||||
        OFFSET_ID_MANUAL,
 | 
			
		||||
        OFFSET_ID_CUSTOM_BASE
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const char* offsetModesTxt = "None\0"
 | 
			
		||||
                                 "Custom\0"
 | 
			
		||||
                                 "SpyVerter\0"
 | 
			
		||||
                                 "Ham-It-Up\0"
 | 
			
		||||
                                 "MMDS S-band (1998MHz)\0"
 | 
			
		||||
                                 "DK5AV X-Band\0"
 | 
			
		||||
                                 "Ku LNB (9750MHz)\0"
 | 
			
		||||
                                 "Ku LNB (10700MHz)\0";
 | 
			
		||||
 | 
			
		||||
    const char* decimationStages = "None\0"
 | 
			
		||||
                                   "2\0"
 | 
			
		||||
                                   "4\0"
 | 
			
		||||
                                   "8\0"
 | 
			
		||||
                                   "16\0"
 | 
			
		||||
                                   "32\0"
 | 
			
		||||
                                   "64\0";
 | 
			
		||||
 | 
			
		||||
    void updateOffset() {
 | 
			
		||||
        if (offsetMode == OFFSET_MODE_CUSTOM) { effectiveOffset = customOffset; }
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_SPYVERTER) {
 | 
			
		||||
            effectiveOffset = 120000000;
 | 
			
		||||
        } // 120MHz Up-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_HAM_IT_UP) {
 | 
			
		||||
            effectiveOffset = 125000000;
 | 
			
		||||
        } // 125MHz Up-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_MMDS_SB_1998) {
 | 
			
		||||
            effectiveOffset = -1998000000;
 | 
			
		||||
        } // 1.998GHz Down-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_DK5AV_XB) {
 | 
			
		||||
            effectiveOffset = -6800000000;
 | 
			
		||||
        } // 6.8GHz Down-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_KU_LNB_9750) {
 | 
			
		||||
            effectiveOffset = -9750000000;
 | 
			
		||||
        } // 9.750GHz Down-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_KU_LNB_10700) {
 | 
			
		||||
            effectiveOffset = -10700000000;
 | 
			
		||||
        } // 10.7GHz Down-conversion
 | 
			
		||||
        else {
 | 
			
		||||
        // Compute the effective offset
 | 
			
		||||
        switch (offsetId) {
 | 
			
		||||
        case OFFSET_ID_NONE:
 | 
			
		||||
            effectiveOffset = 0;
 | 
			
		||||
            break;
 | 
			
		||||
        case OFFSET_ID_MANUAL:
 | 
			
		||||
            effectiveOffset = manualOffset;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            effectiveOffset = namedOffsets[offsets.name(offsetId)];
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Apply it
 | 
			
		||||
        sigpath::sourceManager.setTuningOffset(effectiveOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void selectOffsetById(int id) {
 | 
			
		||||
        // Update the offset mode
 | 
			
		||||
        offsetId = id;
 | 
			
		||||
        selectedOffset = offsets.name(id);
 | 
			
		||||
 | 
			
		||||
        // Update the offset
 | 
			
		||||
        updateOffset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void selectOffsetByName(const std::string& name) {
 | 
			
		||||
        // If the name doesn't exist, select 'None'
 | 
			
		||||
        if (!offsets.nameExists(name)) {
 | 
			
		||||
            selectOffsetById(OFFSET_ID_NONE);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Select using the ID associated with the name
 | 
			
		||||
        selectOffsetById(offsets.nameId(name));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void refreshSources() {
 | 
			
		||||
        sourceNames = sigpath::sourceManager.getSourceNames();
 | 
			
		||||
        sourceNamesTxt.clear();
 | 
			
		||||
        // Get sources
 | 
			
		||||
        auto sourceNames = sigpath::sourceManager.getSourceNames();
 | 
			
		||||
 | 
			
		||||
        // Define source options
 | 
			
		||||
        sources.clear();
 | 
			
		||||
        for (auto name : sourceNames) {
 | 
			
		||||
            sourceNamesTxt += name;
 | 
			
		||||
            sourceNamesTxt += '\0';
 | 
			
		||||
            sources.define(name, name, name);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void selectSource(std::string name) {
 | 
			
		||||
        if (sourceNames.empty()) {
 | 
			
		||||
        // If there is no source, give up
 | 
			
		||||
        if (sources.empty()) {
 | 
			
		||||
            sourceId = 0;
 | 
			
		||||
            selectedSource.clear();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        auto it = std::find(sourceNames.begin(), sourceNames.end(), name);
 | 
			
		||||
        if (it == sourceNames.end()) {
 | 
			
		||||
            selectSource(sourceNames[0]);
 | 
			
		||||
 | 
			
		||||
        // If a source with the given name doesn't exist, select the first source instead
 | 
			
		||||
        if (!sources.valueExists(name)) {
 | 
			
		||||
            selectSource(sources.value(0));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        sourceId = std::distance(sourceNames.begin(), it);
 | 
			
		||||
        selectedSource = sourceNames[sourceId];
 | 
			
		||||
        sigpath::sourceManager.selectSource(sourceNames[sourceId]);
 | 
			
		||||
 | 
			
		||||
        // Update the GUI variables
 | 
			
		||||
        sourceId = sources.valueId(name);
 | 
			
		||||
        selectedSource = name;
 | 
			
		||||
 | 
			
		||||
        // Select the source module
 | 
			
		||||
        sigpath::sourceManager.selectSource(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void onSourceRegistered(std::string name, void* ctx) {
 | 
			
		||||
    void onSourcesChanged(std::string name, void* ctx) {
 | 
			
		||||
        // Update the source list
 | 
			
		||||
        refreshSources();
 | 
			
		||||
 | 
			
		||||
        if (selectedSource.empty()) {
 | 
			
		||||
            sourceId = 0;
 | 
			
		||||
            selectSource(sourceNames[0]);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
 | 
			
		||||
        // Reselect the current source
 | 
			
		||||
        selectSource(selectedSource);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void onSourceUnregister(std::string name, void* ctx) {
 | 
			
		||||
@@ -120,60 +127,173 @@ namespace sourcemenu {
 | 
			
		||||
        // TODO: Stop everything
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void onSourceUnregistered(std::string name, void* ctx) {
 | 
			
		||||
        refreshSources();
 | 
			
		||||
    void reloadOffsets() {
 | 
			
		||||
        // Clear list
 | 
			
		||||
        offsets.clear();
 | 
			
		||||
        namedOffsets.clear();
 | 
			
		||||
 | 
			
		||||
        if (sourceNames.empty()) {
 | 
			
		||||
            selectedSource = "";
 | 
			
		||||
            return;
 | 
			
		||||
        // Define special offset modes
 | 
			
		||||
        offsets.define("None", OFFSET_ID_NONE);
 | 
			
		||||
        offsets.define("Manual", OFFSET_ID_MANUAL);
 | 
			
		||||
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.acquire();
 | 
			
		||||
 | 
			
		||||
        // Load custom offsets
 | 
			
		||||
        auto ofs = core::configManager.conf["offsets"].items();
 | 
			
		||||
        for (auto& o : ofs) {
 | 
			
		||||
            namedOffsets[o.key()] = (double)o.value();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (name == selectedSource) {
 | 
			
		||||
            sourceId = std::clamp<int>(sourceId, 0, sourceNames.size() - 1);
 | 
			
		||||
            selectSource(sourceNames[sourceId]);
 | 
			
		||||
            return;
 | 
			
		||||
        // Define custom offsets
 | 
			
		||||
        for (auto& [name, offset] : namedOffsets) {
 | 
			
		||||
            offsets.define(name, offsets.size());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
 | 
			
		||||
        // Release the config file
 | 
			
		||||
        core::configManager.release();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void init() {
 | 
			
		||||
        // Load offset modes
 | 
			
		||||
        reloadOffsets();
 | 
			
		||||
 | 
			
		||||
        // Define decimation values
 | 
			
		||||
        decimations.define(1, "None", 1);
 | 
			
		||||
        decimations.define(2, "2x", 2);
 | 
			
		||||
        decimations.define(4, "4x", 4);
 | 
			
		||||
        decimations.define(8, "8x", 8);
 | 
			
		||||
        decimations.define(16, "16x", 16);
 | 
			
		||||
        decimations.define(32, "32x", 32);
 | 
			
		||||
        decimations.define(64, "64x", 64);
 | 
			
		||||
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.acquire();
 | 
			
		||||
        std::string selected = core::configManager.conf["source"];
 | 
			
		||||
        customOffset = core::configManager.conf["offset"];
 | 
			
		||||
        offsetMode = core::configManager.conf["offsetMode"];
 | 
			
		||||
        decimationPower = core::configManager.conf["decimationPower"];
 | 
			
		||||
 | 
			
		||||
        // Load other settings
 | 
			
		||||
        std::string selectedSource = core::configManager.conf["source"];
 | 
			
		||||
        manualOffset = core::configManager.conf["manualOffset"];
 | 
			
		||||
        std::string selectedOffset = core::configManager.conf["selectedOffset"];
 | 
			
		||||
        iqCorrection = core::configManager.conf["iqCorrection"];
 | 
			
		||||
        invertIQ = core::configManager.conf["invertIQ"];
 | 
			
		||||
        int decimation = core::configManager.conf["decimation"];
 | 
			
		||||
        if (decimations.keyExists(decimation)) {
 | 
			
		||||
            decimId = decimations.keyId(decimation);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Release the config file
 | 
			
		||||
        core::configManager.release();
 | 
			
		||||
 | 
			
		||||
        // Select the source module
 | 
			
		||||
        refreshSources();
 | 
			
		||||
        selectSource(selectedSource);
 | 
			
		||||
 | 
			
		||||
        // Update frontend settings
 | 
			
		||||
        sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
 | 
			
		||||
        sigpath::iqFrontEnd.setInvertIQ(invertIQ);
 | 
			
		||||
        updateOffset();
 | 
			
		||||
        sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
 | 
			
		||||
        selectOffsetByName(selectedOffset);
 | 
			
		||||
 | 
			
		||||
        refreshSources();
 | 
			
		||||
        selectSource(selected);
 | 
			
		||||
        sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
 | 
			
		||||
 | 
			
		||||
        sourceRegisteredHandler.handler = onSourceRegistered;
 | 
			
		||||
        // Register handlers
 | 
			
		||||
        sourcesChangedHandler.handler = onSourcesChanged;
 | 
			
		||||
        sourceUnregisterHandler.handler = onSourceUnregister;
 | 
			
		||||
        sourceUnregisteredHandler.handler = onSourceUnregistered;
 | 
			
		||||
        sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
 | 
			
		||||
        sigpath::sourceManager.onSourceRegistered.bindHandler(&sourcesChangedHandler);
 | 
			
		||||
        sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
 | 
			
		||||
        sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
 | 
			
		||||
        sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourcesChangedHandler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        core::configManager.release();
 | 
			
		||||
    void addOffset(const std::string& name, double offset) {
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.acquire();
 | 
			
		||||
 | 
			
		||||
        // Define a new offset
 | 
			
		||||
        core::configManager.conf["offsets"][name] = offset;
 | 
			
		||||
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.release(true);
 | 
			
		||||
 | 
			
		||||
        // Reload the offsets
 | 
			
		||||
        reloadOffsets();
 | 
			
		||||
 | 
			
		||||
        // Attempt to re-select the same one
 | 
			
		||||
        selectOffsetByName(selectedOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void delOffset(const std::string& name) {
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.acquire();
 | 
			
		||||
 | 
			
		||||
        // Define a new offset
 | 
			
		||||
        core::configManager.conf["offsets"].erase(name);
 | 
			
		||||
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.release(true);
 | 
			
		||||
 | 
			
		||||
        // Reload the offsets
 | 
			
		||||
        reloadOffsets();
 | 
			
		||||
 | 
			
		||||
        // Attempt to re-select the same one
 | 
			
		||||
        selectOffsetByName(selectedOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool addOffsetDialog() {
 | 
			
		||||
        bool open = true;
 | 
			
		||||
        gui::mainWindow.lockWaterfallControls = true;
 | 
			
		||||
 | 
			
		||||
        float menuWidth = ImGui::GetContentRegionAvail().x;
 | 
			
		||||
 | 
			
		||||
        const char* id = "Add offset##sdrpp_add_offset_dialog_";
 | 
			
		||||
        ImGui::OpenPopup(id);
 | 
			
		||||
 | 
			
		||||
        if (ImGui::BeginPopup(id, ImGuiWindowFlags_NoResize)) {
 | 
			
		||||
            ImGui::LeftLabel("Name");
 | 
			
		||||
            ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
 | 
			
		||||
            ImGui::InputText("##sdrpp_add_offset_name", newOffsetName, 1023);
 | 
			
		||||
 | 
			
		||||
            ImGui::LeftLabel("Offset");
 | 
			
		||||
            ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
 | 
			
		||||
            ImGui::InputDouble("##sdrpp_add_offset_offset", &newOffset);
 | 
			
		||||
 | 
			
		||||
            bool nameExists = offsets.nameExists(newOffsetName);
 | 
			
		||||
            bool reservedName = !strcmp(newOffsetName, "None") || !strcmp(newOffsetName, "Manual");
 | 
			
		||||
            bool denyApply = !newOffsetName[0] || nameExists || reservedName;
 | 
			
		||||
 | 
			
		||||
            if (nameExists) {
 | 
			
		||||
                ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "An offset with the given name already exists.");
 | 
			
		||||
            }
 | 
			
		||||
            else if (reservedName) {
 | 
			
		||||
                ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "The given name is reserved.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (denyApply) { style::beginDisabled(); }
 | 
			
		||||
            if (ImGui::Button("Apply")) {
 | 
			
		||||
                addOffset(newOffsetName, newOffset);
 | 
			
		||||
                open = false;
 | 
			
		||||
            }
 | 
			
		||||
            if (denyApply) { style::endDisabled(); }
 | 
			
		||||
            ImGui::SameLine();
 | 
			
		||||
            if (ImGui::Button("Cancel")) {
 | 
			
		||||
                open = false;
 | 
			
		||||
            }
 | 
			
		||||
            ImGui::EndPopup();
 | 
			
		||||
        }
 | 
			
		||||
        return open;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void draw(void* ctx) {
 | 
			
		||||
        float itemWidth = ImGui::GetContentRegionAvail().x;
 | 
			
		||||
        float lineHeight = ImGui::GetTextLineHeightWithSpacing();
 | 
			
		||||
        float spacing = lineHeight - ImGui::GetTextLineHeight();
 | 
			
		||||
        bool running = gui::mainWindow.sdrIsRunning();
 | 
			
		||||
 | 
			
		||||
        if (running) { style::beginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth);
 | 
			
		||||
        if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
 | 
			
		||||
            selectSource(sourceNames[sourceId]);
 | 
			
		||||
        if (ImGui::Combo("##source", &sourceId, sources.txt)) {
 | 
			
		||||
            std::string newSource = sources.value(sourceId);
 | 
			
		||||
            selectSource(newSource);
 | 
			
		||||
            core::configManager.acquire();
 | 
			
		||||
            core::configManager.conf["source"] = sourceNames[sourceId];
 | 
			
		||||
            core::configManager.conf["source"] = newSource;
 | 
			
		||||
            core::configManager.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -196,21 +316,45 @@ namespace sourcemenu {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ImGui::LeftLabel("Offset mode");
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) {
 | 
			
		||||
            updateOffset();
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX() - 2.0f*(lineHeight + 1.5f*spacing));
 | 
			
		||||
        if (ImGui::Combo("##_sdrpp_offset", &offsetId, offsets.txt)) {
 | 
			
		||||
            selectOffsetById(offsetId);
 | 
			
		||||
            core::configManager.acquire();
 | 
			
		||||
            core::configManager.conf["offsetMode"] = offsetMode;
 | 
			
		||||
            core::configManager.conf["selectedOffset"] = offsets.key(offsetId);
 | 
			
		||||
            core::configManager.release(true);
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
 | 
			
		||||
        if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::BeginDisabled(); }
 | 
			
		||||
        if (ImGui::Button("-##_sdrpp_offset_del_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
 | 
			
		||||
            delOffsetName = selectedOffset;
 | 
			
		||||
            showDelOffsetDialog = true;
 | 
			
		||||
        }
 | 
			
		||||
        if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::EndDisabled(); }
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
 | 
			
		||||
        if (ImGui::Button("+##_sdrpp_offset_add_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
 | 
			
		||||
            strcpy(newOffsetName, "New Offset");
 | 
			
		||||
            showAddOffsetDialog = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Offset delete confirmation
 | 
			
		||||
        if (ImGui::GenericDialog("sdrpp_del_offset_confirm", showDelOffsetDialog, GENERIC_DIALOG_BUTTONS_YES_NO, []() {
 | 
			
		||||
            ImGui::Text("Deleting offset named \"%s\". Are you sure?", delOffsetName.c_str());
 | 
			
		||||
        }) == GENERIC_DIALOG_BUTTON_YES) {
 | 
			
		||||
            delOffset(delOffsetName);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Offset add diaglog
 | 
			
		||||
        if (showAddOffsetDialog) { showAddOffsetDialog = addOffsetDialog(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::LeftLabel("Offset");
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (offsetMode == OFFSET_MODE_CUSTOM) {
 | 
			
		||||
            if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) {
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (offsetId == OFFSET_ID_MANUAL) {
 | 
			
		||||
            if (ImGui::InputDouble("##freq_offset", &manualOffset, 1.0, 100.0)) {
 | 
			
		||||
                updateOffset();
 | 
			
		||||
                core::configManager.acquire();
 | 
			
		||||
                core::configManager.conf["offset"] = customOffset;
 | 
			
		||||
                core::configManager.conf["manualOffset"] = manualOffset;
 | 
			
		||||
                core::configManager.release(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -222,11 +366,11 @@ namespace sourcemenu {
 | 
			
		||||
 | 
			
		||||
        if (running) { style::beginDisabled(); }
 | 
			
		||||
        ImGui::LeftLabel("Decimation");
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) {
 | 
			
		||||
            sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::Combo("##source_decim", &decimId, decimations.txt)) {
 | 
			
		||||
            sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
 | 
			
		||||
            core::configManager.acquire();
 | 
			
		||||
            core::configManager.conf["decimationPower"] = decimationPower;
 | 
			
		||||
            core::configManager.conf["decimation"] = decimations.key(decimId);
 | 
			
		||||
            core::configManager.release(true);
 | 
			
		||||
        }
 | 
			
		||||
        if (running) { style::endDisabled(); }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <backend.h>
 | 
			
		||||
#include <utils/hrfreq.h>
 | 
			
		||||
 | 
			
		||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
 | 
			
		||||
#define IMGUI_DEFINE_MATH_OPERATORS
 | 
			
		||||
@@ -90,6 +91,7 @@ void FrequencySelect::moveCursorToDigit(int i) {
 | 
			
		||||
 | 
			
		||||
void FrequencySelect::draw() {
 | 
			
		||||
    auto window = ImGui::GetCurrentWindow();
 | 
			
		||||
    auto io = ImGui::GetIO();
 | 
			
		||||
    widgetPos = ImGui::GetWindowContentRegionMin();
 | 
			
		||||
    ImVec2 cursorPos = ImGui::GetCursorPos();
 | 
			
		||||
    widgetPos.x += window->Pos.x + cursorPos.x;
 | 
			
		||||
@@ -132,7 +134,7 @@ void FrequencySelect::draw() {
 | 
			
		||||
        ImVec2 mousePos = ImGui::GetMousePos();
 | 
			
		||||
        bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
 | 
			
		||||
        bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
 | 
			
		||||
        int mw = ImGui::GetIO().MouseWheel;
 | 
			
		||||
        int mw = io.MouseWheel;
 | 
			
		||||
        bool onDigit = false;
 | 
			
		||||
        bool hovered = false;
 | 
			
		||||
 | 
			
		||||
@@ -174,7 +176,7 @@ void FrequencySelect::draw() {
 | 
			
		||||
                    moveCursorToDigit(i + 1);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                auto chars = ImGui::GetIO().InputQueueCharacters;
 | 
			
		||||
                auto chars = io.InputQueueCharacters;
 | 
			
		||||
 | 
			
		||||
                // For each keyboard characters, type it
 | 
			
		||||
                for (int j = 0; j < chars.Size; j++) {
 | 
			
		||||
@@ -194,6 +196,34 @@ void FrequencySelect::draw() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        digitHovered = hovered;
 | 
			
		||||
 | 
			
		||||
        if (isInArea(mousePos, digitTopMins[0], digitBottomMaxs[11])) {
 | 
			
		||||
            bool shortcutKey = io.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
 | 
			
		||||
            bool ctrlOnly = (io.KeyMods == ImGuiKeyModFlags_Ctrl);
 | 
			
		||||
            bool shiftOnly = (io.KeyMods == ImGuiKeyModFlags_Shift);
 | 
			
		||||
            bool copy  = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_C)) || (ctrlOnly  && ImGui::IsKeyPressed(ImGuiKey_Insert)));
 | 
			
		||||
            bool paste = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_V)) || (shiftOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
 | 
			
		||||
            if (copy) {
 | 
			
		||||
                // Convert the freqency to a string
 | 
			
		||||
                std::string freqStr = hrfreq::toString(frequency);
 | 
			
		||||
 | 
			
		||||
                // Write it to the clipboard
 | 
			
		||||
                ImGui::SetClipboardText(freqStr.c_str());
 | 
			
		||||
            }
 | 
			
		||||
            if (paste) {
 | 
			
		||||
                // Attempt to parse the clipboard as a number
 | 
			
		||||
                const char* clip = ImGui::GetClipboardText();
 | 
			
		||||
 | 
			
		||||
                // If the clipboard is not empty, attempt to parse it
 | 
			
		||||
                if (clip) {
 | 
			
		||||
                    double newFreq;
 | 
			
		||||
                    if (hrfreq::fromString(clip, newFreq)) {
 | 
			
		||||
                        setFrequency(abs(newFreq));
 | 
			
		||||
                        frequencyChanged = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint64_t freq = 0;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								core/src/utils/hrfreq.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								core/src/utils/hrfreq.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
#include "hrfreq.h"
 | 
			
		||||
#include <utils/flog.h>
 | 
			
		||||
 | 
			
		||||
namespace hrfreq {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    std::string toString(double freq) {
 | 
			
		||||
        // Determine the scale
 | 
			
		||||
        int maxDecimals = 0;
 | 
			
		||||
        const char* suffix = "Hz";
 | 
			
		||||
        if (freq >= 1e9) {
 | 
			
		||||
            freq /= 1e9;
 | 
			
		||||
            maxDecimals = 9;
 | 
			
		||||
            suffix = "GHz";
 | 
			
		||||
        }
 | 
			
		||||
        else if (freq >= 1e6) {
 | 
			
		||||
            freq /= 1e6;
 | 
			
		||||
            maxDecimals = 6;
 | 
			
		||||
            suffix = "MHz";
 | 
			
		||||
        }
 | 
			
		||||
        else if (freq >= 1e3) {
 | 
			
		||||
            freq /= 1e3;
 | 
			
		||||
            maxDecimals = 3;
 | 
			
		||||
            suffix = "KHz";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Convert to string (TODO: Not sure if limiting the decimals rounds)
 | 
			
		||||
        char numBuf[128];
 | 
			
		||||
        int numLen = sprintf(numBuf, "%0.*lf", maxDecimals, freq);
 | 
			
		||||
 | 
			
		||||
        // If there is a decimal point, remove the useless zeros
 | 
			
		||||
        if (maxDecimals) {
 | 
			
		||||
            for (int i = numLen-1; i >= 0; i--) {
 | 
			
		||||
                bool dot = (numBuf[i] == '.');
 | 
			
		||||
                if (numBuf[i] != '0' && !dot) { break; }
 | 
			
		||||
                numBuf[i] = 0;
 | 
			
		||||
                if (dot) { break; }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Concat the suffix
 | 
			
		||||
        char finalBuf[128];
 | 
			
		||||
        sprintf(finalBuf, "%s%s", numBuf, suffix);
 | 
			
		||||
 | 
			
		||||
        // Return the final string
 | 
			
		||||
        return finalBuf;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isNumeric(char c) {
 | 
			
		||||
        return std::isdigit(c) || c == '+' || c == '-' || c == '.' || c == ',';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool fromString(const std::string& str, double& freq) {
 | 
			
		||||
        // Skip non-numeric characters
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        char c;
 | 
			
		||||
        for (; i < str.size(); i++) {
 | 
			
		||||
            if (isNumeric(str[i])) { break; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Extract the numeric part
 | 
			
		||||
        std::string numeric;
 | 
			
		||||
        for (; i < str.size(); i++) {
 | 
			
		||||
            // Get the character
 | 
			
		||||
            c = str[i];
 | 
			
		||||
 | 
			
		||||
            // If it's a letter, stop
 | 
			
		||||
            if (std::isalpha(c)) { break; }
 | 
			
		||||
 | 
			
		||||
            // If isn't numeric, skip it
 | 
			
		||||
            if (!isNumeric(c)) { continue; }
 | 
			
		||||
 | 
			
		||||
            // If it's a comma, skip it for now. This enforces a dot as a decimal point
 | 
			
		||||
            if (c == ',') { continue; }
 | 
			
		||||
 | 
			
		||||
            // Add the character to the numeric string
 | 
			
		||||
            numeric += c;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Attempt to parse the numeric part
 | 
			
		||||
        double num;
 | 
			
		||||
        try {
 | 
			
		||||
            num = std::stod(numeric);
 | 
			
		||||
        }
 | 
			
		||||
        catch (const std::exception& e) {
 | 
			
		||||
            flog::error("Failed to parse numeric part: '{}'", numeric);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If no more text is available, assume the numeric part gives a frequency in Hz
 | 
			
		||||
        if (i == str.size()) {
 | 
			
		||||
            flog::warn("No unit given, assuming it's Hz");
 | 
			
		||||
            freq = num;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Scale the numeric value depending on the first scale character
 | 
			
		||||
        char scale = std::toupper(str[i]);
 | 
			
		||||
        switch (scale) {
 | 
			
		||||
        case 'G':
 | 
			
		||||
            num *= 1e9;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'M':
 | 
			
		||||
            num *= 1e6;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'K':
 | 
			
		||||
            num *= 1e3;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'H':
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            flog::warn("Unknown frequency scale: '{}'", scale);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Return the frequency
 | 
			
		||||
        freq = num;
 | 
			
		||||
        return true; // TODO
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								core/src/utils/hrfreq.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								core/src/utils/hrfreq.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace hrfreq {
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert a frequency to a human-readable string.
 | 
			
		||||
     * @param freq Frequency in Hz.
 | 
			
		||||
     * @return Human-readable representation of the frequency.
 | 
			
		||||
    */
 | 
			
		||||
    std::string toString(double freq);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert a human-readable representation of a frequency to a frequency value.
 | 
			
		||||
     * @param str String containing the human-readable frequency.
 | 
			
		||||
     * @param freq Value to write the decoded frequency to.
 | 
			
		||||
     * @return True on success, false otherwise.
 | 
			
		||||
    */
 | 
			
		||||
    bool fromString(const std::string& str, double& freq);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define VERSION_STR "1.2.0"
 | 
			
		||||
#define VERSION_STR "1.2.1"
 | 
			
		||||
@@ -35,10 +35,11 @@ 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/file_source/fobossdr_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
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/network_source/network_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,8 @@ cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windo
 | 
			
		||||
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/network_source/Release/network_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								readme.md
									
									
									
									
									
								
							@@ -324,12 +324,13 @@ 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      | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| fobossdr_source      | Working    | libfobos          | OPT_BUILD_FOBOSSDR_SOURCE      | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| hackrf_source        | Working    | libhackrf         | OPT_BUILD_HACKRF_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       | ✅              | ✅                     | ⛔                         |
 | 
			
		||||
| network_source       | Beta       | -                 | OPT_BUILD_NETWORK_SOURCE       | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| perseus_source       | Beta       | libperseus-sdr    | OPT_BUILD_PERSEUS_SOURCE       | ⛔              | ✅                     | ✅                         |
 | 
			
		||||
| plutosdr_source      | Working    | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE      | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| rfnm_source          | Beta       | librfnm           | OPT_BUILD_RFNM_SOURCE          | ⛔              | ✅                     | ✅                         |
 | 
			
		||||
@@ -351,8 +352,8 @@ Modules in beta are still included in releases for the most part but not enabled
 | 
			
		||||
| android_audio_sink | Working    | -            | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔              | ✅              | ✅ (Android only)          |
 | 
			
		||||
| audio_sink         | Working    | rtaudio      | OPT_BUILD_AUDIO_SINK         | ✅              | ✅              | ✅                         |
 | 
			
		||||
| network_sink       | Working    | -            | OPT_BUILD_NETWORK_SINK       | ✅              | ✅              | ✅                         |
 | 
			
		||||
| new_portaudio_sink | Beta       | portaudio    | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
| portaudio_sink     | Beta       | portaudio    | OPT_BUILD_PORTAUDIO_SINK     | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
| new_portaudio_sink | Working    | portaudio    | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
| portaudio_sink     | Working    | portaudio    | OPT_BUILD_PORTAUDIO_SINK     | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
 | 
			
		||||
## Decoders
 | 
			
		||||
 | 
			
		||||
@@ -418,8 +419,8 @@ If you still have an issue, please open an issue about it or ask on the discord.
 | 
			
		||||
 | 
			
		||||
# Contributing
 | 
			
		||||
 | 
			
		||||
Feel free to submit pull requests and report bugs via the GitHub issue tracker.
 | 
			
		||||
I will soon publish a contributing.md listing the code style to use.
 | 
			
		||||
Feel free to submit band plans via the GitHub issue tracker.
 | 
			
		||||
For code changes, please create a feature request instead.
 | 
			
		||||
 | 
			
		||||
# Credits
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										117
									
								
								root/res/bandplans/ireland.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								root/res/bandplans/ireland.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "Ireland",
 | 
			
		||||
    "country_name": "Republic Of Ireland",
 | 
			
		||||
    "country_code": "IE",
 | 
			
		||||
    "author_name": "Oskar Dudek",
 | 
			
		||||
    "author_url": "",
 | 
			
		||||
    "bands": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "2200m Ham Band",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 135700,
 | 
			
		||||
            "end": 137800
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Long wave",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 148500,
 | 
			
		||||
            "end": 282500
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "AM broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 531000,
 | 
			
		||||
            "end": 1602000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "120m SW broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 2300000,
 | 
			
		||||
            "end": 2495000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "90m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 3200000,
 | 
			
		||||
            "end": 3400000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "75m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 3900000,
 | 
			
		||||
            "end": 4000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "60m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 4750000,
 | 
			
		||||
            "end": 5060000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "49m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 5900000,
 | 
			
		||||
            "end": 6200000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "40m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 7200000,
 | 
			
		||||
            "end": 7450000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "31m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 9400000,
 | 
			
		||||
            "end": 9900000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "25m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 11600000,
 | 
			
		||||
            "end": 12100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "22m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 13570000,
 | 
			
		||||
            "end": 13870000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "19m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 15100000,
 | 
			
		||||
            "end": 15800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "16m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 17480000,
 | 
			
		||||
            "end": 17900000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "15m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 18900000,
 | 
			
		||||
            "end": 19020000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "13m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 21450000,
 | 
			
		||||
            "end": 21850000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "11m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 25670000,
 | 
			
		||||
            "end": 26100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "FM Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 87500000,
 | 
			
		||||
            "end": 108000000
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										549
									
								
								root/res/bandplans/republic-of-korea.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										549
									
								
								root/res/bandplans/republic-of-korea.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,549 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "Republic of Korea",
 | 
			
		||||
    "country_name": "Republic of Korea",
 | 
			
		||||
    "country_code": "KR",
 | 
			
		||||
    "author_name": "SeoyeonBae",
 | 
			
		||||
    "author_url": "https://github.com/bsy0317",
 | 
			
		||||
    "bands": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radio Navigation",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 8300,
 | 
			
		||||
            "end": 14000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Coastal Telegraph",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 14000,
 | 
			
		||||
            "end": 19950
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 19950,
 | 
			
		||||
            "end": 20250
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Coastal Telegraph",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 20250,
 | 
			
		||||
            "end": 70000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radio Navigation",
 | 
			
		||||
            "type": "navigation",
 | 
			
		||||
            "start": 70000,
 | 
			
		||||
            "end": 160000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Radio Navigation",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 160000,
 | 
			
		||||
            "end": 285000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Maritime Radiobeacon",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 285000,
 | 
			
		||||
            "end": 325000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Radio Navigation",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 325000,
 | 
			
		||||
            "end": 472000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 472000,
 | 
			
		||||
            "end": 479000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "International Distress Safety Call",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 479000,
 | 
			
		||||
            "end": 505000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Maritime Telegraph",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 505000,
 | 
			
		||||
            "end": 526500
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 526500,
 | 
			
		||||
            "end": 1606500
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radiobuoy",
 | 
			
		||||
            "type": "navigation",
 | 
			
		||||
            "start": 1606500,
 | 
			
		||||
            "end": 1800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 1800000,
 | 
			
		||||
            "end": 1825000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radiobuoy Control LORAN",
 | 
			
		||||
            "type": "radiolocation",
 | 
			
		||||
            "start": 1825000,
 | 
			
		||||
            "end": 2000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radiobuoy",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 2000000,
 | 
			
		||||
            "end": 2065000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Distress Call",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 2065000,
 | 
			
		||||
            "end": 2107000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "International Distress Search and Rescue",
 | 
			
		||||
            "type": "mobile",
 | 
			
		||||
            "start": 2173500,
 | 
			
		||||
            "end": 2190500
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Road Management",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 2194000,
 | 
			
		||||
            "end": 2495000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 2495000,
 | 
			
		||||
            "end": 2505000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Telephone",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 2505000,
 | 
			
		||||
            "end": 2850000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile R",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 2850000,
 | 
			
		||||
            "end": 3025000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile OR",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 3025000,
 | 
			
		||||
            "end": 3155000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile R",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 3400000,
 | 
			
		||||
            "end": 3500000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 3500000,
 | 
			
		||||
            "end": 3550000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Experimental Station",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 3550000,
 | 
			
		||||
            "end": 3790000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 3790000,
 | 
			
		||||
            "end": 3800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 3900000,
 | 
			
		||||
            "end": 3950000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 3995000,
 | 
			
		||||
            "end": 4005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Telephone",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4005000,
 | 
			
		||||
            "end": 4063000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Oceanographic Data",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4063000,
 | 
			
		||||
            "end": 4065000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Duplex Telephone",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4065000,
 | 
			
		||||
            "end": 4146000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Simplex Telephone",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4146000,
 | 
			
		||||
            "end": 4152000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Wideband Telegraph Fax",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4152000,
 | 
			
		||||
            "end": 4172000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Narrowband",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4172000,
 | 
			
		||||
            "end": 4181750
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station A1A Morse Code Communication",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4186750,
 | 
			
		||||
            "end": 4202250
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radiolocation",
 | 
			
		||||
            "type": "radiolocation",
 | 
			
		||||
            "start": 4438000,
 | 
			
		||||
            "end": 4488000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Calling Response",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 4488000,
 | 
			
		||||
            "end": 4650000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile R",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 4650000,
 | 
			
		||||
            "end": 4850000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 4995000,
 | 
			
		||||
            "end": 5005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Search Rescue",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 5480000,
 | 
			
		||||
            "end": 5730000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 5900000,
 | 
			
		||||
            "end": 5950000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 5950000,
 | 
			
		||||
            "end": 6200000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 7000000,
 | 
			
		||||
            "end": 7100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 7100000,
 | 
			
		||||
            "end": 7200000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 7200000,
 | 
			
		||||
            "end": 7450000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 7995000,
 | 
			
		||||
            "end": 8005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 9400000,
 | 
			
		||||
            "end": 9500000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 9500000,
 | 
			
		||||
            "end": 9900000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 9995000,
 | 
			
		||||
            "end": 10005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 10100000,
 | 
			
		||||
            "end": 10150000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 10150000,
 | 
			
		||||
            "end": 11600000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 11600000,
 | 
			
		||||
            "end": 11650000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 11650000,
 | 
			
		||||
            "end": 12050000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 12050000,
 | 
			
		||||
            "end": 12100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 13260000,
 | 
			
		||||
            "end": 13360000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radio Astronomy",
 | 
			
		||||
            "type": "astronomy",
 | 
			
		||||
            "start": 13360000,
 | 
			
		||||
            "end": 13410000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 13570000,
 | 
			
		||||
            "end": 13600000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 13600000,
 | 
			
		||||
            "end": 13800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 13800000,
 | 
			
		||||
            "end": 13870000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 14000000,
 | 
			
		||||
            "end": 14350000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 15010000,
 | 
			
		||||
            "end": 15100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 15100000,
 | 
			
		||||
            "end": 15600000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 15600000,
 | 
			
		||||
            "end": 15800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 15800000,
 | 
			
		||||
            "end": 15995000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 15995000,
 | 
			
		||||
            "end": 16005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 18900000,
 | 
			
		||||
            "end": 19020000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 21000000,
 | 
			
		||||
            "end": 21450000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 21450000,
 | 
			
		||||
            "end": 21850000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 24890000,
 | 
			
		||||
            "end": 24990000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 25670000,
 | 
			
		||||
            "end": 26100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 28000000,
 | 
			
		||||
            "end": 29700000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 50000000,
 | 
			
		||||
            "end": 54000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "TV Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 54000000,
 | 
			
		||||
            "end": 72000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Flood Warning",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 72000000,
 | 
			
		||||
            "end": 74800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "TV Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 76000000,
 | 
			
		||||
            "end": 88000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "FM Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 88000000,
 | 
			
		||||
            "end": 100000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "FM Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 100000000,
 | 
			
		||||
            "end": 108000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "ILS Localizer VOR",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 108000000,
 | 
			
		||||
            "end": 117975000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 144000000,
 | 
			
		||||
            "end": 146000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "General Communication",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 146000000,
 | 
			
		||||
            "end": 148000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Low Power Device",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 162037500,
 | 
			
		||||
            "end": 174000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "TV Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 174000000,
 | 
			
		||||
            "end": 216000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Low Power Device",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 216000000,
 | 
			
		||||
            "end": 230000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Low Power Device",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 273000000,
 | 
			
		||||
            "end": 322000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Personal Radio",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 420000000,
 | 
			
		||||
            "end": 470000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Public Network",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 698000000,
 | 
			
		||||
            "end": 806000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Low Power Device",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 942000000,
 | 
			
		||||
            "end": 960000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Satellite Mobile Communication",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 15250000000,
 | 
			
		||||
            "end": 16605000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Mobile Communication",
 | 
			
		||||
            "type": "mobile",
 | 
			
		||||
            "start": 25000000000,
 | 
			
		||||
            "end": 37000000000
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
#include <libbladeRF.h>
 | 
			
		||||
#include <gui/smgui.h>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <utils/optionlist.h>
 | 
			
		||||
 | 
			
		||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
 | 
			
		||||
 | 
			
		||||
@@ -37,6 +38,10 @@ public:
 | 
			
		||||
    BladeRFSourceModule(std::string name) {
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        // Define clocks
 | 
			
		||||
        clocks.define("onboard", "On-Board", CLOCK_SELECT_ONBOARD);
 | 
			
		||||
        clocks.define("external", "External", CLOCK_SELECT_EXTERNAL);
 | 
			
		||||
 | 
			
		||||
        sampleRate = 1000000.0;
 | 
			
		||||
 | 
			
		||||
        handler.ctx = this;
 | 
			
		||||
@@ -267,6 +272,15 @@ public:
 | 
			
		||||
        }
 | 
			
		||||
        config.release(true);
 | 
			
		||||
 | 
			
		||||
        // Load clock source
 | 
			
		||||
        clkId = clocks.keyId("onboard");
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("clock")) {
 | 
			
		||||
            std::string clkStr = config.conf["devices"][selectedSerial]["clock"];
 | 
			
		||||
            if (clocks.keyExists(clkStr)) {
 | 
			
		||||
                clkId = clocks.keyId(clkStr);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Load gain mode
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("gainMode")) {
 | 
			
		||||
            std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
 | 
			
		||||
@@ -364,6 +378,7 @@ private:
 | 
			
		||||
        if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
 | 
			
		||||
 | 
			
		||||
        // Setup device parameters
 | 
			
		||||
        _this->setClockSource(_this->clocks[_this->clkId]);
 | 
			
		||||
        bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL);
 | 
			
		||||
        bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
 | 
			
		||||
        bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
 | 
			
		||||
@@ -486,6 +501,19 @@ private:
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("Clock Source");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Combo(CONCAT("##_balderf_clk_sel_", _this->name), &_this->clkId, _this->clocks.txt)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                _this->setClockSource(_this->clocks[_this->clkId]);
 | 
			
		||||
            }
 | 
			
		||||
            if (_this->selectedSerial != "") {
 | 
			
		||||
                config.acquire();
 | 
			
		||||
                config.conf["devices"][_this->selectedSerial]["clock"] = _this->clocks.key(_this->clkId);
 | 
			
		||||
                config.release(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // General config BS
 | 
			
		||||
        SmGui::LeftLabel("Gain control mode");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
@@ -537,6 +565,15 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setClockSource(bladerf_clock_select clk) {
 | 
			
		||||
        if (selectedBladeType == BLADERF_TYPE_V1) {
 | 
			
		||||
            bladerf_set_smb_mode(openDev, (clk == CLOCK_SELECT_EXTERNAL) ? BLADERF_SMB_MODE_INPUT : BLADERF_SMB_MODE_DISABLED);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            bladerf_set_clock_select(openDev, clk);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void worker() {
 | 
			
		||||
        int16_t* buffer = new int16_t[bufferSize * 2];
 | 
			
		||||
        bladerf_metadata meta;
 | 
			
		||||
@@ -565,6 +602,7 @@ private:
 | 
			
		||||
    int devId = 0;
 | 
			
		||||
    int srId = 0;
 | 
			
		||||
    int bwId = 0;
 | 
			
		||||
    int clkId = 0;
 | 
			
		||||
    int chanId = 0;
 | 
			
		||||
    int gainMode = 0;
 | 
			
		||||
    bool streamingEnabled = false;
 | 
			
		||||
@@ -580,8 +618,8 @@ private:
 | 
			
		||||
    std::string sampleRatesTxt;
 | 
			
		||||
    std::vector<uint64_t> bandwidths;
 | 
			
		||||
    std::string bandwidthsTxt;
 | 
			
		||||
 | 
			
		||||
    std::string channelNamesTxt;
 | 
			
		||||
    OptionList<std::string, bladerf_clock_select> clocks;
 | 
			
		||||
 | 
			
		||||
    int bufferSize;
 | 
			
		||||
    struct bladerf_stream* rxStream;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								source_modules/kcsdr_source/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								source_modules/kcsdr_source/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
vendor/*
 | 
			
		||||
							
								
								
									
										10
									
								
								source_modules/kcsdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								source_modules/kcsdr_source/CMakeLists.txt
									
									
									
									
									
										Normal 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)
 | 
			
		||||
							
								
								
									
										209
									
								
								source_modules/kcsdr_source/src/kcsdr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								source_modules/kcsdr_source/src/kcsdr.c
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								source_modules/kcsdr_source/src/kcsdr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								source_modules/kcsdr_source/src/kcsdr.h
									
									
									
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										324
									
								
								source_modules/kcsdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								source_modules/kcsdr_source/src/main.cpp
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
@@ -36,10 +36,10 @@ enum SampleType {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const size_t SAMPLE_TYPE_SIZE[] {
 | 
			
		||||
    sizeof(int8_t)*2,
 | 
			
		||||
    sizeof(int16_t)*2,
 | 
			
		||||
    sizeof(int32_t)*2,
 | 
			
		||||
    sizeof(float)*2,
 | 
			
		||||
    2*sizeof(int8_t),
 | 
			
		||||
    2*sizeof(int16_t),
 | 
			
		||||
    2*sizeof(int32_t),
 | 
			
		||||
    2*sizeof(float),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class NetworkSourceModule : public ModuleManager::Instance {
 | 
			
		||||
@@ -58,20 +58,6 @@ public:
 | 
			
		||||
        handler.tuneHandler = tune;
 | 
			
		||||
        handler.stream = &stream;
 | 
			
		||||
 | 
			
		||||
        // Define samplerates
 | 
			
		||||
        for (int i = 3000; i <= 192000; i <<= 1) {
 | 
			
		||||
            samplerates.define(i, getSrScaled(i), i);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 250000; i < 1000000; i += 250000) {
 | 
			
		||||
            samplerates.define(i, getSrScaled(i), i);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 1000000; i < 10000000; i += 500000) {
 | 
			
		||||
            samplerates.define(i, getSrScaled(i), i);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 10000000; i <= 100000000; i += 5000000) {
 | 
			
		||||
            samplerates.define(i, getSrScaled(i), i);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Define protocols
 | 
			
		||||
        // protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
 | 
			
		||||
        protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
 | 
			
		||||
@@ -86,8 +72,8 @@ public:
 | 
			
		||||
        // Load config
 | 
			
		||||
        config.acquire();
 | 
			
		||||
        if (config.conf[name].contains("samplerate")) {
 | 
			
		||||
            int sr = config.conf[name]["samplerate"];
 | 
			
		||||
            if (samplerates.keyExists(sr)) { samplerate = samplerates.value(samplerates.keyId(sr)); }
 | 
			
		||||
            samplerate = config.conf[name]["samplerate"];
 | 
			
		||||
            tempSamplerate = samplerate;
 | 
			
		||||
        }
 | 
			
		||||
        if (config.conf[name].contains("protocol")) {
 | 
			
		||||
            std::string protoStr = config.conf[name]["protocol"];
 | 
			
		||||
@@ -108,7 +94,6 @@ public:
 | 
			
		||||
        config.release();
 | 
			
		||||
 | 
			
		||||
        // Set menu IDs
 | 
			
		||||
        srId = samplerates.valueId(samplerate);
 | 
			
		||||
        protoId = protocols.valueId(proto);
 | 
			
		||||
        sampTypeId = sampleTypes.valueId(sampType);
 | 
			
		||||
 | 
			
		||||
@@ -228,35 +213,24 @@ private:
 | 
			
		||||
        if (_this->running) { SmGui::BeginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        // Hostname and port field
 | 
			
		||||
        if (ImGui::InputText(("##iq_exporter_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
 | 
			
		||||
        if (SmGui::InputText(("##network_source_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["host"] = _this->hostname;
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::InputInt(("##iq_exporter_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
 | 
			
		||||
        SmGui::SameLine();
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::InputInt(("##network_source_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
 | 
			
		||||
            _this->port = std::clamp<int>(_this->port, 1, 65535);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["port"] = _this->port;
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Samplerate selector
 | 
			
		||||
        ImGui::LeftLabel("Samplerate");
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::Combo(("##iq_exporter_sr_" + _this->name).c_str(), &_this->srId, _this->samplerates.txt)) {
 | 
			
		||||
            _this->samplerate = _this->samplerates.value(_this->srId);
 | 
			
		||||
            core::setInputSampleRate(_this->samplerate);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["samplerate"] = _this->samplerates.key(_this->srId);
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Mode protocol selector
 | 
			
		||||
        ImGui::LeftLabel("Protocol");
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::Combo(("##iq_exporter_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
 | 
			
		||||
        SmGui::LeftLabel("Protocol");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Combo(("##network_source_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
 | 
			
		||||
            _this->proto = _this->protocols.value(_this->protoId);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
 | 
			
		||||
@@ -264,15 +238,38 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sample type selector
 | 
			
		||||
        ImGui::LeftLabel("Sample type");
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::Combo(("##iq_exporter_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
 | 
			
		||||
        SmGui::LeftLabel("Sample type");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Combo(("##network_source_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
 | 
			
		||||
            _this->sampType = _this->sampleTypes.value(_this->sampTypeId);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Samplerate selector
 | 
			
		||||
        SmGui::LeftLabel("Samplerate");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::InputInt(("##network_source_sr_" + _this->name).c_str(), &_this->tempSamplerate)) {
 | 
			
		||||
            // Prevent silly values from silly users
 | 
			
		||||
            _this->tempSamplerate = std::max<int>(_this->tempSamplerate, 1000);
 | 
			
		||||
        }
 | 
			
		||||
        bool applyEn = (!_this->running && _this->tempSamplerate != _this->samplerate);
 | 
			
		||||
        if (!applyEn) { SmGui::BeginDisabled(); }
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Button(("Apply##network_source_apply_" + _this->name).c_str())) {
 | 
			
		||||
            _this->samplerate = _this->tempSamplerate;
 | 
			
		||||
            core::setInputSampleRate(_this->samplerate);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["samplerate"] = _this->samplerate;
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
        if (!applyEn) { SmGui::EndDisabled(); }
 | 
			
		||||
 | 
			
		||||
        if (_this->tempSamplerate != _this->samplerate) {
 | 
			
		||||
            SmGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Warning: Samplerate not applied yet");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_this->running) { SmGui::EndDisabled(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -280,14 +277,17 @@ private:
 | 
			
		||||
        // Compute sizes
 | 
			
		||||
        int blockSize = samplerate / 200;
 | 
			
		||||
        int sampleSize = SAMPLE_TYPE_SIZE[sampType];
 | 
			
		||||
        int frameSize = blockSize*sampleSize;
 | 
			
		||||
 | 
			
		||||
        // Chose amount of bytes to attempt to read
 | 
			
		||||
        bool forceSize = (proto != PROTOCOL_UDP);
 | 
			
		||||
        int frameSize = sampleSize * (forceSize ? blockSize : STREAM_BUFFER_SIZE);
 | 
			
		||||
 | 
			
		||||
        // Allocate receive buffer
 | 
			
		||||
        uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
 | 
			
		||||
 | 
			
		||||
        while (true) {
 | 
			
		||||
            // Read samples from socket
 | 
			
		||||
            int bytes = sock->recv(buffer, frameSize, true);
 | 
			
		||||
            int bytes = sock->recv(buffer, frameSize, forceSize);
 | 
			
		||||
            if (bytes <= 0) { break; }
 | 
			
		||||
 | 
			
		||||
            // Convert to CF32 (note: problem if partial sample)
 | 
			
		||||
@@ -325,7 +325,7 @@ private:
 | 
			
		||||
    double freq;
 | 
			
		||||
    
 | 
			
		||||
    int samplerate = 1000000;
 | 
			
		||||
    int srId;
 | 
			
		||||
    int tempSamplerate = 1000000;
 | 
			
		||||
    Protocol proto = PROTOCOL_UDP;
 | 
			
		||||
    int protoId;
 | 
			
		||||
    SampleType sampType = SAMPLE_TYPE_INT16;
 | 
			
		||||
@@ -333,7 +333,6 @@ private:
 | 
			
		||||
    char hostname[1024] = "localhost";
 | 
			
		||||
    int port = 1234;
 | 
			
		||||
 | 
			
		||||
    OptionList<int, int> samplerates;
 | 
			
		||||
    OptionList<std::string, Protocol> protocols;
 | 
			
		||||
    OptionList<std::string, SampleType> sampleTypes;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user