mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 00:48:11 +01:00 
			
		
		
		
	New spyserver source
This commit is contained in:
		| @@ -19,7 +19,7 @@ option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Depedencies: librt | ||||
| option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON) | ||||
| option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Depedencies: libsdrplay)" OFF) | ||||
| option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Depedencies: soapysdr)" ON) | ||||
| option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" OFF) | ||||
| option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON) | ||||
| option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON) | ||||
|  | ||||
| # Sinks | ||||
| @@ -182,7 +182,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") | ||||
|     add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.dylib\" \"$<TARGET_FILE_DIR:sdrpp>\") | ||||
| endif () | ||||
|  | ||||
| # cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" | ||||
| # cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON | ||||
|  | ||||
| # Install directives | ||||
| install(TARGETS sdrpp DESTINATION bin) | ||||
|   | ||||
| @@ -178,6 +178,8 @@ int sdrpp_main(int argc, char *argv[]) { | ||||
|     defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["SoapySDR Source"]["module"] = "soapy_source"; | ||||
|     defConfig["moduleInstances"]["SoapySDR Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["SpyServer Source"]["module"] = "spyserver_source"; | ||||
|     defConfig["moduleInstances"]["SpyServer Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source"; | ||||
|     defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true; | ||||
|  | ||||
|   | ||||
| @@ -343,6 +343,8 @@ void MainWindow::draw() { | ||||
|  | ||||
|     ImGui::SameLine(); | ||||
|  | ||||
|     bool tmpPlaySate = playing; | ||||
|     if (playButtonLocked && !tmpPlaySate) { style::beginDisabled(); } | ||||
|     if (playing) { | ||||
|         ImGui::PushID(ImGui::GetID("sdrpp_stop_btn")); | ||||
|         if (ImGui::ImageButton(icons::STOP, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(GLFW_KEY_END, false)) { | ||||
| @@ -364,6 +366,7 @@ void MainWindow::draw() { | ||||
|         } | ||||
|         ImGui::PopID(); | ||||
|     } | ||||
|     if (playButtonLocked && !tmpPlaySate) { style::endDisabled(); } | ||||
|  | ||||
|     ImGui::SameLine(); | ||||
|  | ||||
|   | ||||
| @@ -32,6 +32,7 @@ public: | ||||
|     bool isPlaying(); | ||||
|  | ||||
|     bool lockWaterfallControls = false; | ||||
|     bool playButtonLocked = false; | ||||
|  | ||||
|     Event<bool> onPlayStateChange; | ||||
|     Event<bool> onInitComplete; | ||||
|   | ||||
| @@ -279,7 +279,7 @@ Modules in beta are still included in releases for the most part but not enabled | ||||
| | rtl_tcp_source   | Working    | -                 | OPT_BUILD_RTL_TCP_SOURCE   | ✅              | ✅                     | ✅                         | | ||||
| | sdrplay_source   | Working    | SDRplay API       | OPT_BUILD_SDRPLAY_SOURCE   | ⛔              | ✅                     | ✅                         | | ||||
| | soapy_source     | Working    | soapysdr          | OPT_BUILD_SOAPY_SOURCE     | ✅              | ✅                     | ✅                         | | ||||
| | spyserver_source | Unfinished | -                 | OPT_BUILD_SPYSERVER_SOURCE | ⛔              | ⛔                     | ⛔                         | | ||||
| | spyserver_source | Beta       | -                 | OPT_BUILD_SPYSERVER_SOURCE | ✅              | ✅                     | ✅                         | | ||||
| | plutosdr_source  | Working    | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE  | ✅              | ✅                     | ✅                         | | ||||
|  | ||||
| ## Sinks | ||||
|   | ||||
| @@ -6,23 +6,50 @@ | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <core.h> | ||||
| #include <gui/style.h> | ||||
| #include <config.h> | ||||
| #include <options.h> | ||||
| #include <libairspyhf/airspyhf.h> | ||||
| #include <gui/widgets/stepped_slider.h> | ||||
|  | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| SDRPP_MOD_INFO { | ||||
|     /* Name:            */ "spyserver_source", | ||||
|     /* Description:     */ "SpyServer source module for SDR++", | ||||
|     /* Description:     */ "Airspy HF+ source module for SDR++", | ||||
|     /* Author:          */ "Ryzerth", | ||||
|     /* Version:         */ 0, 1, 0, | ||||
|     /* Max instances    */ 1 | ||||
| }; | ||||
|  | ||||
| class SpyServerSourceModule : public ModuleManager::Instance { | ||||
| const char* deviceTypesStr[] = { | ||||
|     "Unknown", | ||||
|     "Airspy One", | ||||
|     "Airspy HF+", | ||||
|     "RTL-SDR" | ||||
| }; | ||||
|  | ||||
| const char* streamFormatStr = "UInt8\0" | ||||
|                             "Int16\0" | ||||
|                             "Float32\0"; | ||||
|  | ||||
| const SpyServerStreamFormat streamFormats[] = { | ||||
|     SPYSERVER_STREAM_FORMAT_UINT8, | ||||
|     SPYSERVER_STREAM_FORMAT_INT16, | ||||
|     SPYSERVER_STREAM_FORMAT_FLOAT | ||||
| }; | ||||
|  | ||||
| ConfigManager config; | ||||
|  | ||||
| class AirspyHFSourceModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     SpyServerSourceModule(std::string name) { | ||||
|     AirspyHFSourceModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         sampleRate = 2560000.0; | ||||
|         config.acquire(); | ||||
|         std::string host = config.conf["hostname"]; | ||||
|         port = config.conf["port"]; | ||||
|         config.release(); | ||||
|  | ||||
|         handler.ctx = this; | ||||
|         handler.selectHandler = menuSelected; | ||||
| @@ -31,11 +58,14 @@ public: | ||||
|         handler.startHandler = start; | ||||
|         handler.stopHandler = stop; | ||||
|         handler.tuneHandler = tune; | ||||
|         handler.stream = &client.iqStream; | ||||
|         handler.stream = &stream; | ||||
|  | ||||
|         strcpy(hostname, host.c_str()); | ||||
|  | ||||
|         sigpath::sourceManager.registerSource("SpyServer", &handler); | ||||
|     } | ||||
|  | ||||
|     ~SpyServerSourceModule() { | ||||
|     ~AirspyHFSourceModule() { | ||||
|          | ||||
|     } | ||||
|  | ||||
| @@ -52,127 +82,244 @@ public: | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::string getBandwdithScaled(double bw) { | ||||
|         char buf[1024]; | ||||
|         if (bw >= 1000000.0) { | ||||
|             sprintf(buf, "%.1lfMHz", bw / 1000000.0); | ||||
|         } | ||||
|         else if (bw >= 1000.0) { | ||||
|             sprintf(buf, "%.1lfKHz", bw / 1000.0); | ||||
|         } | ||||
|         else { | ||||
|             sprintf(buf, "%.1lfHz", bw); | ||||
|         } | ||||
|         return std::string(buf); | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; | ||||
|         core::setInputSampleRate(_this->sampleRate); | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Menu Select!", _this->name); | ||||
|         gui::mainWindow.playButtonLocked = !(_this->client && _this->client->isOpen()); | ||||
|         spdlog::info("AirspyHFSourceModule '{0}': Menu Select!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void menuDeselected(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Menu Deselect!", _this->name); | ||||
|         AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; | ||||
|         gui::mainWindow.playButtonLocked = false; | ||||
|         spdlog::info("AirspyHFSourceModule '{0}': Menu Deselect!", _this->name); | ||||
|     } | ||||
|      | ||||
|     static void start(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             return; | ||||
|         } | ||||
|         if (!_this->client.connectToSpyserver(_this->ip, _this->port)) { | ||||
|             spdlog::error("Could not connect to {0}:{1}", _this->ip, _this->port); | ||||
|             return; | ||||
|         } | ||||
|         _this->client.tune(_this->freq); | ||||
|         _this->client.setSampleRate(_this->sampleRate); | ||||
|         //_this->client.setGainIndex(_this->gain); | ||||
|         //_this->client.setGainMode(!_this->tunerAGC); | ||||
|         //_this->client.setDirectSampling(_this->directSamplingMode); | ||||
|         //_this->client.setAGCMode(_this->rtlAGC); | ||||
|          | ||||
|         _this->client->setSetting(SPYSERVER_SETTING_IQ_FORMAT, streamFormats[_this->iqType]); | ||||
|         _this->client->setSetting(SPYSERVER_SETTING_IQ_DECIMATION, _this->srId); | ||||
|         _this->client->setSetting(SPYSERVER_SETTING_IQ_FREQUENCY, _this->freq); | ||||
|         _this->client->setSetting(SPYSERVER_SETTING_IQ_DIGITAL_GAIN, 0); | ||||
|         _this->client->setSetting(SPYSERVER_SETTING_STREAMING_MODE, SPYSERVER_STREAM_MODE_IQ_ONLY); | ||||
|          | ||||
|         _this->client->setSetting(SPYSERVER_SETTING_GAIN, _this->gain); | ||||
|         _this->client->startStream(); | ||||
|  | ||||
|         _this->running = true; | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Start!", _this->name); | ||||
|         spdlog::info("AirspyHFSourceModule '{0}': Start!", _this->name); | ||||
|     } | ||||
|      | ||||
|     static void stop(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; | ||||
|         if (!_this->running) { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         _this->client->stopStream(); | ||||
|  | ||||
|         _this->running = false; | ||||
|         _this->client.disconnect(); | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Stop!", _this->name); | ||||
|         spdlog::info("AirspyHFSourceModule '{0}': Stop!", _this->name); | ||||
|     } | ||||
|      | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             _this->client.tune(freq); | ||||
|             _this->client->setSetting(SPYSERVER_SETTING_IQ_FREQUENCY, freq); | ||||
|         } | ||||
|         _this->freq = freq; | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
|         spdlog::info("AirspyHFSourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
|     } | ||||
|      | ||||
|     static void menuHandler(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; | ||||
|         float menuWidth = ImGui::GetContentRegionAvailWidth(); | ||||
|         float portWidth = ImGui::CalcTextSize("00000").x + 20; | ||||
|  | ||||
|         ImGui::SetNextItemWidth(menuWidth - portWidth); | ||||
|         ImGui::InputText(CONCAT("##_ip_select_", _this->name), _this->ip, 1024); | ||||
|         bool connected = (_this->client && _this->client->isOpen()); | ||||
|         gui::mainWindow.playButtonLocked = !connected; | ||||
|  | ||||
|         if (connected) { style::beginDisabled(); } | ||||
|         if (ImGui::InputText(CONCAT("##_rigctl_srv_host_", _this->name), _this->hostname, 1023)) { | ||||
|             config.acquire(); | ||||
|             config.conf["hostname"] = _this->hostname; | ||||
|             config.release(true); | ||||
|         } | ||||
|         ImGui::SameLine(); | ||||
|         ImGui::SetNextItemWidth(portWidth); | ||||
|         ImGui::InputInt(CONCAT("##_port_select_", _this->name), &_this->port, 0); | ||||
|  | ||||
|         ImGui::SetNextItemWidth(ImGui::CalcTextSize("OOOOOOOOOO").x); | ||||
|         if (ImGui::Combo("Direct sampling", &_this->directSamplingMode, "Disabled\0I branch\0Q branch\0")) { | ||||
|             if (_this->running) { | ||||
|                 //_this->client.setDirectSampling(_this->directSamplingMode); | ||||
|             } | ||||
|         ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|         if (ImGui::InputInt(CONCAT("##_rigctl_srv_port_", _this->name), &_this->port, 0, 0)) { | ||||
|             config.acquire(); | ||||
|             config.conf["port"] = _this->port; | ||||
|             config.release(true); | ||||
|         } | ||||
|         if (connected) { style::endDisabled(); } | ||||
|  | ||||
|         if (ImGui::Checkbox("RTL AGC", &_this->rtlAGC)) { | ||||
|             if (_this->running) { | ||||
|                 //_this->client.setAGCMode(_this->rtlAGC); | ||||
|             } | ||||
|         } | ||||
|         if (_this->running) { style::beginDisabled(); } | ||||
|         if (!connected && ImGui::Button("Connect##spyserver_source", ImVec2(menuWidth, 0))) { | ||||
|             try { | ||||
|                 _this->client = spyserver::connect(_this->hostname, _this->port, &_this->stream); | ||||
|  | ||||
|         if (ImGui::Checkbox("Tuner AGC", &_this->tunerAGC)) { | ||||
|             if (_this->running) { | ||||
|                 //_this->client.setGainMode(!_this->tunerAGC); | ||||
|                 if (!_this->tunerAGC) { | ||||
|                     //_this->client.setGainIndex(_this->gain); | ||||
|                 if (!_this->client->waitForDevInfo(3000)) { | ||||
|                     spdlog::error("SpyServer didn't respond with device information"); | ||||
|                 } | ||||
|                 else { | ||||
|                     char buf[1024]; | ||||
|                     sprintf(buf, "%s [%08X]", deviceTypesStr[_this->client->devInfo.DeviceType], _this->client->devInfo.DeviceSerial); | ||||
|                     _this->devRef = std::string(buf); | ||||
|  | ||||
|                     config.acquire(); | ||||
|                     if (!config.conf["devices"].contains(_this->devRef)) { | ||||
|                         config.conf["devices"][_this->devRef]["sampleRateId"] = 0; | ||||
|                         config.conf["devices"][_this->devRef]["sampleBitDepthId"] = 1; | ||||
|                         config.conf["devices"][_this->devRef]["gainId"] = 0; | ||||
|                     } | ||||
|                     _this->srId = config.conf["devices"][_this->devRef]["sampleRateId"]; | ||||
|                     _this->iqType = config.conf["devices"][_this->devRef]["sampleBitDepthId"]; | ||||
|                     _this->gain = config.conf["devices"][_this->devRef]["gainId"]; | ||||
|                     config.release(true); | ||||
|  | ||||
|                     _this->gain = std::clamp<int>(_this->gain, 0, _this->client->devInfo.MaximumGainIndex); | ||||
|  | ||||
|                     // Refresh sample rates | ||||
|                     _this->sampleRates.clear(); | ||||
|                     _this->sampleRatesTxt.clear(); | ||||
|                     for (int i = _this->client->devInfo.MinimumIQDecimation; i <= _this->client->devInfo.DecimationStageCount; i++) { | ||||
|                         double sr = (double)_this->client->devInfo.MaximumSampleRate / ((double)(1 << i)); | ||||
|                         _this->sampleRates.push_back(sr); | ||||
|                         _this->sampleRatesTxt += _this->getBandwdithScaled(sr); | ||||
|                         _this->sampleRatesTxt += '\0'; | ||||
|                     } | ||||
|  | ||||
|                     _this->srId = std::clamp<int>(_this->srId, 0, _this->sampleRates.size()-1); | ||||
|  | ||||
|                     _this->sampleRate = _this->sampleRates[_this->srId]; | ||||
|                     core::setInputSampleRate(_this->sampleRate); | ||||
|                     spdlog::info("Connected to server"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (_this->tunerAGC) { style::beginDisabled(); } | ||||
|         ImGui::SetNextItemWidth(menuWidth); | ||||
|         if (ImGui::SliderInt(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 29, "")) { | ||||
|             if (_this->running) { | ||||
|                 //_this->client.setGainIndex(_this->gain); | ||||
|             catch (std::exception e) { | ||||
|                 spdlog::error("Could not connect to spyserver {0}", e.what()); | ||||
|             } | ||||
|         } | ||||
|         if (_this->tunerAGC) { style::endDisabled(); } | ||||
|         else if (connected && ImGui::Button("Disconnect##spyserver_source", ImVec2(menuWidth, 0))) { | ||||
|             _this->client->close(); | ||||
|         } | ||||
|         if (_this->running) { style::endDisabled(); } | ||||
|  | ||||
|          | ||||
|  | ||||
|         if (connected) { | ||||
|             if (_this->running) { style::beginDisabled(); } | ||||
|             ImGui::Text("Samplerate"); | ||||
|             ImGui::SameLine(); | ||||
|             ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|             if (ImGui::Combo("##spyserver_source_sr", &_this->srId, _this->sampleRatesTxt.c_str())) { | ||||
|                 _this->sampleRate = _this->sampleRates[_this->srId]; | ||||
|                 core::setInputSampleRate(_this->sampleRate); | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->devRef]["sampleRateId"] = _this->srId; | ||||
|                 config.release(true); | ||||
|             } | ||||
|             ImGui::Text("Sample bit depth"); | ||||
|             ImGui::SameLine(); | ||||
|             ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|             if (ImGui::Combo("##spyserver_source_type", &_this->iqType, streamFormatStr)) { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->devRef]["sampleBitDepthId"] = _this->iqType; | ||||
|                 config.release(true); | ||||
|             } | ||||
|             if (_this->running) { style::endDisabled(); } | ||||
|  | ||||
|             if (_this->client->devInfo.MaximumGainIndex) { | ||||
|                 ImGui::SetNextItemWidth(menuWidth); | ||||
|                 if (ImGui::SliderInt("##spyserver_source_gain", &_this->gain, 0, _this->client->devInfo.MaximumGainIndex)) { | ||||
|                     _this->client->setSetting(SPYSERVER_SETTING_GAIN, _this->gain); | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->devRef]["gainId"] = _this->gain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             }             | ||||
|  | ||||
|             ImGui::Text("Status:"); | ||||
|             ImGui::SameLine(); | ||||
|             ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Connected (%s)", deviceTypesStr[_this->client->devInfo.DeviceType]); | ||||
|         } | ||||
|         else { | ||||
|             ImGui::Text("Status:"); | ||||
|             ImGui::SameLine(); | ||||
|             ImGui::Text("Not connected"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
|     double sampleRate; | ||||
|     SourceManager::SourceHandler handler; | ||||
|     std::thread workerThread; | ||||
|     SpyServerClient client; | ||||
|     bool running = false; | ||||
|     double sampleRate = 1000000; | ||||
|     double freq; | ||||
|     char ip[1024] = "localhost"; | ||||
|  | ||||
|     char hostname[1024]; | ||||
|     int port = 5555; | ||||
|     int iqType = 0; | ||||
|  | ||||
|     int srId = 0; | ||||
|     std::vector<double> sampleRates; | ||||
|     std::string sampleRatesTxt; | ||||
|  | ||||
|     int gain = 0; | ||||
|     bool rtlAGC = false; | ||||
|     bool tunerAGC = false; | ||||
|     int directSamplingMode = 0; | ||||
|  | ||||
|     std::string devRef = ""; | ||||
|  | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
|     SourceManager::SourceHandler handler; | ||||
|  | ||||
|     spyserver::SpyServerClient client; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|    // Do your one time init here | ||||
|     json def = json({}); | ||||
|     def["hostname"] = "localhost"; | ||||
|     def["port"] = 5555; | ||||
|     def["devices"] = json::object(); | ||||
|     config.setPath(options::opts.root + "/spyserver_config.json"); | ||||
|     config.load(def); | ||||
|     config.enableAutoSave(); | ||||
|  | ||||
|     // Check config in case a user has a very old version | ||||
|     config.acquire(); | ||||
|     bool corrected = false; | ||||
|     if (!config.conf.contains("hostname") || !config.conf.contains("port") || !config.conf.contains("devices")) { | ||||
|         config.conf = def; | ||||
|         corrected = true; | ||||
|     } | ||||
|     config.release(corrected); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new SpyServerSourceModule(name); | ||||
|     return new AirspyHFSourceModule(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { | ||||
|     delete (SpyServerSourceModule*)instance; | ||||
|     delete (AirspyHFSourceModule*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|     // Do your one shutdown here | ||||
|     config.disableAutoSave(); | ||||
|     config.save(); | ||||
| } | ||||
| @@ -1,254 +1,139 @@ | ||||
| #include <spyserver_client.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <volk/volk.h> | ||||
|  | ||||
| SpyServerClient::SpyServerClient() { | ||||
| using namespace std::chrono_literals; | ||||
|  | ||||
| } | ||||
| namespace spyserver { | ||||
|     SpyServerClientClass::SpyServerClientClass(net::Conn conn, dsp::stream<dsp::complex_t>* out) { | ||||
|         readBuf = new uint8_t[SPYSERVER_MAX_MESSAGE_BODY_SIZE]; | ||||
|         writeBuf = new uint8_t[SPYSERVER_MAX_MESSAGE_BODY_SIZE]; | ||||
|         client = std::move(conn); | ||||
|         output = out; | ||||
|  | ||||
| bool SpyServerClient::connectToSpyserver(char* host, int port) { | ||||
|     if (connected) { | ||||
|         return true; | ||||
|         sendHandshake("SDR++"); | ||||
|  | ||||
|         client->readAsync(sizeof(SpyServerMessageHeader), (uint8_t*)&receivedHeader, dataHandler, this); | ||||
|     } | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|     struct addrinfo *result = NULL; | ||||
|     struct addrinfo *ptr = NULL; | ||||
|     struct addrinfo hints; | ||||
|  | ||||
|     ZeroMemory( &hints, sizeof(hints) ); | ||||
|     hints.ai_family = AF_UNSPEC; | ||||
|     hints.ai_socktype = SOCK_STREAM; | ||||
|     hints.ai_protocol = IPPROTO_TCP; | ||||
|  | ||||
|     char buf[128]; | ||||
|     sprintf(buf, "%hu", port); | ||||
|  | ||||
|     int iResult = getaddrinfo(host, buf, &hints, &result); | ||||
|     if (iResult != 0) { | ||||
|         // TODO: log error | ||||
|         printf("A"); | ||||
|         WSACleanup(); | ||||
|         return false; | ||||
|     } | ||||
|     ptr = result; | ||||
|  | ||||
|     sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); | ||||
|  | ||||
|     if (sock == INVALID_SOCKET) { | ||||
|         // TODO: log error | ||||
|         printf("B"); | ||||
|         freeaddrinfo(result); | ||||
|         WSACleanup(); | ||||
|         return false; | ||||
|     SpyServerClientClass::~SpyServerClientClass() { | ||||
|         close(); | ||||
|         delete[] readBuf; | ||||
|         delete[] writeBuf; | ||||
|     } | ||||
|  | ||||
|     iResult = connect(sock, ptr->ai_addr, (int)ptr->ai_addrlen); | ||||
|     if (iResult == SOCKET_ERROR) { | ||||
|         printf("C"); | ||||
|         closesocket(sock); | ||||
|         freeaddrinfo(result); | ||||
|         WSACleanup(); | ||||
|         return false; | ||||
|     void SpyServerClientClass::startStream() { | ||||
|         setSetting(SPYSERVER_SETTING_STREAMING_ENABLED, true); | ||||
|     } | ||||
|     freeaddrinfo(result); | ||||
| #else | ||||
|     sockfd = socket(AF_INET, SOCK_STREAM, 0); | ||||
|     if (sockfd < 0) { | ||||
|         // TODO: Log error | ||||
|         return false; | ||||
|  | ||||
|     void SpyServerClientClass::stopStream() { | ||||
|         setSetting(SPYSERVER_SETTING_STREAMING_ENABLED, false); | ||||
|     } | ||||
|     struct hostent *server = gethostbyname(host); | ||||
|     struct sockaddr_in serv_addr; | ||||
|     bzero(&serv_addr, sizeof(struct sockaddr_in)); | ||||
|     serv_addr.sin_family = AF_INET; | ||||
|     bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); | ||||
|     serv_addr.sin_port = htons(port); | ||||
|     if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) { | ||||
|         // TODO: log error | ||||
|         return false; | ||||
|  | ||||
|     void SpyServerClientClass::close() { | ||||
|         client->close(); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     // Switch to non-blocking mode | ||||
| #ifdef _WIN32 | ||||
|     unsigned long mode = 1; | ||||
|     ioctlsocket(sock, FIONBIO, &mode); | ||||
| #else | ||||
|     fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK); | ||||
| #endif | ||||
|     bool SpyServerClientClass::isOpen() { | ||||
|         return client->isOpen(); | ||||
|     } | ||||
|  | ||||
|     connected = true; | ||||
|     waiting = true; | ||||
|     bool SpyServerClientClass::waitForDevInfo(int timeoutMS) { | ||||
|         std::unique_lock lck(deviceInfoMtx); | ||||
|         auto now = std::chrono::system_clock::now(); | ||||
|         deviceInfoCnd.wait_until(lck, now + (timeoutMS*1ms), [this](){ return deviceInfoAvailable; }); | ||||
|         return deviceInfoAvailable; | ||||
|     } | ||||
|  | ||||
|     workerThread = std::thread(&SpyServerClient::worker, this); | ||||
|     void SpyServerClientClass::sendCommand(uint32_t command, void* data, int len) { | ||||
|         SpyServerCommandHeader* hdr = (SpyServerCommandHeader*)writeBuf; | ||||
|         hdr->CommandType = command; | ||||
|         hdr->BodySize = len; | ||||
|         memcpy(&writeBuf[sizeof(SpyServerCommandHeader)], data, len); | ||||
|         client->write(sizeof(SpyServerCommandHeader) + len, writeBuf); | ||||
|     } | ||||
|  | ||||
|     printf("Connected"); | ||||
|     void SpyServerClientClass::sendHandshake(std::string appName) { | ||||
|         int totSize = sizeof(SpyServerClientHandshake) + appName.size(); | ||||
|         uint8_t* buf = new uint8_t[totSize]; | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|         SpyServerClientHandshake* cmdHandshake = (SpyServerClientHandshake*)buf; | ||||
|         cmdHandshake->ProtocolVersion = SPYSERVER_PROTOCOL_VERSION; | ||||
|  | ||||
| bool SpyServerClient::disconnect() { | ||||
|         if (!connected) { | ||||
|             return false; | ||||
|         memcpy(&buf[sizeof(SpyServerClientHandshake)], appName.c_str(), appName.size()); | ||||
|         sendCommand(SPYSERVER_CMD_HELLO, buf, totSize); | ||||
|  | ||||
|         delete[] buf; | ||||
|     } | ||||
|  | ||||
|     void SpyServerClientClass::setSetting(uint32_t setting, uint32_t arg) { | ||||
|         SpyServerSettingTarget target; | ||||
|         target.Setting = setting; | ||||
|         target.Value = arg; | ||||
|         sendCommand(SPYSERVER_CMD_SET_SETTING, &target, sizeof(SpyServerSettingTarget)); | ||||
|     } | ||||
|  | ||||
|     int SpyServerClientClass::readSize(int count, uint8_t* buffer) { | ||||
|         int read = 0; | ||||
|         int len = 0; | ||||
|         while (read < count) { | ||||
|             len = client->read(count - read, &buffer[read]); | ||||
|             if (len <= 0) { return len; } | ||||
|             read += len; | ||||
|         } | ||||
|         waiting = false; | ||||
|         if (workerThread.joinable()) { | ||||
|             workerThread.join(); | ||||
|         } | ||||
| #ifdef _WIN32 | ||||
|         closesocket(sock); | ||||
|         WSACleanup(); | ||||
| #else | ||||
|         close(sockfd); | ||||
| #endif | ||||
|         connected = false; | ||||
|         return true; | ||||
| } | ||||
|  | ||||
| void SpyServerClient::setSampleRate(uint32_t setSampleRate) { | ||||
|  | ||||
| } | ||||
|  | ||||
| void SpyServerClient::tune(uint32_t freq) { | ||||
|  | ||||
| } | ||||
|  | ||||
| int SpyServerClient::receive(char* buf, int count) { | ||||
| #ifdef _WIN32 | ||||
|     return checkError(recv(sock, (char*)buf, count, 0), count); | ||||
| #else | ||||
|     return checkError(read(sockfd, buf, count), count); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int SpyServerClient::checkError(int len, int expected) { | ||||
| #ifdef _WIN32 | ||||
|     if (len != SOCKET_ERROR) { return len; } | ||||
|     int code = WSAGetLastError(); | ||||
|     if (code == WSAEWOULDBLOCK) { return 0; } | ||||
|     spdlog::error("{0}", code); | ||||
|     return -1; | ||||
| #else | ||||
|     if (len <= expected) { | ||||
|         return len; | ||||
|         return read; | ||||
|     } | ||||
|     if (len == EAGAIN || len == EWOULDBLOCK) { return 0; } | ||||
|     return -1; | ||||
| #endif   | ||||
| } | ||||
|  | ||||
| int SpyServerClient::receiveSync(char* buf, int count) { | ||||
|     int len = receive(buf, count); | ||||
|     while (len == 0 && waiting) { | ||||
|         len = receive(buf, count); | ||||
|     } | ||||
|     if (!waiting) { | ||||
|         return 0; | ||||
|     } | ||||
|     return len; | ||||
| } | ||||
|     void SpyServerClientClass::dataHandler(int count, uint8_t* buf, void* ctx) { | ||||
|         SpyServerClientClass* _this = (SpyServerClientClass*)ctx; | ||||
|  | ||||
| void SpyServerClient::worker() { | ||||
|     // Allocate dummy buffer | ||||
|     char* dummyBuf = (char*)malloc(1000000); | ||||
|  | ||||
|     // Send hello | ||||
|     hello(); | ||||
|  | ||||
|     //   SETTING_STREAMING_MODE        = 0, | ||||
|     //   SETTING_STREAMING_ENABLED     = 1, | ||||
|     //   SETTING_GAIN                  = 2, | ||||
|  | ||||
|     //   SETTING_IQ_FORMAT             = 100,    // 0x64 | ||||
|     //   SETTING_IQ_FREQUENCY          = 101,    // 0x65 | ||||
|     //   SETTING_IQ_DECIMATION         = 102,    // 0x66 | ||||
|     //   SETTING_IQ_DIGITAL_GAIN       = 103,    // 0x67 | ||||
|  | ||||
|     // Set settings | ||||
|     setSetting(SETTING_STREAMING_MODE, STREAM_MODE_IQ_ONLY); | ||||
|     setSetting(SETTING_GAIN, 5); | ||||
|     setSetting(SETTING_IQ_FORMAT, STREAM_FORMAT_FLOAT); | ||||
|     setSetting(SETTING_IQ_FREQUENCY, 2000000); | ||||
|     setSetting(SETTING_IQ_DECIMATION, 1); | ||||
|     setSetting(SETTING_IQ_DIGITAL_GAIN, 1); | ||||
|  | ||||
|     // Enable stream | ||||
|     setSetting(SETTING_STREAMING_ENABLED, 1); | ||||
|  | ||||
|     // Main message receive loop | ||||
|     while (true) { | ||||
|         MessageHeader msgHeader; | ||||
|         int len = receiveSync((char*)&msgHeader, sizeof(MessageHeader)); | ||||
|         if (len < 0) { | ||||
|             spdlog::error("Socket error"); | ||||
|         int size = _this->readSize(_this->receivedHeader.BodySize, _this->readBuf); | ||||
|         if (size <= 0) { | ||||
|             printf("ERROR: Didn't receive enough bytes\n"); | ||||
|             return; | ||||
|         } | ||||
|         if (len == 0) { return; } | ||||
|  | ||||
|         int type = (msgHeader.MessageType & 0xFFFF); | ||||
|         //printf("MSG %08X %d %d %08X %d\n", _this->receivedHeader.ProtocolID, _this->receivedHeader.MessageType, _this->receivedHeader.StreamType, _this->receivedHeader.SequenceNumber, _this->receivedHeader.BodySize); | ||||
|  | ||||
|         if (type == MSG_TYPE_DEVICE_INFO) { | ||||
|             DeviceInfo devInfo; | ||||
|             len = receiveSync((char*)&devInfo, sizeof(DeviceInfo)); | ||||
|             if (len < 0) { | ||||
|                 spdlog::error("A Socket error"); | ||||
|                 return; | ||||
|         if (_this->receivedHeader.MessageType == SPYSERVER_MSG_TYPE_DEVICE_INFO) { | ||||
|             { | ||||
|                 std::lock_guard lck(_this->deviceInfoMtx); | ||||
|                 SpyServerDeviceInfo* _devInfo = (SpyServerDeviceInfo*)_this->readBuf; | ||||
|                 _this->devInfo = *_devInfo; | ||||
|                 _this->deviceInfoAvailable = true; | ||||
|             } | ||||
|             if (len == 0) { return; } | ||||
|  | ||||
|             spdlog::warn("Dev type: {0}", devInfo.DeviceType); | ||||
|             _this->deviceInfoCnd.notify_all(); | ||||
|         } | ||||
|         // else if (type == MSG_TYPE_FLOAT_IQ) { | ||||
|         //     //if (iqStream.acquire() < 0) { return; } | ||||
|         //     len = receiveSync(dummyBuf, msgHeader.BodySize); | ||||
|         //     //iqStream.write(msgHeader.BodySize); | ||||
|         //     if (len < 0) { | ||||
|         //         spdlog::error("T Socket error"); | ||||
|         //         return; | ||||
|         //     } | ||||
|         //     if (len == 0) { return; } | ||||
|         // }    | ||||
|         else if (msgHeader.BodySize > 0) { | ||||
|             len = receiveSync(dummyBuf, msgHeader.BodySize); | ||||
|             if (len < 0) { | ||||
|                 spdlog::error("B Socket error {0}", msgHeader.ProtocolID); | ||||
|                 return; | ||||
|         else if (_this->receivedHeader.MessageType == SPYSERVER_MSG_TYPE_UINT8_IQ) { | ||||
|             int sampCount = _this->receivedHeader.BodySize / (sizeof(uint8_t)*2); | ||||
|             for (int i = 0; i < sampCount; i++) { | ||||
|                 _this->output->writeBuf[i].re = ((float)_this->readBuf[(2*i)] / 128.0f)-1.0f; | ||||
|                 _this->output->writeBuf[i].im = ((float)_this->readBuf[(2*i)+1] / 128.0f)-1.0f; | ||||
|             } | ||||
|             if (len == 0) { return; } | ||||
|             _this->output->swap(sampCount); | ||||
|         } | ||||
|         else if (_this->receivedHeader.MessageType == SPYSERVER_MSG_TYPE_INT16_IQ) { | ||||
|             int sampCount = _this->receivedHeader.BodySize / (sizeof(int16_t)*2); | ||||
|             volk_16i_s32f_convert_32f((float*)_this->output->writeBuf, (int16_t*)_this->readBuf, 32768.0, sampCount*2); | ||||
|             _this->output->swap(sampCount); | ||||
|         } | ||||
|         else if (_this->receivedHeader.MessageType == SPYSERVER_MSG_TYPE_INT24_IQ) { | ||||
|             printf("ERROR: IQ format not supported\n"); | ||||
|             return; | ||||
|         } | ||||
|         else if (_this->receivedHeader.MessageType == SPYSERVER_MSG_TYPE_FLOAT_IQ) { | ||||
|             int sampCount = _this->receivedHeader.BodySize / sizeof(dsp::complex_t); | ||||
|             memcpy(_this->output->writeBuf, _this->readBuf, _this->receivedHeader.BodySize); | ||||
|             _this->output->swap(sampCount); | ||||
|         } | ||||
|  | ||||
|         _this->client->readAsync(sizeof(SpyServerMessageHeader), (uint8_t*)&_this->receivedHeader, dataHandler, _this); | ||||
|     } | ||||
|  | ||||
|     free(dummyBuf); | ||||
| } | ||||
|  | ||||
| void SpyServerClient::sendCommand(uint32_t cmd, void* body, size_t bodySize) { | ||||
|     int size = sizeof(CommandHeader) + bodySize; | ||||
|     char* buf = new char[size]; | ||||
|     CommandHeader* cmdHdr = (CommandHeader*)buf; | ||||
|     memcpy(&buf[sizeof(CommandHeader)], body, bodySize); | ||||
|     cmdHdr->CommandType = cmd; | ||||
|     cmdHdr->BodySize = bodySize; | ||||
| #ifdef _WIN32 | ||||
|     send(sock, buf, size, 0); | ||||
| #else | ||||
|     write(sockfd, buf, size); | ||||
| #endif | ||||
|     delete[] buf; | ||||
| } | ||||
|  | ||||
| void SpyServerClient::setSetting(uint32_t setting, uint32_t value) { | ||||
|     char buf[sizeof(SettingTarget) + sizeof(uint32_t)]; | ||||
|     SettingTarget* tgt = (SettingTarget*)buf; | ||||
|     uint32_t* val = (uint32_t*)&buf[sizeof(SettingTarget)]; | ||||
|     tgt->SettingType = setting; | ||||
|     *val = value; | ||||
|     sendCommand(CMD_SET_SETTING, buf, sizeof(SettingTarget) + sizeof(uint32_t)); | ||||
| } | ||||
|  | ||||
| void SpyServerClient::hello() { | ||||
|     char buf[1024]; | ||||
|     ClientHandshake* handshake = (ClientHandshake*)buf; | ||||
|     handshake->ProtocolVersion = SPYSERVER_PROTOCOL_VERSION; | ||||
|     strcpy(&buf[sizeof(ClientHandshake)], "sdr++"); | ||||
|     sendCommand(CMD_HELLO, buf, sizeof(ClientHandshake) + 5); | ||||
|     SpyServerClient connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out) { | ||||
|         net::Conn conn = net::connect(net::PROTO_TCP, host, port); | ||||
|         if (!conn) { | ||||
|             return NULL; | ||||
|         } | ||||
|         return SpyServerClient(new SpyServerClientClass(std::move(conn), out)); | ||||
|     } | ||||
| } | ||||
| @@ -1,69 +1,52 @@ | ||||
| #pragma once | ||||
| #include <utils/networking.h> | ||||
| #include <spyserver_protocol.h> | ||||
| #include <dsp/stream.h> | ||||
| #include <dsp/types.h> | ||||
| #include <thread> | ||||
| #include <string> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #include <WinSock2.h> | ||||
| #include <WS2tcpip.h> | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #include <strings.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <netdb.h>  | ||||
| #endif | ||||
| namespace spyserver { | ||||
|     class SpyServerClientClass { | ||||
|     public: | ||||
|         SpyServerClientClass(net::Conn conn, dsp::stream<dsp::complex_t>* out); | ||||
|         ~SpyServerClientClass(); | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #define __attribute__(x) | ||||
| #pragma pack(push, 1) | ||||
| #endif | ||||
| struct command_t{ | ||||
| 	unsigned char cmd; | ||||
| 	unsigned int param; | ||||
| }__attribute__((packed)); | ||||
| #ifdef _WIN32 | ||||
| #pragma pack(pop) | ||||
| #endif | ||||
|         bool waitForDevInfo(int timeoutMS); | ||||
|  | ||||
| class SpyServerClient { | ||||
| public: | ||||
|     SpyServerClient(); | ||||
|         void startStream(); | ||||
|         void stopStream(); | ||||
|  | ||||
|     bool connectToSpyserver(char* host, int port); | ||||
|     bool disconnect(); | ||||
|         void setSetting(uint32_t setting, uint32_t arg); | ||||
|  | ||||
|     void start(); | ||||
|     void stop(); | ||||
|         void close(); | ||||
|         bool isOpen(); | ||||
|  | ||||
|     void setSampleRate(uint32_t setSampleRate); | ||||
|         SpyServerDeviceInfo devInfo; | ||||
|  | ||||
|     void tune(uint32_t freq); | ||||
|     private: | ||||
|         void sendCommand(uint32_t command, void* data, int len); | ||||
|         void sendHandshake(std::string appName); | ||||
|  | ||||
|     dsp::stream<dsp::complex_t> iqStream; | ||||
|         int readSize(int count, uint8_t* buffer); | ||||
|  | ||||
| private: | ||||
|     int receive(char* buf, int count); | ||||
|     int receiveSync(char* buf, int count); | ||||
|     int checkError(int err, int expected); | ||||
|     void worker(); | ||||
|         static void dataHandler(int count, uint8_t* buf, void* ctx); | ||||
|  | ||||
|     void sendCommand(uint32_t cmd, void* body, size_t bodySize); | ||||
|     void setSetting(uint32_t setting, uint32_t value); | ||||
|         net::Conn client; | ||||
|  | ||||
|     void hello(); | ||||
|         uint8_t* readBuf; | ||||
|         uint8_t* writeBuf; | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|     SOCKET sock; | ||||
| #else | ||||
|     int sockfd; | ||||
| #endif | ||||
|     bool connected = false; | ||||
|     bool waiting = false; | ||||
|         bool deviceInfoAvailable = false; | ||||
|         std::mutex deviceInfoMtx; | ||||
|         std::condition_variable deviceInfoCnd; | ||||
|  | ||||
|     std::thread workerThread; | ||||
|         SpyServerMessageHeader receivedHeader; | ||||
|  | ||||
| }; | ||||
|         dsp::stream<dsp::complex_t>* output; | ||||
|  | ||||
|     }; | ||||
|      | ||||
|     typedef std::unique_ptr<SpyServerClientClass> SpyServerClient; | ||||
|  | ||||
|     SpyServerClient connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out); | ||||
|      | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,10 @@ | ||||
| /* | ||||
|  | ||||
| SPY Server protocol structures and constants | ||||
| Copyright (C) 2017 Youssef Touil youssef@live.com | ||||
|  | ||||
| Corrections by Ryzerth. | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
| @@ -19,170 +23,126 @@ Copyright (C) 2017 Youssef Touil youssef@live.com | ||||
| #define SPYSERVER_MIN_FFT_DB_RANGE (10) | ||||
| #define SPYSERVER_MAX_FFT_DB_OFFSET (100) | ||||
|  | ||||
| enum DeviceType | ||||
| { | ||||
|   DEVICE_INVALID        = 0, | ||||
|   DEVICE_AIRSPY_ONE     = 1, | ||||
|   DEVICE_AIRSPY_HF      = 2, | ||||
|   DEVICE_RTLSDR         = 3, | ||||
| enum SpyServerDeviceType { | ||||
|     SPYSERVER_DEVICE_INVALID        = 0, | ||||
|     SPYSERVER_DEVICE_AIRSPY_ONE     = 1, | ||||
|     SPYSERVER_DEVICE_AIRSPY_HF      = 2, | ||||
|     SPYSERVER_DEVICE_RTLSDR         = 3, | ||||
| }; | ||||
|  | ||||
| enum CommandType | ||||
| { | ||||
|   CMD_HELLO             = 0, | ||||
|   CMD_GET_SETTING       = 1, | ||||
|   CMD_SET_SETTING       = 2, | ||||
|   CMD_PING              = 3, | ||||
| enum SpyServerCommandType { | ||||
|     SPYSERVER_CMD_HELLO             = 0, | ||||
|     SPYSERVER_CMD_SET_SETTING       = 2, | ||||
|     SPYSERVER_CMD_PING              = 3, | ||||
| }; | ||||
|  | ||||
| enum SettingType | ||||
| { | ||||
|   SETTING_STREAMING_MODE        = 0, | ||||
|   SETTING_STREAMING_ENABLED     = 1, | ||||
|   SETTING_GAIN                  = 2, | ||||
| enum SpyServerSettingType { | ||||
|     SPYSERVER_SETTING_STREAMING_MODE        = 0, | ||||
|     SPYSERVER_SETTING_STREAMING_ENABLED     = 1, | ||||
|     SPYSERVER_SETTING_GAIN                  = 2, | ||||
|  | ||||
|   SETTING_IQ_FORMAT             = 100,    // 0x64 | ||||
|   SETTING_IQ_FREQUENCY          = 101,    // 0x65 | ||||
|   SETTING_IQ_DECIMATION         = 102,    // 0x66 | ||||
|   SETTING_IQ_DIGITAL_GAIN       = 103,    // 0x67 | ||||
|     SPYSERVER_SETTING_IQ_FORMAT             = 100,    // 0x64 | ||||
|     SPYSERVER_SETTING_IQ_FREQUENCY          = 101,    // 0x65 | ||||
|     SPYSERVER_SETTING_IQ_DECIMATION         = 102,    // 0x66 | ||||
|     SPYSERVER_SETTING_IQ_DIGITAL_GAIN       = 103,    // 0x67 | ||||
|  | ||||
|   SETTING_FFT_FORMAT            = 200,    // 0xc8 | ||||
|   SETTING_FFT_FREQUENCY         = 201,    // 0xc9 | ||||
|   SETTING_FFT_DECIMATION        = 202,    // 0xca | ||||
|   SETTING_FFT_DB_OFFSET         = 203,    // 0xcb | ||||
|   SETTING_FFT_DB_RANGE          = 204,    // 0xcc | ||||
|   SETTING_FFT_DISPLAY_PIXELS    = 205,    // 0xcd | ||||
|     SPYSERVER_SETTING_FFT_FORMAT            = 200,    // 0xc8 | ||||
|     SPYSERVER_SETTING_FFT_FREQUENCY         = 201,    // 0xc9 | ||||
|     SPYSERVER_SETTING_FFT_DECIMATION        = 202,    // 0xca | ||||
|     SPYSERVER_SETTING_FFT_DB_OFFSET         = 203,    // 0xcb | ||||
|     SPYSERVER_SETTING_FFT_DB_RANGE          = 204,    // 0xcc | ||||
|     SPYSERVER_SETTING_FFT_DISPLAY_PIXELS    = 205,    // 0xcd | ||||
| }; | ||||
|  | ||||
| enum StreamType | ||||
| { | ||||
|   STREAM_TYPE_STATUS    = 0, | ||||
|   STREAM_TYPE_IQ        = 1, | ||||
|   STREAM_TYPE_AF        = 2, | ||||
|   STREAM_TYPE_FFT       = 4, | ||||
| enum SpyServerStreamType { | ||||
|     SPYSERVER_STREAM_TYPE_STATUS    = 0, | ||||
|     SPYSERVER_STREAM_TYPE_IQ        = 1, | ||||
|     SPYSERVER_STREAM_TYPE_AF        = 2, | ||||
|     SPYSERVER_STREAM_TYPE_FFT       = 4, | ||||
| }; | ||||
|  | ||||
|  | ||||
| enum StreamingMode | ||||
| { | ||||
|   STREAM_MODE_IQ_ONLY       = STREAM_TYPE_IQ,                     // 0x01 | ||||
|   STREAM_MODE_AF_ONLY       = STREAM_TYPE_AF,                     // 0x02 | ||||
|   STREAM_MODE_FFT_ONLY      = STREAM_TYPE_FFT,                    // 0x04 | ||||
|   STREAM_MODE_FFT_IQ        = STREAM_TYPE_FFT | STREAM_TYPE_IQ,   // 0x05 | ||||
|   STREAM_MODE_FFT_AF        = STREAM_TYPE_FFT | STREAM_TYPE_AF,   // 0x06 | ||||
| enum SpyServerStreamingMode { | ||||
|     SPYSERVER_STREAM_MODE_IQ_ONLY       = SPYSERVER_STREAM_TYPE_IQ,                     // 0x01 | ||||
|     SPYSERVER_STREAM_MODE_AF_ONLY       = SPYSERVER_STREAM_TYPE_AF,                     // 0x02 | ||||
|     SPYSERVER_STREAM_MODE_FFT_ONLY      = SPYSERVER_STREAM_TYPE_FFT,                    // 0x04 | ||||
|     SPYSERVER_STREAM_MODE_FFT_IQ        = SPYSERVER_STREAM_TYPE_FFT | SPYSERVER_STREAM_TYPE_IQ,   // 0x05 | ||||
|     SPYSERVER_STREAM_MODE_FFT_AF        = SPYSERVER_STREAM_TYPE_FFT | SPYSERVER_STREAM_TYPE_AF,   // 0x06 | ||||
| }; | ||||
|  | ||||
| enum StreamFormat | ||||
| { | ||||
|   STREAM_FORMAT_INVALID     = 0, | ||||
|   STREAM_FORMAT_UINT8       = 1, | ||||
|   STREAM_FORMAT_INT16       = 2, | ||||
|   STREAM_FORMAT_INT24       = 3, | ||||
|   STREAM_FORMAT_FLOAT       = 4, | ||||
|   STREAM_FORMAT_DINT4       = 5, | ||||
| enum SpyServerStreamFormat { | ||||
|     SPYSERVER_STREAM_FORMAT_INVALID     = 0, | ||||
|     SPYSERVER_STREAM_FORMAT_UINT8       = 1, | ||||
|     SPYSERVER_STREAM_FORMAT_INT16       = 2, | ||||
|     SPYSERVER_STREAM_FORMAT_INT24       = 3, | ||||
|     SPYSERVER_STREAM_FORMAT_FLOAT       = 4, | ||||
|     SPYSERVER_STREAM_FORMAT_DINT4       = 5, | ||||
| }; | ||||
|  | ||||
| enum MessageType | ||||
| { | ||||
|   MSG_TYPE_DEVICE_INFO      = 0, | ||||
|   MSG_TYPE_CLIENT_SYNC      = 1, | ||||
|   MSG_TYPE_PONG             = 2, | ||||
|   MSG_TYPE_READ_SETTING     = 3, | ||||
| enum SpyServerMessageType { | ||||
|     SPYSERVER_MSG_TYPE_DEVICE_INFO      = 0, | ||||
|     SPYSERVER_MSG_TYPE_CLIENT_SYNC      = 1, | ||||
|     SPYSERVER_MSG_TYPE_PONG             = 2, | ||||
|     SPYSERVER_MSG_TYPE_READ_SETTING     = 3, | ||||
|  | ||||
|   MSG_TYPE_UINT8_IQ         = 100,     // 0x64 | ||||
|   MSG_TYPE_INT16_IQ         = 101,     // 0x65 | ||||
|   MSG_TYPE_INT24_IQ         = 102,     // 0x66 | ||||
|   MSG_TYPE_FLOAT_IQ         = 103,     // 0x67 | ||||
|     SPYSERVER_MSG_TYPE_UINT8_IQ         = 100,     // 0x64 | ||||
|     SPYSERVER_MSG_TYPE_INT16_IQ         = 101,     // 0x65 | ||||
|     SPYSERVER_MSG_TYPE_INT24_IQ         = 102,     // 0x66 | ||||
|     SPYSERVER_MSG_TYPE_FLOAT_IQ         = 103,     // 0x67 | ||||
|  | ||||
|   MSG_TYPE_UINT8_AF         = 200,    // 0xc8 | ||||
|   MSG_TYPE_INT16_AF         = 201,    // 0xc9 | ||||
|   MSG_TYPE_INT24_AF         = 202,    // 0xca | ||||
|   MSG_TYPE_FLOAT_AF         = 203,    // 0xcb | ||||
|     SPYSERVER_MSG_TYPE_UINT8_AF         = 200,    // 0xc8 | ||||
|     SPYSERVER_MSG_TYPE_INT16_AF         = 201,    // 0xc9 | ||||
|     SPYSERVER_MSG_TYPE_INT24_AF         = 202,    // 0xca | ||||
|     SPYSERVER_MSG_TYPE_FLOAT_AF         = 203,    // 0xcb | ||||
|  | ||||
|   MSG_TYPE_DINT4_FFT        = 300,    //0x12C | ||||
|   MSG_TYPE_UINT8_FFT        = 301,    //0x12D | ||||
|     SPYSERVER_MSG_TYPE_DINT4_FFT        = 300,    //0x12C | ||||
|     SPYSERVER_MSG_TYPE_UINT8_FFT        = 301,    //0x12D | ||||
| }; | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #define __attribute__(x) | ||||
| #pragma pack(push, 1) | ||||
| #endif | ||||
| struct SpyServerClientHandshake { | ||||
|     uint32_t ProtocolVersion; | ||||
| }; | ||||
|  | ||||
| struct ClientHandshake | ||||
| { | ||||
|   uint32_t ProtocolVersion; | ||||
|   // SDR# doesn't seem to send this | ||||
|   //uint32_t ClientNameLength; | ||||
| }__attribute__((packed)); | ||||
| struct SpyServerCommandHeader { | ||||
|     uint32_t CommandType; | ||||
|     uint32_t BodySize; | ||||
| }; | ||||
|  | ||||
| struct CommandHeader | ||||
| { | ||||
|   uint32_t CommandType; | ||||
|   uint32_t BodySize; | ||||
| }__attribute__((packed)); | ||||
| struct SpyServerSettingTarget { | ||||
|     uint32_t Setting; | ||||
|     uint32_t Value; | ||||
| }; | ||||
|  | ||||
| struct SettingTarget | ||||
| { | ||||
|   // Again, not used | ||||
|   //uint32_t StreamType; | ||||
|   uint32_t SettingType; | ||||
| }__attribute__((packed)); | ||||
| struct SpyServerMessageHeader { | ||||
|     uint32_t ProtocolID; | ||||
|     uint32_t MessageType; | ||||
|     uint32_t StreamType; | ||||
|     uint32_t SequenceNumber; | ||||
|     uint32_t BodySize; | ||||
| }; | ||||
|  | ||||
| struct MessageHeader | ||||
| { | ||||
|   uint32_t ProtocolID; | ||||
|   uint32_t MessageType; | ||||
|   uint32_t StreamType; | ||||
|   uint32_t SequenceNumber; | ||||
|   uint32_t BodySize; | ||||
| }__attribute__((packed)); | ||||
| struct SpyServerDeviceInfo { | ||||
|     uint32_t DeviceType; | ||||
|     uint32_t DeviceSerial; | ||||
|     uint32_t MaximumSampleRate; | ||||
|     uint32_t MaximumBandwidth; | ||||
|     uint32_t DecimationStageCount; | ||||
|     uint32_t GainStageCount; | ||||
|     uint32_t MaximumGainIndex; | ||||
|     uint32_t MinimumFrequency; | ||||
|     uint32_t MaximumFrequency; | ||||
|     uint32_t Resolution; | ||||
|     uint32_t MinimumIQDecimation; | ||||
|     uint32_t ForcedIQFormat; | ||||
| }; | ||||
|  | ||||
| struct DeviceInfo | ||||
| { | ||||
|   uint32_t DeviceType; | ||||
|   uint32_t DeviceSerial; | ||||
|   uint32_t MaximumSampleRate; | ||||
|   uint32_t MaximumBandwidth; | ||||
|   uint32_t DecimationStageCount; | ||||
|   uint32_t GainStageCount; | ||||
|   uint32_t MaximumGainIndex; | ||||
|   uint32_t MinimumFrequency; | ||||
|   uint32_t MaximumFrequency; | ||||
|   uint32_t Resolution; | ||||
|   uint32_t MinimumIQDecimation; | ||||
|   uint32_t ForcedIQFormat; | ||||
| }__attribute__((packed)); | ||||
|  | ||||
| struct ClientSync | ||||
| { | ||||
|   uint32_t CanControl; | ||||
|   uint32_t Gain; | ||||
|   uint32_t DeviceCenterFrequency; | ||||
|   uint32_t IQCenterFrequency; | ||||
|   uint32_t FFTCenterFrequency; | ||||
|   uint32_t MinimumIQCenterFrequency; | ||||
|   uint32_t MaximumIQCenterFrequency; | ||||
|   uint32_t MinimumFFTCenterFrequency; | ||||
|   uint32_t MaximumFFTCenterFrequency; | ||||
| }__attribute__((packed)); | ||||
|  | ||||
| struct ComplexInt16 | ||||
| { | ||||
|   int16_t real; | ||||
|   int16_t imag; | ||||
| }__attribute__((packed)); | ||||
|  | ||||
| struct ComplexUInt8 | ||||
| { | ||||
|   uint8_t real; | ||||
|   uint8_t imag; | ||||
| }__attribute__((packed)); | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #pragma pack(pop) | ||||
| #endif | ||||
|  | ||||
| enum ParserPhase { | ||||
|   AcquiringHeader, | ||||
|   ReadingData | ||||
| struct SpyServerClientSync { | ||||
|     uint32_t CanControl; | ||||
|     uint32_t Gain; | ||||
|     uint32_t DeviceCenterFrequency; | ||||
|     uint32_t IQCenterFrequency; | ||||
|     uint32_t FFTCenterFrequency; | ||||
|     uint32_t MinimumIQCenterFrequency; | ||||
|     uint32_t MaximumIQCenterFrequency; | ||||
|     uint32_t MinimumFFTCenterFrequency; | ||||
|     uint32_t MaximumFFTCenterFrequency; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user