mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 00:48:11 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			new_rigctl
			...
			new_source
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | c488d72ce2 | 
| @@ -15,10 +15,6 @@ namespace sourcemenu { | ||||
|     bool iqCorrection = false; | ||||
|     bool invertIQ = false; | ||||
|  | ||||
|     EventHandler<std::string> sourceRegisteredHandler; | ||||
|     EventHandler<std::string> sourceUnregisterHandler; | ||||
|     EventHandler<std::string> sourceUnregisteredHandler; | ||||
|  | ||||
|     std::vector<std::string> sourceNames; | ||||
|     std::string sourceNamesTxt; | ||||
|     std::string selectedSource; | ||||
| @@ -99,10 +95,10 @@ namespace sourcemenu { | ||||
|         } | ||||
|         sourceId = std::distance(sourceNames.begin(), it); | ||||
|         selectedSource = sourceNames[sourceId]; | ||||
|         sigpath::sourceManager.selectSource(sourceNames[sourceId]); | ||||
|         sigpath::sourceManager.select(sourceNames[sourceId]); | ||||
|     } | ||||
|  | ||||
|     void onSourceRegistered(std::string name, void* ctx) { | ||||
|     void onSourceRegistered(std::string name) { | ||||
|         refreshSources(); | ||||
|  | ||||
|         if (selectedSource.empty()) { | ||||
| @@ -114,13 +110,13 @@ namespace sourcemenu { | ||||
|         sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource)); | ||||
|     } | ||||
|  | ||||
|     void onSourceUnregister(std::string name, void* ctx) { | ||||
|     void onSourceUnregister(std::string name) { | ||||
|         if (name != selectedSource) { return; } | ||||
|  | ||||
|         // TODO: Stop everything | ||||
|     } | ||||
|  | ||||
|     void onSourceUnregistered(std::string name, void* ctx) { | ||||
|     void onSourceUnregistered(std::string name) { | ||||
|         refreshSources(); | ||||
|  | ||||
|         if (sourceNames.empty()) { | ||||
| @@ -153,12 +149,9 @@ namespace sourcemenu { | ||||
|         selectSource(selected); | ||||
|         sigpath::iqFrontEnd.setDecimation(1 << decimationPower); | ||||
|  | ||||
|         sourceRegisteredHandler.handler = onSourceRegistered; | ||||
|         sourceUnregisterHandler.handler = onSourceUnregister; | ||||
|         sourceUnregisteredHandler.handler = onSourceUnregistered; | ||||
|         sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler); | ||||
|         sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler); | ||||
|         sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler); | ||||
|         sigpath::sourceManager.onSourceRegistered.bind(onSourceRegistered); | ||||
|         sigpath::sourceManager.onSourceUnregister.bind(onSourceUnregister); | ||||
|         sigpath::sourceManager.onSourceUnregistered.bind(onSourceUnregistered); | ||||
|  | ||||
|         core::configManager.release(); | ||||
|     } | ||||
| @@ -179,7 +172,7 @@ namespace sourcemenu { | ||||
|  | ||||
|         if (running) { style::endDisabled(); } | ||||
|  | ||||
|         sigpath::sourceManager.showSelectedMenu(); | ||||
|         sigpath::sourceManager.showMenu(); | ||||
|  | ||||
|         if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) { | ||||
|             sigpath::iqFrontEnd.setDCBlocking(iqCorrection); | ||||
|   | ||||
| @@ -146,7 +146,7 @@ namespace server { | ||||
|         // Load sourceId from config | ||||
|         sourceId = 0; | ||||
|         if (sourceList.keyExists(sourceName)) { sourceId = sourceList.keyId(sourceName); } | ||||
|         sigpath::sourceManager.selectSource(sourceList[sourceId]); | ||||
|         sigpath::sourceManager.select(sourceList[sourceId]); | ||||
|  | ||||
|         // TODO: Use command line option | ||||
|         std::string host = (std::string)core::args["addr"]; | ||||
| @@ -280,8 +280,7 @@ namespace server { | ||||
|             } | ||||
|         } | ||||
|         else if (cmd == COMMAND_START) { | ||||
|             sigpath::sourceManager.start(); | ||||
|             running = true; | ||||
|             running = sigpath::sourceManager.start(); | ||||
|         } | ||||
|         else if (cmd == COMMAND_STOP) { | ||||
|             sigpath::sourceManager.stop(); | ||||
| @@ -309,14 +308,14 @@ namespace server { | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo("##sdrpp_server_src_sel", &sourceId, sourceList.txt)) { | ||||
|             sigpath::sourceManager.selectSource(sourceList[sourceId]); | ||||
|             sigpath::sourceManager.select(sourceList[sourceId]); | ||||
|             core::configManager.acquire(); | ||||
|             core::configManager.conf["source"] = sourceList.key(sourceId); | ||||
|             core::configManager.release(true); | ||||
|         } | ||||
|         if (running) { SmGui::EndDisabled(); } | ||||
|  | ||||
|         sigpath::sourceManager.showSelectedMenu(); | ||||
|         sigpath::sourceManager.showMenu(); | ||||
|     } | ||||
|  | ||||
|     void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue) { | ||||
|   | ||||
| @@ -1,106 +1,186 @@ | ||||
| #include <server.h> | ||||
| #include <signal_path/source.h> | ||||
| #include "source.h" | ||||
| #include <utils/flog.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <core.h> | ||||
|  | ||||
| SourceManager::SourceManager() { | ||||
| } | ||||
| void SourceManager::registerSource(const std::string& name, Source* source) { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
| void SourceManager::registerSource(std::string name, SourceHandler* handler) { | ||||
|     // Check arguments | ||||
|     if (source || name.empty()) { | ||||
|         flog::error("Invalid argument to register source", name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Check that a source with that name doesn't already exist | ||||
|     if (sources.find(name) != sources.end()) { | ||||
|         flog::error("Tried to register new source with existing name: {0}", name); | ||||
|         flog::error("Tried to register source with existing name: {}", name); | ||||
|         return; | ||||
|     } | ||||
|     sources[name] = handler; | ||||
|     onSourceRegistered.emit(name); | ||||
|  | ||||
|     // Add source to map | ||||
|     sources[name] = source; | ||||
|  | ||||
|     // Add source to lists | ||||
|     sourceNames.push_back(name); | ||||
|     onSourceRegistered(name); | ||||
| } | ||||
|  | ||||
| void SourceManager::unregisterSource(std::string name) { | ||||
| void SourceManager::unregisterSource(const std::string& name) { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Check that a source with that name exists | ||||
|     if (sources.find(name) == sources.end()) { | ||||
|         flog::error("Tried to unregister non existent source: {0}", name); | ||||
|         flog::error("Tried to unregister a non-existent source: {}", name); | ||||
|         return; | ||||
|     } | ||||
|     onSourceUnregister.emit(name); | ||||
|     if (name == selectedName) { | ||||
|         if (selectedHandler != NULL) { | ||||
|             sources[selectedName]->deselectHandler(sources[selectedName]->ctx); | ||||
|         } | ||||
|         sigpath::iqFrontEnd.setInput(&nullSource); | ||||
|         selectedHandler = NULL; | ||||
|     } | ||||
|  | ||||
|     // Notify event listeners of the imminent deletion | ||||
|     onSourceUnregister(name); | ||||
|  | ||||
|     // Delete from lists | ||||
|     sourceNames.erase(std::find(sourceNames.begin(), sourceNames.end(), name)); | ||||
|     sources.erase(name); | ||||
|     onSourceUnregistered.emit(name); | ||||
|  | ||||
|     // Notify event listeners of the deletion | ||||
|     onSourceUnregistered(name); | ||||
| } | ||||
|  | ||||
| std::vector<std::string> SourceManager::getSourceNames() { | ||||
|     std::vector<std::string> names; | ||||
|     for (auto const& [name, src] : sources) { names.push_back(name); } | ||||
|     return names; | ||||
| const std::vector<std::string>& SourceManager::getSourceNames() { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|     return sourceNames; | ||||
| } | ||||
|  | ||||
| void SourceManager::selectSource(std::string name) { | ||||
| void SourceManager::select(const std::string& name) { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // make sure that source isn't currently selected | ||||
|     if (selectedSourceName == name) { return; } | ||||
|  | ||||
|     // Deselect current source | ||||
|     deselect(); | ||||
|  | ||||
|     // Check that a source with that name exists | ||||
|     if (sources.find(name) == sources.end()) { | ||||
|         flog::error("Tried to select non existent source: {0}", name); | ||||
|         flog::error("Tried to select a non-existent source: {}", name); | ||||
|         return; | ||||
|     } | ||||
|     if (selectedHandler != NULL) { | ||||
|         sources[selectedName]->deselectHandler(sources[selectedName]->ctx); | ||||
|     } | ||||
|     selectedHandler = sources[name]; | ||||
|     selectedHandler->selectHandler(selectedHandler->ctx); | ||||
|     selectedName = name; | ||||
|     if (core::args["server"].b()) { | ||||
|         server::setInput(selectedHandler->stream); | ||||
|     } | ||||
|     else { | ||||
|         sigpath::iqFrontEnd.setInput(selectedHandler->stream); | ||||
|     } | ||||
|     // Set server input here | ||||
|  | ||||
|     // Select the source | ||||
|     selectedSourceName = name; | ||||
|     selectedSource = sources[name]; | ||||
|  | ||||
|     // Call the selected source | ||||
|     selectedSource->select(); | ||||
|  | ||||
|     // Retune to make sure the source has the latest frequency | ||||
|     tune(frequency); | ||||
| } | ||||
|  | ||||
| void SourceManager::showSelectedMenu() { | ||||
|     if (selectedHandler == NULL) { | ||||
|         return; | ||||
|     } | ||||
|     selectedHandler->menuHandler(selectedHandler->ctx); | ||||
| const std::string& SourceManager::getSelected() { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|     return selectedSourceName; | ||||
| } | ||||
|  | ||||
| void SourceManager::start() { | ||||
|     if (selectedHandler == NULL) { | ||||
|         return; | ||||
|     } | ||||
|     selectedHandler->startHandler(selectedHandler->ctx); | ||||
| bool SourceManager::start() { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Check if not already running | ||||
|     if (running) { return true; } | ||||
|  | ||||
|     // Call source if selected and save if started | ||||
|     running = (!selectedSource) ? false : selectedSource->start(); | ||||
|  | ||||
|     return running; | ||||
| } | ||||
|  | ||||
| void SourceManager::stop() { | ||||
|     if (selectedHandler == NULL) { | ||||
|         return; | ||||
|     } | ||||
|     selectedHandler->stopHandler(selectedHandler->ctx); | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Check if running | ||||
|     if (!running) { return; } | ||||
|  | ||||
|     // Call source if selected and save state | ||||
|     if (selectedSource) { selectedSource->stop(); } | ||||
|     running = false; | ||||
| } | ||||
|  | ||||
| bool SourceManager::isRunning() { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|     return running; | ||||
| } | ||||
|  | ||||
| void SourceManager::tune(double freq) { | ||||
|     if (selectedHandler == NULL) { | ||||
|         return; | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Save frequency | ||||
|     frequency = freq; | ||||
|      | ||||
|     // Call source if selected | ||||
|     if (selectedSource) { | ||||
|         selectedSource->tune(((mode == TUNING_MODE_NORMAL) ? freq : ifFrequency) + offset); | ||||
|     } | ||||
|     // TODO: No need to always retune the hardware in panadpter mode | ||||
|     selectedHandler->tuneHandler(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset, selectedHandler->ctx); | ||||
|     onRetune.emit(freq); | ||||
|     currentFreq = freq; | ||||
| } | ||||
|  | ||||
| void SourceManager::showMenu() { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|      | ||||
|     // Call source if selected | ||||
|     if (selectedSource) { selectedSource->showMenu(); } | ||||
| } | ||||
|  | ||||
| double SourceManager::getSamplerate() { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|     return samplerate; | ||||
| } | ||||
|  | ||||
| // =========== TODO: These functions should not happen in this class =========== | ||||
|  | ||||
| void SourceManager::setTuningOffset(double offset) { | ||||
|     tuneOffset = offset; | ||||
|     tune(currentFreq); | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Update offset | ||||
|     this->offset = offset; | ||||
|  | ||||
|     // Retune to take affect | ||||
|     tune(frequency); | ||||
| } | ||||
|  | ||||
| void SourceManager::setTuningMode(TuningMode mode) { | ||||
|     tuneMode = mode; | ||||
|     tune(currentFreq); | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Update mode | ||||
|     this->mode = mode; | ||||
|  | ||||
|     // Retune to take affect | ||||
|     tune(frequency); | ||||
| } | ||||
|  | ||||
| void SourceManager::setPanadpterIF(double freq) { | ||||
|     ifFreq = freq; | ||||
|     tune(currentFreq); | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Update offset | ||||
|     ifFrequency = freq; | ||||
|  | ||||
|     // Return to take affect if in panadapter mode  | ||||
|     if (mode == TUNING_MODE_PANADAPTER) { tune(frequency); } | ||||
| } | ||||
|  | ||||
| // ============================================================================= | ||||
|  | ||||
| void SourceManager::deselect() { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Call source if selected | ||||
|     if (selectedSource) { selectedSource->deselect(); } | ||||
|  | ||||
|     // Mark as deselected | ||||
|     selectedSourceName.clear(); | ||||
|     selectedSource = NULL; | ||||
| } | ||||
|  | ||||
| void SourceManager::setSamplerate(double samplerate) { | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|  | ||||
|     // Save samplerate and emit event | ||||
|     this->samplerate = samplerate; | ||||
|     onSamplerateChanged(samplerate); | ||||
| } | ||||
| @@ -1,56 +1,153 @@ | ||||
| #pragma once | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <functional> | ||||
| #include <map> | ||||
| #include <dsp/stream.h> | ||||
| #include <mutex> | ||||
| #include <dsp/types.h> | ||||
| #include <dsp/stream.h> | ||||
| #include <utils/event.h> | ||||
|  | ||||
| enum TuningMode { | ||||
|     TUNING_MODE_NORMAL, | ||||
|     TUNING_MODE_PANADAPTER | ||||
| }; | ||||
|  | ||||
| class Source; | ||||
|  | ||||
| class SourceManager { | ||||
|     friend Source; | ||||
| public: | ||||
|     SourceManager(); | ||||
|     /** | ||||
|      * Register a source. | ||||
|      * @param name Name of the source. | ||||
|      * @param source Pointer to the source instance. | ||||
|      */ | ||||
|     void registerSource(const std::string& name, Source* source); | ||||
|  | ||||
|     struct SourceHandler { | ||||
|         dsp::stream<dsp::complex_t>* stream; | ||||
|         void (*menuHandler)(void* ctx); | ||||
|         void (*selectHandler)(void* ctx); | ||||
|         void (*deselectHandler)(void* ctx); | ||||
|         void (*startHandler)(void* ctx); | ||||
|         void (*stopHandler)(void* ctx); | ||||
|         void (*tuneHandler)(double freq, void* ctx); | ||||
|         void* ctx; | ||||
|     }; | ||||
|     /** | ||||
|      * Unregister a source. | ||||
|      * @param name Name of the source. | ||||
|      */ | ||||
|     void unregisterSource(const std::string& name); | ||||
|  | ||||
|     enum TuningMode { | ||||
|         NORMAL, | ||||
|         PANADAPTER | ||||
|     }; | ||||
|     /** | ||||
|      * Get a list of source names. | ||||
|      * @return List of source names. | ||||
|      */ | ||||
|     const std::vector<std::string>& getSourceNames(); | ||||
|  | ||||
|     void registerSource(std::string name, SourceHandler* handler); | ||||
|     void unregisterSource(std::string name); | ||||
|     void selectSource(std::string name); | ||||
|     void showSelectedMenu(); | ||||
|     void start(); | ||||
|     /** | ||||
|      * Select a source. | ||||
|      * @param name Name of the source. | ||||
|      */ | ||||
|     void select(const std::string& name); | ||||
|  | ||||
|     /** | ||||
|      * Get the name of the currently selected source. | ||||
|      * @return Name of the source or empty if no source is selected. | ||||
|      */ | ||||
|     const std::string& getSelected(); | ||||
|      | ||||
|     /** | ||||
|      * Start the radio. | ||||
|      * @return True if the radio started successfully, false if not. | ||||
|      */ | ||||
|     bool start(); | ||||
|  | ||||
|     /** | ||||
|      * Stop the radio. | ||||
|      */ | ||||
|     void stop(); | ||||
|  | ||||
|     /** | ||||
|      * Check if the radio is running. | ||||
|      * @return True if the radio is running, false if not. | ||||
|      */ | ||||
|     bool isRunning(); | ||||
|  | ||||
|     /** | ||||
|      * Tune the radio. | ||||
|      * @param freq Frequency in Hz. | ||||
|      */ | ||||
|     void tune(double freq); | ||||
|  | ||||
|     /** | ||||
|      * Tune the radio. | ||||
|      * @param freq Frequency to tune the radio to. | ||||
|      */ | ||||
|     void showMenu(); | ||||
|  | ||||
|     /** | ||||
|      * Get the current samplerate of the radio. | ||||
|      * @return Samplerate in Hz. | ||||
|      */ | ||||
|     double getSamplerate(); | ||||
|  | ||||
|     // =========== TODO: These functions should not happen in this class =========== | ||||
|  | ||||
|     /** | ||||
|      * Set offset to add to the tuned frequency. | ||||
|      * @param offset Offset in Hz. | ||||
|      */ | ||||
|     void setTuningOffset(double offset); | ||||
|  | ||||
|     /** | ||||
|      * Set tuning mode. | ||||
|      * @param mode Tuning mode. | ||||
|      */ | ||||
|     void setTuningMode(TuningMode mode); | ||||
|  | ||||
|     /** | ||||
|      * Set panadapter mode IF frequency. | ||||
|      * @param freq IF frequency in Hz. | ||||
|      */ | ||||
|     void setPanadpterIF(double freq); | ||||
|  | ||||
|     std::vector<std::string> getSourceNames(); | ||||
|     // ============================================================================= | ||||
|  | ||||
|     // Emitted after a new source has been registered. | ||||
|     Event<std::string> onSourceRegistered; | ||||
|  | ||||
|     // Emitted when a source is about to be unregistered. | ||||
|     Event<std::string> onSourceUnregister; | ||||
|  | ||||
|     // Emitted after a source has been unregistered. | ||||
|     Event<std::string> onSourceUnregistered; | ||||
|  | ||||
|     // Emitted when the samplerate of the incoming IQ has changed. | ||||
|     Event<double> onSamplerateChanged; | ||||
|  | ||||
|     // Emitted when the source manager is instructed to tune the radio. | ||||
|     Event<double> onRetune; | ||||
|  | ||||
| private: | ||||
|     std::map<std::string, SourceHandler*> sources; | ||||
|     std::string selectedName; | ||||
|     SourceHandler* selectedHandler = NULL; | ||||
|     double tuneOffset; | ||||
|     double currentFreq; | ||||
|     double ifFreq = 0.0; | ||||
|     TuningMode tuneMode = TuningMode::NORMAL; | ||||
|     dsp::stream<dsp::complex_t> nullSource; | ||||
|     void deselect(); | ||||
|     void setSamplerate(double samplerate); | ||||
|      | ||||
|     std::vector<std::string> sourceNames; | ||||
|     std::map<std::string, Source*> sources; | ||||
|  | ||||
|     std::string selectedSourceName = ""; | ||||
|     Source* selectedSource = NULL; | ||||
|  | ||||
|     bool running = false; | ||||
|     double samplerate = 1e6; | ||||
|     double frequency = 100e6; | ||||
|     double offset = 0; | ||||
|     double ifFrequency = 8.830e6; | ||||
|     TuningMode mode = TUNING_MODE_NORMAL; | ||||
|  | ||||
|     std::recursive_mutex mtx; | ||||
| }; | ||||
|  | ||||
| class Source { | ||||
| public: | ||||
|     virtual void showMenu() {} | ||||
|     virtual void select() = 0; | ||||
|     virtual void deselect() {} | ||||
|     virtual bool start() = 0; | ||||
|     virtual void stop() = 0; | ||||
|     virtual void tune(double freq) {} | ||||
|  | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
| }; | ||||
| @@ -1,43 +1,51 @@ | ||||
| #pragma once | ||||
| #include <vector> | ||||
| #include <utils/flog.h> | ||||
| #include <functional> | ||||
| #include <stdexcept> | ||||
| #include <mutex> | ||||
| #include <map> | ||||
|  | ||||
| template <class T> | ||||
| struct EventHandler { | ||||
|     EventHandler() {} | ||||
|     EventHandler(void (*handler)(T, void*), void* ctx) { | ||||
|         this->handler = handler; | ||||
|         this->ctx = ctx; | ||||
|     } | ||||
| typedef int HandlerID; | ||||
|  | ||||
|     void (*handler)(T, void*); | ||||
|     void* ctx; | ||||
| }; | ||||
|  | ||||
| template <class T> | ||||
| template <typename... Args> | ||||
| class Event { | ||||
|     using Handler = std::function<void(Args...)>; | ||||
| public: | ||||
|     Event() {} | ||||
|     ~Event() {} | ||||
|  | ||||
|     void emit(T value) { | ||||
|         for (auto const& handler : handlers) { | ||||
|             handler->handler(value, handler->ctx); | ||||
|         } | ||||
|     HandlerID bind(Handler handler) { | ||||
|         std::lock_guard<std::mutex> lck(mtx); | ||||
|         HandlerID id = genID(); | ||||
|         handlers[id] = handler; | ||||
|         return id; | ||||
|     } | ||||
|  | ||||
|     void bindHandler(EventHandler<T>* handler) { | ||||
|         handlers.push_back(handler); | ||||
|     template<typename MHandler, class T> | ||||
|     HandlerID bind(MHandler handler, T* ctx) { | ||||
|         return bind([=](Args... args){ | ||||
|             (ctx->*handler)(args...); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     void unbindHandler(EventHandler<T>* handler) { | ||||
|         if (std::find(handlers.begin(), handlers.end(), handler) == handlers.end()) { | ||||
|             flog::error("Tried to remove a non-existent event handler"); | ||||
|             return; | ||||
|     void unbind(HandlerID id) { | ||||
|         std::lock_guard<std::mutex> lck(mtx); | ||||
|         if (handlers.find(id) == handlers.end()) { | ||||
|             throw std::runtime_error("Could not unbind handler, unknown ID"); | ||||
|         } | ||||
|         handlers.erase(id); | ||||
|     } | ||||
|  | ||||
|     void operator()(Args... args) { | ||||
|         std::lock_guard<std::mutex> lck(mtx); | ||||
|         for (const auto& [desc, handler] : handlers) { | ||||
|             handler(args...); | ||||
|         } | ||||
|         handlers.erase(std::remove(handlers.begin(), handlers.end(), handler), handlers.end()); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::vector<EventHandler<T>*> handlers; | ||||
|     HandlerID genID() { | ||||
|         int id; | ||||
|         for (id = 1; handlers.find(id) != handlers.end(); id++); | ||||
|         return id; | ||||
|     } | ||||
|  | ||||
|     std::map<HandlerID, Handler> handlers; | ||||
|     std::mutex mtx; | ||||
| }; | ||||
| @@ -45,9 +45,6 @@ public: | ||||
|         } | ||||
|         config.release(); | ||||
|  | ||||
|         _retuneHandler.ctx = this; | ||||
|         _retuneHandler.handler = retuneHandler; | ||||
|  | ||||
|         gui::menu.registerEntry(name, menuHandler, this, NULL); | ||||
|     } | ||||
|  | ||||
| @@ -87,8 +84,8 @@ public: | ||||
|  | ||||
|         // Switch source to panadapter mode | ||||
|         sigpath::sourceManager.setPanadpterIF(ifFreq); | ||||
|         sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER); | ||||
|         sigpath::sourceManager.onRetune.bindHandler(&_retuneHandler); | ||||
|         sigpath::sourceManager.setTuningMode(TUNING_MODE_PANADAPTER); | ||||
|         retuneHandlerId = sigpath::sourceManager.onRetune.bind(retuneHandler, this); | ||||
|  | ||||
|         running = true; | ||||
|     } | ||||
| @@ -98,8 +95,8 @@ public: | ||||
|         if (!running) { return; } | ||||
|  | ||||
|         // Switch source back to normal mode | ||||
|         sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler); | ||||
|         sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL); | ||||
|         sigpath::sourceManager.onRetune.unbind(retuneHandlerId); | ||||
|         sigpath::sourceManager.setTuningMode(TUNING_MODE_NORMAL); | ||||
|  | ||||
|         // Disconnect from rigctl server | ||||
|         client->close(); | ||||
| @@ -159,10 +156,9 @@ private: | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static void retuneHandler(double freq, void* ctx) { | ||||
|         RigctlClientModule* _this = (RigctlClientModule*)ctx; | ||||
|         if (!_this->client || !_this->client->isOpen()) { return; } | ||||
|         if (_this->client->setFreq(freq)) { | ||||
|     void retuneHandler(double freq) { | ||||
|         if (!client || !client->isOpen()) { return; } | ||||
|         if (client->setFreq(freq)) { | ||||
|             flog::error("Could not set frequency"); | ||||
|         } | ||||
|     } | ||||
| @@ -178,7 +174,7 @@ private: | ||||
|  | ||||
|     double ifFreq = 8830000.0; | ||||
|  | ||||
|     EventHandler<double> _retuneHandler; | ||||
|     HandlerID retuneHandlerId; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|   | ||||
| @@ -25,7 +25,7 @@ SDRPP_MOD_INFO{ | ||||
|  | ||||
| ConfigManager config; | ||||
|  | ||||
| class AirspySourceModule : public ModuleManager::Instance { | ||||
| class AirspySourceModule : public ModuleManager::Instance, public Source { | ||||
| public: | ||||
|     AirspySourceModule(std::string name) { | ||||
|         this->name = name; | ||||
| @@ -34,15 +34,6 @@ public: | ||||
|  | ||||
|         sampleRate = 10000000.0; | ||||
|  | ||||
|         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(); | ||||
|         if (sampleRateList.size() > 0) { | ||||
|             sampleRate = sampleRateList[0]; | ||||
| @@ -54,11 +45,11 @@ public: | ||||
|         config.release(); | ||||
|         selectByString(devSerial); | ||||
|  | ||||
|         sigpath::sourceManager.registerSource("Airspy", &handler); | ||||
|         sigpath::sourceManager.registerSource("Airspy", this); | ||||
|     } | ||||
|  | ||||
|     ~AirspySourceModule() { | ||||
|         stop(this); | ||||
|         stop(); | ||||
|         sigpath::sourceManager.unregisterSource("Airspy"); | ||||
|         airspy_exit(); | ||||
|     } | ||||
| @@ -231,6 +222,315 @@ public: | ||||
|         airspy_close(dev); | ||||
|     } | ||||
|  | ||||
|         void select() { | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|         flog::info("AirspySourceModule '{0}': Select!", name); | ||||
|     } | ||||
|  | ||||
|     void deselect() { | ||||
|         flog::info("AirspySourceModule '{0}': Deselect!", name); | ||||
|     } | ||||
|  | ||||
|     bool start() { | ||||
|         if (running) { return true; } | ||||
|         if (selectedSerial == 0) { | ||||
|             flog::error("Tried to start Airspy source with null serial"); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
|         int err = airspy_open_sn(&openDev, selectedSerial); | ||||
| #else | ||||
|         int err = airspy_open_fd(&openDev, devFd); | ||||
| #endif | ||||
|         if (err != 0) { | ||||
|             char buf[1024]; | ||||
|             sprintf(buf, "%016" PRIX64, selectedSerial); | ||||
|             flog::error("Could not open Airspy {0}", buf); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         airspy_set_samplerate(openDev, sampleRateList[srId]); | ||||
|         airspy_set_freq(openDev, freq); | ||||
|  | ||||
|         if (gainMode == 0) { | ||||
|             airspy_set_lna_agc(openDev, 0); | ||||
|             airspy_set_mixer_agc(openDev, 0); | ||||
|             airspy_set_sensitivity_gain(openDev, sensitiveGain); | ||||
|         } | ||||
|         else if (gainMode == 1) { | ||||
|             airspy_set_lna_agc(openDev, 0); | ||||
|             airspy_set_mixer_agc(openDev, 0); | ||||
|             airspy_set_linearity_gain(openDev, linearGain); | ||||
|         } | ||||
|         else if (gainMode == 2) { | ||||
|             if (lnaAgc) { | ||||
|                 airspy_set_lna_agc(openDev, 1); | ||||
|             } | ||||
|             else { | ||||
|                 airspy_set_lna_agc(openDev, 0); | ||||
|                 airspy_set_lna_gain(openDev, lnaGain); | ||||
|             } | ||||
|             if (mixerAgc) { | ||||
|                 airspy_set_mixer_agc(openDev, 1); | ||||
|             } | ||||
|             else { | ||||
|                 airspy_set_mixer_agc(openDev, 0); | ||||
|                 airspy_set_mixer_gain(openDev, mixerGain); | ||||
|             } | ||||
|             airspy_set_vga_gain(openDev, vgaGain); | ||||
|         } | ||||
|  | ||||
|         airspy_set_rf_bias(openDev, biasT); | ||||
|  | ||||
|         airspy_start_rx(openDev, callback, this); | ||||
|  | ||||
|         running = true; | ||||
|         flog::info("AirspySourceModule '{0}': Start!", name); | ||||
|     } | ||||
|  | ||||
|     void stop() { | ||||
|         if (!running) { return; } | ||||
|         running = false; | ||||
|         stream.stopWriter(); | ||||
|         airspy_close(openDev); | ||||
|         stream.clearWriteStop(); | ||||
|         flog::info("AirspySourceModule '{0}': Stop!", name); | ||||
|     } | ||||
|  | ||||
|     void tune(double freq) { | ||||
|         this->freq = freq; | ||||
|         if (running) { | ||||
|             airspy_set_freq(openDev, freq); | ||||
|         } | ||||
|         flog::info("AirspySourceModule '{0}': Tune: {1}!", name, freq); | ||||
|     } | ||||
|  | ||||
|     void showMenu() { | ||||
|         if (running) { SmGui::BeginDisabled(); } | ||||
|  | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", name), &devId, devListTxt.c_str())) { | ||||
|             selectBySerial(devList[devId]); | ||||
|             core::setInputSampleRate(sampleRate); | ||||
|             if (selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["device"] = selectedSerStr; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", name), &srId, sampleRateListTxt.c_str())) { | ||||
|             sampleRate = sampleRateList[srId]; | ||||
|             core::setInputSampleRate(sampleRate); | ||||
|             if (selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][selectedSerStr]["sampleRate"] = sampleRate; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SmGui::SameLine(); | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", name))) { | ||||
|             refresh(); | ||||
|             config.acquire(); | ||||
|             std::string devSerial = config.conf["device"]; | ||||
|             config.release(); | ||||
|             selectByString(devSerial); | ||||
|             core::setInputSampleRate(sampleRate); | ||||
|         } | ||||
|  | ||||
|         if (running) { SmGui::EndDisabled(); } | ||||
|  | ||||
|         SmGui::BeginGroup(); | ||||
|         SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", name), false); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", name), gainMode == 0)) { | ||||
|             gainMode = 0; | ||||
|             if (running) { | ||||
|                 airspy_set_lna_agc(openDev, 0); | ||||
|                 airspy_set_mixer_agc(openDev, 0); | ||||
|                 airspy_set_sensitivity_gain(openDev, sensitiveGain); | ||||
|             } | ||||
|             if (selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][selectedSerStr]["gainMode"] = 0; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|         SmGui::NextColumn(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", name), gainMode == 1)) { | ||||
|             gainMode = 1; | ||||
|             if (running) { | ||||
|                 airspy_set_lna_agc(openDev, 0); | ||||
|                 airspy_set_mixer_agc(openDev, 0); | ||||
|                 airspy_set_linearity_gain(openDev, linearGain); | ||||
|             } | ||||
|             if (selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][selectedSerStr]["gainMode"] = 1; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|         SmGui::NextColumn(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", name), gainMode == 2)) { | ||||
|             gainMode = 2; | ||||
|             if (running) { | ||||
|                 if (lnaAgc) { | ||||
|                     airspy_set_lna_agc(openDev, 1); | ||||
|                 } | ||||
|                 else { | ||||
|                     airspy_set_lna_agc(openDev, 0); | ||||
|                     airspy_set_lna_gain(openDev, lnaGain); | ||||
|                 } | ||||
|                 if (mixerAgc) { | ||||
|                     airspy_set_mixer_agc(openDev, 1); | ||||
|                 } | ||||
|                 else { | ||||
|                     airspy_set_mixer_agc(openDev, 0); | ||||
|                     airspy_set_mixer_gain(openDev, mixerGain); | ||||
|                 } | ||||
|                 airspy_set_vga_gain(openDev, vgaGain); | ||||
|             } | ||||
|             if (selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][selectedSerStr]["gainMode"] = 2; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|         SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", name), false); | ||||
|         SmGui::EndGroup(); | ||||
|  | ||||
|         // Gain menus | ||||
|  | ||||
|         if (gainMode == 0) { | ||||
|             SmGui::LeftLabel("Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", name), &sensitiveGain, 0, 21)) { | ||||
|                 if (running) { | ||||
|                     airspy_set_sensitivity_gain(openDev, sensitiveGain); | ||||
|                 } | ||||
|                 if (selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][selectedSerStr]["sensitiveGain"] = sensitiveGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else if (gainMode == 1) { | ||||
|             SmGui::LeftLabel("Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", name), &linearGain, 0, 21)) { | ||||
|                 if (running) { | ||||
|                     airspy_set_linearity_gain(openDev, linearGain); | ||||
|                 } | ||||
|                 if (selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][selectedSerStr]["linearGain"] = linearGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else if (gainMode == 2) { | ||||
|             // TODO: Switch to a table for alignment | ||||
|             if (lnaAgc) { SmGui::BeginDisabled(); } | ||||
|             SmGui::LeftLabel("LNA Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", name), &lnaGain, 0, 15)) { | ||||
|                 if (running) { | ||||
|                     airspy_set_lna_gain(openDev, lnaGain); | ||||
|                 } | ||||
|                 if (selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][selectedSerStr]["lnaGain"] = lnaGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|             if (lnaAgc) { SmGui::EndDisabled(); } | ||||
|  | ||||
|             if (mixerAgc) { SmGui::BeginDisabled(); } | ||||
|             SmGui::LeftLabel("Mixer Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", name), &mixerGain, 0, 15)) { | ||||
|                 if (running) { | ||||
|                     airspy_set_mixer_gain(openDev, mixerGain); | ||||
|                 } | ||||
|                 if (selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][selectedSerStr]["mixerGain"] = mixerGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|             if (mixerAgc) { SmGui::EndDisabled(); } | ||||
|  | ||||
|             SmGui::LeftLabel("VGA Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", name), &vgaGain, 0, 15)) { | ||||
|                 if (running) { | ||||
|                     airspy_set_vga_gain(openDev, vgaGain); | ||||
|                 } | ||||
|                 if (selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][selectedSerStr]["vgaGain"] = vgaGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // AGC Control | ||||
|             SmGui::ForceSync(); | ||||
|             if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", name), &lnaAgc)) { | ||||
|                 if (running) { | ||||
|                     if (lnaAgc) { | ||||
|                         airspy_set_lna_agc(openDev, 1); | ||||
|                     } | ||||
|                     else { | ||||
|                         airspy_set_lna_agc(openDev, 0); | ||||
|                         airspy_set_lna_gain(openDev, lnaGain); | ||||
|                     } | ||||
|                 } | ||||
|                 if (selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][selectedSerStr]["lnaAgc"] = lnaAgc; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|             SmGui::ForceSync(); | ||||
|             if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", name), &mixerAgc)) { | ||||
|                 if (running) { | ||||
|                     if (mixerAgc) { | ||||
|                         airspy_set_mixer_agc(openDev, 1); | ||||
|                     } | ||||
|                     else { | ||||
|                         airspy_set_mixer_agc(openDev, 0); | ||||
|                         airspy_set_mixer_gain(openDev, mixerGain); | ||||
|                     } | ||||
|                 } | ||||
|                 if (selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][selectedSerStr]["mixerAgc"] = mixerAgc; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Bias T | ||||
|         if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", name), &biasT)) { | ||||
|             if (running) { | ||||
|                 airspy_set_rf_bias(openDev, biasT); | ||||
|             } | ||||
|             if (selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][selectedSerStr]["biasT"] = biasT; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::string getBandwdithScaled(double bw) { | ||||
|         char buf[1024]; | ||||
| @@ -246,322 +546,6 @@ private: | ||||
|         return std::string(buf); | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
|         AirspySourceModule* _this = (AirspySourceModule*)ctx; | ||||
|         core::setInputSampleRate(_this->sampleRate); | ||||
|         flog::info("AirspySourceModule '{0}': Menu Select!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void menuDeselected(void* ctx) { | ||||
|         AirspySourceModule* _this = (AirspySourceModule*)ctx; | ||||
|         flog::info("AirspySourceModule '{0}': Menu Deselect!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void start(void* ctx) { | ||||
|         AirspySourceModule* _this = (AirspySourceModule*)ctx; | ||||
|         if (_this->running) { return; } | ||||
|         if (_this->selectedSerial == 0) { | ||||
|             flog::error("Tried to start Airspy source with null serial"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
|         int err = airspy_open_sn(&_this->openDev, _this->selectedSerial); | ||||
| #else | ||||
|         int err = airspy_open_fd(&_this->openDev, _this->devFd); | ||||
| #endif | ||||
|         if (err != 0) { | ||||
|             char buf[1024]; | ||||
|             sprintf(buf, "%016" PRIX64, _this->selectedSerial); | ||||
|             flog::error("Could not open Airspy {0}", buf); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         airspy_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]); | ||||
|         airspy_set_freq(_this->openDev, _this->freq); | ||||
|  | ||||
|         if (_this->gainMode == 0) { | ||||
|             airspy_set_lna_agc(_this->openDev, 0); | ||||
|             airspy_set_mixer_agc(_this->openDev, 0); | ||||
|             airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); | ||||
|         } | ||||
|         else if (_this->gainMode == 1) { | ||||
|             airspy_set_lna_agc(_this->openDev, 0); | ||||
|             airspy_set_mixer_agc(_this->openDev, 0); | ||||
|             airspy_set_linearity_gain(_this->openDev, _this->linearGain); | ||||
|         } | ||||
|         else if (_this->gainMode == 2) { | ||||
|             if (_this->lnaAgc) { | ||||
|                 airspy_set_lna_agc(_this->openDev, 1); | ||||
|             } | ||||
|             else { | ||||
|                 airspy_set_lna_agc(_this->openDev, 0); | ||||
|                 airspy_set_lna_gain(_this->openDev, _this->lnaGain); | ||||
|             } | ||||
|             if (_this->mixerAgc) { | ||||
|                 airspy_set_mixer_agc(_this->openDev, 1); | ||||
|             } | ||||
|             else { | ||||
|                 airspy_set_mixer_agc(_this->openDev, 0); | ||||
|                 airspy_set_mixer_gain(_this->openDev, _this->mixerGain); | ||||
|             } | ||||
|             airspy_set_vga_gain(_this->openDev, _this->vgaGain); | ||||
|         } | ||||
|  | ||||
|         airspy_set_rf_bias(_this->openDev, _this->biasT); | ||||
|  | ||||
|         airspy_start_rx(_this->openDev, callback, _this); | ||||
|  | ||||
|         _this->running = true; | ||||
|         flog::info("AirspySourceModule '{0}': Start!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void stop(void* ctx) { | ||||
|         AirspySourceModule* _this = (AirspySourceModule*)ctx; | ||||
|         if (!_this->running) { return; } | ||||
|         _this->running = false; | ||||
|         _this->stream.stopWriter(); | ||||
|         airspy_close(_this->openDev); | ||||
|         _this->stream.clearWriteStop(); | ||||
|         flog::info("AirspySourceModule '{0}': Stop!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         AirspySourceModule* _this = (AirspySourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             airspy_set_freq(_this->openDev, freq); | ||||
|         } | ||||
|         _this->freq = freq; | ||||
|         flog::info("AirspySourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
|     } | ||||
|  | ||||
|     static void menuHandler(void* ctx) { | ||||
|         AirspySourceModule* _this = (AirspySourceModule*)ctx; | ||||
|  | ||||
|         if (_this->running) { SmGui::BeginDisabled(); } | ||||
|  | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { | ||||
|             _this->selectBySerial(_this->devList[_this->devId]); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             if (_this->selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["device"] = _this->selectedSerStr; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) { | ||||
|             _this->sampleRate = _this->sampleRateList[_this->srId]; | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             if (_this->selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->sampleRate; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SmGui::SameLine(); | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             config.acquire(); | ||||
|             std::string devSerial = config.conf["device"]; | ||||
|             config.release(); | ||||
|             _this->selectByString(devSerial); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|         } | ||||
|  | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|  | ||||
|         SmGui::BeginGroup(); | ||||
|         SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", _this->name), false); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", _this->name), _this->gainMode == 0)) { | ||||
|             _this->gainMode = 0; | ||||
|             if (_this->running) { | ||||
|                 airspy_set_lna_agc(_this->openDev, 0); | ||||
|                 airspy_set_mixer_agc(_this->openDev, 0); | ||||
|                 airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); | ||||
|             } | ||||
|             if (_this->selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|         SmGui::NextColumn(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", _this->name), _this->gainMode == 1)) { | ||||
|             _this->gainMode = 1; | ||||
|             if (_this->running) { | ||||
|                 airspy_set_lna_agc(_this->openDev, 0); | ||||
|                 airspy_set_mixer_agc(_this->openDev, 0); | ||||
|                 airspy_set_linearity_gain(_this->openDev, _this->linearGain); | ||||
|             } | ||||
|             if (_this->selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|         SmGui::NextColumn(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", _this->name), _this->gainMode == 2)) { | ||||
|             _this->gainMode = 2; | ||||
|             if (_this->running) { | ||||
|                 if (_this->lnaAgc) { | ||||
|                     airspy_set_lna_agc(_this->openDev, 1); | ||||
|                 } | ||||
|                 else { | ||||
|                     airspy_set_lna_agc(_this->openDev, 0); | ||||
|                     airspy_set_lna_gain(_this->openDev, _this->lnaGain); | ||||
|                 } | ||||
|                 if (_this->mixerAgc) { | ||||
|                     airspy_set_mixer_agc(_this->openDev, 1); | ||||
|                 } | ||||
|                 else { | ||||
|                     airspy_set_mixer_agc(_this->openDev, 0); | ||||
|                     airspy_set_mixer_gain(_this->openDev, _this->mixerGain); | ||||
|                 } | ||||
|                 airspy_set_vga_gain(_this->openDev, _this->vgaGain); | ||||
|             } | ||||
|             if (_this->selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|         SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false); | ||||
|         SmGui::EndGroup(); | ||||
|  | ||||
|         // Gain menus | ||||
|  | ||||
|         if (_this->gainMode == 0) { | ||||
|             SmGui::LeftLabel("Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) { | ||||
|                 if (_this->running) { | ||||
|                     airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); | ||||
|                 } | ||||
|                 if (_this->selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else if (_this->gainMode == 1) { | ||||
|             SmGui::LeftLabel("Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) { | ||||
|                 if (_this->running) { | ||||
|                     airspy_set_linearity_gain(_this->openDev, _this->linearGain); | ||||
|                 } | ||||
|                 if (_this->selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else if (_this->gainMode == 2) { | ||||
|             // TODO: Switch to a table for alignment | ||||
|             if (_this->lnaAgc) { SmGui::BeginDisabled(); } | ||||
|             SmGui::LeftLabel("LNA Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) { | ||||
|                 if (_this->running) { | ||||
|                     airspy_set_lna_gain(_this->openDev, _this->lnaGain); | ||||
|                 } | ||||
|                 if (_this->selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|             if (_this->lnaAgc) { SmGui::EndDisabled(); } | ||||
|  | ||||
|             if (_this->mixerAgc) { SmGui::BeginDisabled(); } | ||||
|             SmGui::LeftLabel("Mixer Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) { | ||||
|                 if (_this->running) { | ||||
|                     airspy_set_mixer_gain(_this->openDev, _this->mixerGain); | ||||
|                 } | ||||
|                 if (_this->selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|             if (_this->mixerAgc) { SmGui::EndDisabled(); } | ||||
|  | ||||
|             SmGui::LeftLabel("VGA Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { | ||||
|                 if (_this->running) { | ||||
|                     airspy_set_vga_gain(_this->openDev, _this->vgaGain); | ||||
|                 } | ||||
|                 if (_this->selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // AGC Control | ||||
|             SmGui::ForceSync(); | ||||
|             if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", _this->name), &_this->lnaAgc)) { | ||||
|                 if (_this->running) { | ||||
|                     if (_this->lnaAgc) { | ||||
|                         airspy_set_lna_agc(_this->openDev, 1); | ||||
|                     } | ||||
|                     else { | ||||
|                         airspy_set_lna_agc(_this->openDev, 0); | ||||
|                         airspy_set_lna_gain(_this->openDev, _this->lnaGain); | ||||
|                     } | ||||
|                 } | ||||
|                 if (_this->selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|             SmGui::ForceSync(); | ||||
|             if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", _this->name), &_this->mixerAgc)) { | ||||
|                 if (_this->running) { | ||||
|                     if (_this->mixerAgc) { | ||||
|                         airspy_set_mixer_agc(_this->openDev, 1); | ||||
|                     } | ||||
|                     else { | ||||
|                         airspy_set_mixer_agc(_this->openDev, 0); | ||||
|                         airspy_set_mixer_gain(_this->openDev, _this->mixerGain); | ||||
|                     } | ||||
|                 } | ||||
|                 if (_this->selectedSerStr != "") { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Bias T | ||||
|         if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", _this->name), &_this->biasT)) { | ||||
|             if (_this->running) { | ||||
|                 airspy_set_rf_bias(_this->openDev, _this->biasT); | ||||
|             } | ||||
|             if (_this->selectedSerStr != "") { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static int callback(airspy_transfer_t* transfer) { | ||||
|         AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx; | ||||
|         memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t)); | ||||
| @@ -574,7 +558,6 @@ private: | ||||
|     bool enabled = true; | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
|     double sampleRate; | ||||
|     SourceManager::SourceHandler handler; | ||||
|     bool running = false; | ||||
|     double freq; | ||||
|     uint64_t selectedSerial = 0; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user