From 79e2747aedf8add8f1bb433b4e8ea1c0e4ba1ca3 Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Mon, 19 Jul 2021 04:52:13 +0200 Subject: [PATCH] New spyserver source --- CMakeLists.txt | 4 +- core/src/core.cpp | 2 + core/src/gui/main_window.cpp | 3 + core/src/gui/main_window.h | 1 + readme.md | 2 +- spyserver_source/src/main.cpp | 289 ++++++++++++++----- spyserver_source/src/spyserver_client.cpp | 329 +++++++--------------- spyserver_source/src/spyserver_client.h | 85 +++--- spyserver_source/src/spyserver_protocol.h | 244 +++++++--------- 9 files changed, 470 insertions(+), 489 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 492f37dd..32477221 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 \"$/libsdrpp_core.dylib\" \"$\") 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) diff --git a/core/src/core.cpp b/core/src/core.cpp index 3e76370c..7e7cf17b 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -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; diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 2fd7b692..060e578b 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -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(); diff --git a/core/src/gui/main_window.h b/core/src/gui/main_window.h index 39727b71..ec569c08 100644 --- a/core/src/gui/main_window.h +++ b/core/src/gui/main_window.h @@ -32,6 +32,7 @@ public: bool isPlaying(); bool lockWaterfallControls = false; + bool playButtonLocked = false; Event onPlayStateChange; Event onInitComplete; diff --git a/readme.md b/readme.md index c23ad8d8..f4169e64 100644 --- a/readme.md +++ b/readme.md @@ -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 diff --git a/spyserver_source/src/main.cpp b/spyserver_source/src/main.cpp index 7611c74e..45e1d983 100644 --- a/spyserver_source/src/main.cpp +++ b/spyserver_source/src/main.cpp @@ -6,23 +6,50 @@ #include #include #include +#include +#include +#include +#include + #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(_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(_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 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 sampleRates; + std::string sampleRatesTxt; + int gain = 0; - bool rtlAGC = false; - bool tunerAGC = false; - int directSamplingMode = 0; + + std::string devRef = ""; + + dsp::stream 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(); } \ No newline at end of file diff --git a/spyserver_source/src/spyserver_client.cpp b/spyserver_source/src/spyserver_client.cpp index a58175f3..c8f003a5 100644 --- a/spyserver_source/src/spyserver_client.cpp +++ b/spyserver_source/src/spyserver_client.cpp @@ -1,254 +1,139 @@ #include -#include +#include -SpyServerClient::SpyServerClient() { +using namespace std::chrono_literals; -} +namespace spyserver { + SpyServerClientClass::SpyServerClientClass(net::Conn conn, dsp::stream* 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* out) { + net::Conn conn = net::connect(net::PROTO_TCP, host, port); + if (!conn) { + return NULL; + } + return SpyServerClient(new SpyServerClientClass(std::move(conn), out)); + } } \ No newline at end of file diff --git a/spyserver_source/src/spyserver_client.h b/spyserver_source/src/spyserver_client.h index 23f7fde6..002a53c8 100644 --- a/spyserver_source/src/spyserver_client.h +++ b/spyserver_source/src/spyserver_client.h @@ -1,69 +1,52 @@ #pragma once +#include #include #include #include -#include -#include -#ifdef _WIN32 -#include -#include -#else -#include -#include -#include -#include -#include -#include -#endif +namespace spyserver { + class SpyServerClientClass { + public: + SpyServerClientClass(net::Conn conn, dsp::stream* 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 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; -}; \ No newline at end of file + dsp::stream* output; + + }; + + typedef std::unique_ptr SpyServerClient; + + SpyServerClient connect(std::string host, uint16_t port, dsp::stream* out); + +} diff --git a/spyserver_source/src/spyserver_protocol.h b/spyserver_source/src/spyserver_protocol.h index de9ca6cb..debacc60 100644 --- a/spyserver_source/src/spyserver_protocol.h +++ b/spyserver_source/src/spyserver_protocol.h @@ -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; }; \ No newline at end of file