mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 02:39:11 +01:00 
			
		
		
		
	enabled scanner
This commit is contained in:
		@@ -60,7 +60,7 @@ option(OPT_BUILD_DISCORD_PRESENCE "Build the Discord Rich Presence module" ON)
 | 
			
		||||
option(OPT_BUILD_FREQUENCY_MANAGER "Build the Frequency Manager module" ON)
 | 
			
		||||
option(OPT_BUILD_RECORDER "Audio and baseband recorder" ON)
 | 
			
		||||
option(OPT_BUILD_RIGCTL_SERVER "Rigctl backend for controlling SDR++ with software like gpredict" ON)
 | 
			
		||||
option(OPT_BUILD_SCANNER "Frequency scanner" OFF)
 | 
			
		||||
option(OPT_BUILD_SCANNER "Frequency scanner" ON)
 | 
			
		||||
option(OPT_BUILD_SCHEDULER "Build the scheduler" OFF)
 | 
			
		||||
 | 
			
		||||
# Other options
 | 
			
		||||
 
 | 
			
		||||
@@ -56,10 +56,12 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_module
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/meteor_demodulator/meteor_demodulator.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/radio/radio.dylib
 | 
			
		||||
 | 
			
		||||
# Misc modules
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/discord_integration/discord_integration.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/frequency_manager/frequency_manager.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/recorder/recorder.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/rigctl_server/rigctl_server.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/scanner/scanner.dylib
 | 
			
		||||
 | 
			
		||||
# ========================= Finalize =========================
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,7 @@ cp $build_dir/misc_modules/discord_integration/Release/discord_integration.dll s
 | 
			
		||||
cp $build_dir/misc_modules/frequency_manager/Release/frequency_manager.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp $build_dir/misc_modules/recorder/Release/recorder.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp $build_dir/misc_modules/rigctl_server/Release/rigctl_server.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp $build_dir/misc_modules/scanner/Release/scanner.dll sdrpp_windows_x64/modules/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Copy supporting libs
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ SDRPP_MOD_INFO{
 | 
			
		||||
    /* Description:     */ "Frequency scanner for SDR++",
 | 
			
		||||
    /* Author:          */ "Ryzerth",
 | 
			
		||||
    /* Version:         */ 0, 1, 0,
 | 
			
		||||
    /* Max instances    */ -1
 | 
			
		||||
    /* Max instances    */ 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ScannerModule : public ModuleManager::Instance {
 | 
			
		||||
@@ -59,12 +59,45 @@ private:
 | 
			
		||||
        if (ImGui::InputDouble("##interval_scanner", &_this->interval, 100.0, 100000.0, "%0.0f")) {
 | 
			
		||||
            _this->interval = round(_this->interval);
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::LeftLabel("Passband Ratio (%)");
 | 
			
		||||
        ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (ImGui::InputDouble("##pb_ratio_scanner", &_this->passbandRatio, 1.0, 10.0, "%0.0f")) {
 | 
			
		||||
            _this->passbandRatio = std::clamp<double>(round(_this->passbandRatio), 1.0, 100.0);
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::LeftLabel("Tuning Time (ms)");
 | 
			
		||||
        ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (ImGui::InputInt("##tuning_time_scanner", &_this->tuningTime, 100, 1000)) {
 | 
			
		||||
            _this->tuningTime = std::clamp<int>(_this->tuningTime, 100, 10000.0);
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::LeftLabel("Linger Time (ms)");
 | 
			
		||||
        ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (ImGui::InputInt("##linger_time_scanner", &_this->lingerTime, 100, 1000)) {
 | 
			
		||||
            _this->lingerTime = std::clamp<int>(_this->lingerTime, 100, 10000.0);
 | 
			
		||||
        }
 | 
			
		||||
        if (_this->running) { ImGui::EndDisabled(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::LeftLabel("Level");
 | 
			
		||||
        ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        ImGui::SliderFloat("##scanner_level", &_this->level, -150.0, 0.0);
 | 
			
		||||
 | 
			
		||||
        ImGui::BeginTable(("scanner_bottom_btn_table" + _this->name).c_str(), 2);
 | 
			
		||||
        ImGui::TableNextRow();
 | 
			
		||||
        ImGui::TableSetColumnIndex(0);
 | 
			
		||||
        if (ImGui::Button(("<<##scanner_back_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
 | 
			
		||||
            std::lock_guard<std::mutex> lck(_this->scanMtx);
 | 
			
		||||
            _this->reverseLock = true;
 | 
			
		||||
            _this->receiving = false;
 | 
			
		||||
            _this->scanUp = false;
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::TableSetColumnIndex(1);
 | 
			
		||||
        if (ImGui::Button((">>##scanner_forw_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
 | 
			
		||||
            std::lock_guard<std::mutex> lck(_this->scanMtx);
 | 
			
		||||
            _this->reverseLock = true;
 | 
			
		||||
            _this->receiving = false;
 | 
			
		||||
            _this->scanUp = true;
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::EndTable();
 | 
			
		||||
 | 
			
		||||
        if (!_this->running) {
 | 
			
		||||
            if (ImGui::Button("Start##scanner_start", ImVec2(menuWidth, 0))) {
 | 
			
		||||
                _this->start();
 | 
			
		||||
@@ -75,17 +108,6 @@ private:
 | 
			
		||||
                _this->stop();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ImGui::Button("<<##scanner_start", ImVec2(menuWidth, 0))) {
 | 
			
		||||
            std::lock_guard<std::mutex> lck(_this->scanMtx);
 | 
			
		||||
            _this->receiving = false;
 | 
			
		||||
            _this->scanUp = false;
 | 
			
		||||
        }
 | 
			
		||||
        if (ImGui::Button(">>##scanner_start", ImVec2(menuWidth, 0))) {
 | 
			
		||||
            std::lock_guard<std::mutex> lck(_this->scanMtx);
 | 
			
		||||
            _this->receiving = false;
 | 
			
		||||
            _this->scanUp = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void start() {
 | 
			
		||||
@@ -107,18 +129,21 @@ private:
 | 
			
		||||
        // 10Hz scan loop
 | 
			
		||||
        while (running) {
 | 
			
		||||
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
			
		||||
 | 
			
		||||
            {
 | 
			
		||||
                std::lock_guard<std::mutex> lck(scanMtx);
 | 
			
		||||
                auto now = std::chrono::high_resolution_clock::now();
 | 
			
		||||
 | 
			
		||||
                // Enforce tuning
 | 
			
		||||
                tuner::normalTuning(selectedVFO, current);
 | 
			
		||||
                if (gui::waterfall.selectedVFO.empty()) {
 | 
			
		||||
                    running = false;
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                tuner::normalTuning(gui::waterfall.selectedVFO, current);
 | 
			
		||||
 | 
			
		||||
                // Check if we are waiting for a tune
 | 
			
		||||
                if (tuning) {
 | 
			
		||||
                    spdlog::warn("Tuning");
 | 
			
		||||
                    if ((std::chrono::duration_cast<std::chrono::milliseconds>(now - lastTuneTime)).count() > 250.0) {
 | 
			
		||||
                    if ((std::chrono::duration_cast<std::chrono::milliseconds>(now - lastTuneTime)).count() > tuningTime) {
 | 
			
		||||
                        tuning = false;
 | 
			
		||||
                    }
 | 
			
		||||
                    continue;
 | 
			
		||||
@@ -136,7 +161,7 @@ private:
 | 
			
		||||
                double wfEnd = wfCenter + (wfWidth / 2.0);
 | 
			
		||||
 | 
			
		||||
                // Gather VFO data
 | 
			
		||||
                double vfoWidth = sigpath::vfoManager.getBandwidth(selectedVFO);
 | 
			
		||||
                double vfoWidth = sigpath::vfoManager.getBandwidth(gui::waterfall.selectedVFO);
 | 
			
		||||
 | 
			
		||||
                if (receiving) {
 | 
			
		||||
                    spdlog::warn("Receiving");
 | 
			
		||||
@@ -145,26 +170,30 @@ private:
 | 
			
		||||
                    if (maxLevel >= level) {
 | 
			
		||||
                        lastSignalTime = now;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if ((std::chrono::duration_cast<std::chrono::milliseconds>(now - lastSignalTime)).count() > 1000.0) {
 | 
			
		||||
                    else if ((std::chrono::duration_cast<std::chrono::milliseconds>(now - lastSignalTime)).count() > lingerTime) {
 | 
			
		||||
                        receiving = false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    spdlog::warn("Seeking signal");
 | 
			
		||||
                    double bottomLimit = INFINITY;
 | 
			
		||||
                    double topLimit = -INFINITY;
 | 
			
		||||
                    double bottomLimit = current;
 | 
			
		||||
                    double topLimit = current;
 | 
			
		||||
                    
 | 
			
		||||
                    // Search for a signal in scan direction
 | 
			
		||||
                    if (tuneIfAvailable(scanUp, bottomLimit, topLimit, wfStart, wfEnd, wfWidth, vfoWidth, data, dataWidth)) {
 | 
			
		||||
                    if (findSignal(scanUp, bottomLimit, topLimit, wfStart, wfEnd, wfWidth, vfoWidth, data, dataWidth)) {
 | 
			
		||||
                        gui::waterfall.releaseLatestFFT();
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    // Search for signal in the inverse scan direction
 | 
			
		||||
                    if (tuneIfAvailable(!scanUp, bottomLimit, topLimit, wfStart, wfEnd, wfWidth, vfoWidth, data, dataWidth)) {
 | 
			
		||||
                        gui::waterfall.releaseLatestFFT();
 | 
			
		||||
                        continue;
 | 
			
		||||
                    // Search for signal in the inverse scan direction if direction isn't enforced
 | 
			
		||||
                    if (!reverseLock) {
 | 
			
		||||
                        if (findSignal(!scanUp, bottomLimit, topLimit, wfStart, wfEnd, wfWidth, vfoWidth, data, dataWidth)) {
 | 
			
		||||
                            gui::waterfall.releaseLatestFFT();
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else { reverseLock = false; }
 | 
			
		||||
                    
 | 
			
		||||
 | 
			
		||||
                    // There is no signal on the visible spectrum, tune in scan direction and retry
 | 
			
		||||
                    if (scanUp) {
 | 
			
		||||
@@ -172,7 +201,7 @@ private:
 | 
			
		||||
                        if (current > stopFreq) { current = startFreq; }
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        current = topLimit - interval;
 | 
			
		||||
                        current = bottomLimit - interval;
 | 
			
		||||
                        if (current < startFreq) { current = stopFreq; }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@@ -189,7 +218,7 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool tuneIfAvailable(bool scanDir, double& bottomLimit, double& topLimit, double wfStart, double wfEnd, double wfWidth, double vfoWidth, float* data, int dataWidth) {
 | 
			
		||||
    bool findSignal(bool scanDir, double& bottomLimit, double& topLimit, double wfStart, double wfEnd, double wfWidth, double vfoWidth, float* data, int dataWidth) {
 | 
			
		||||
        bool found = false;
 | 
			
		||||
        double freq = current;
 | 
			
		||||
        for (freq += scanDir ? interval : -interval;
 | 
			
		||||
@@ -204,7 +233,7 @@ private:
 | 
			
		||||
            if (freq > topLimit) { topLimit = freq; }
 | 
			
		||||
            
 | 
			
		||||
            // Check signal level
 | 
			
		||||
            float maxLevel = getMaxLevel(data, freq, vfoWidth *0.2, dataWidth, wfStart, wfWidth);
 | 
			
		||||
            float maxLevel = getMaxLevel(data, freq, vfoWidth * (passbandRatio * 0.01f), dataWidth, wfStart, wfWidth);
 | 
			
		||||
            if (maxLevel >= level) {
 | 
			
		||||
                found = true;
 | 
			
		||||
                receiving = true;
 | 
			
		||||
@@ -231,15 +260,19 @@ private:
 | 
			
		||||
    bool enabled = true;
 | 
			
		||||
    
 | 
			
		||||
    bool running = false;
 | 
			
		||||
    std::string selectedVFO = "Radio";
 | 
			
		||||
    double startFreq = 93300000.0;
 | 
			
		||||
    double stopFreq = 98700000.0;
 | 
			
		||||
    //std::string selectedVFO = "Radio";
 | 
			
		||||
    double startFreq = 88000000.0;
 | 
			
		||||
    double stopFreq = 108000000.0;
 | 
			
		||||
    double interval = 100000.0;
 | 
			
		||||
    double current = 88000000.0;
 | 
			
		||||
    double passbandRatio = 10.0;
 | 
			
		||||
    int tuningTime = 250;
 | 
			
		||||
    int lingerTime = 1000.0;
 | 
			
		||||
    float level = -50.0;
 | 
			
		||||
    bool receiving = true;
 | 
			
		||||
    bool tuning = false;
 | 
			
		||||
    bool scanUp = true;
 | 
			
		||||
    bool reverseLock = false;
 | 
			
		||||
    std::chrono::time_point<std::chrono::high_resolution_clock> lastSignalTime;
 | 
			
		||||
    std::chrono::time_point<std::chrono::high_resolution_clock> lastTuneTime;
 | 
			
		||||
    std::thread workerThread;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user