mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 17:08:13 +01:00 
			
		
		
		
	More work on RFSpace source
This commit is contained in:
		| @@ -18,7 +18,7 @@ option(OPT_BUILD_FILE_SOURCE "Wav file source" ON) | ||||
| option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON) | ||||
| option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF) | ||||
| option(OPT_BUILD_SDDC_SOURCE "Build SDDC Source Module (Dependencies: libusb-1.0)" OFF) | ||||
| option(OPT_BUILD_RFSPACE_SOURCE "Build RFspace Source Module no dependencies required)" OFF) | ||||
| option(OPT_BUILD_RFSPACE_SOURCE "Build RFspace Source Module (no dependencies required)" ON) | ||||
| option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Dependencies: librtlsdr)" ON) | ||||
| option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON) | ||||
| option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Dependencies: libsdrplay)" OFF) | ||||
|   | ||||
| @@ -111,7 +111,7 @@ private: | ||||
|         txt = _txt.c_str(); | ||||
|     } | ||||
|  | ||||
|     std::vector<std::string> keys; | ||||
|     std::vector<K> keys; | ||||
|     std::vector<std::string> names; | ||||
|     std::vector<T> values; | ||||
|     std::string _txt; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #include <config.h> | ||||
| #include <options.h> | ||||
| #include <gui/widgets/stepped_slider.h> | ||||
|  | ||||
| #include <utils/optionlist.h> | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| @@ -23,9 +23,9 @@ SDRPP_MOD_INFO{ | ||||
|  | ||||
| ConfigManager config; | ||||
|  | ||||
| class SpyServerSourceModule : public ModuleManager::Instance { | ||||
| class RFSpaceSource : public ModuleManager::Instance { | ||||
| public: | ||||
|     SpyServerSourceModule(std::string name) { | ||||
|     RFSpaceSource(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         handler.ctx = this; | ||||
| @@ -42,7 +42,7 @@ public: | ||||
|         sigpath::sourceManager.registerSource("RFspace", &handler); | ||||
|     } | ||||
|  | ||||
|     ~SpyServerSourceModule() { | ||||
|     ~RFSpaceSource() { | ||||
|         stop(this); | ||||
|         sigpath::sourceManager.unregisterSource("RFspace"); | ||||
|     } | ||||
| @@ -77,49 +77,50 @@ private: | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         RFSpaceSource* _this = (RFSpaceSource*)ctx; | ||||
|         core::setInputSampleRate(_this->sampleRate); | ||||
|         gui::mainWindow.playButtonLocked = !(_this->client && _this->client->isOpen()); | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Menu Select!", _this->name); | ||||
|         spdlog::info("RFSpaceSource '{0}': Menu Select!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void menuDeselected(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         RFSpaceSource* _this = (RFSpaceSource*)ctx; | ||||
|         gui::mainWindow.playButtonLocked = false; | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Menu Deselect!", _this->name); | ||||
|         spdlog::info("RFSpaceSource '{0}': Menu Deselect!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void start(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         RFSpaceSource* _this = (RFSpaceSource*)ctx; | ||||
|         if (_this->running) { return; } | ||||
|  | ||||
|         // TODO: Start | ||||
|         // TODO: Set configuration here | ||||
|         if (_this->client) { _this->client->start(rfspace::RFSPACE_SAMP_FORMAT_COMPLEX, rfspace::RFSPACE_SAMP_FORMAT_16BIT); } | ||||
|  | ||||
|         _this->running = true; | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Start!", _this->name); | ||||
|         spdlog::info("RFSpaceSource '{0}': Start!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void stop(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         RFSpaceSource* _this = (RFSpaceSource*)ctx; | ||||
|         if (!_this->running) { return; } | ||||
|  | ||||
|         // TODO: Stop | ||||
|         if (_this->client) { _this->client->stop(); } | ||||
|  | ||||
|         _this->running = false; | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Stop!", _this->name); | ||||
|         spdlog::info("RFSpaceSource '{0}': Stop!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             // TODO: Tune | ||||
|         RFSpaceSource* _this = (RFSpaceSource*)ctx; | ||||
|         if (_this->running && _this->client) { | ||||
|             _this->client->setFrequency(freq); | ||||
|         } | ||||
|         _this->freq = freq; | ||||
|         spdlog::info("SpyServerSourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
|         spdlog::info("RFSpaceSource '{0}': Tune: {1}!", _this->name, freq); | ||||
|     } | ||||
|  | ||||
|     static void menuHandler(void* ctx) { | ||||
|         SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; | ||||
|         RFSpaceSource* _this = (RFSpaceSource*)ctx; | ||||
|         float menuWidth = ImGui::GetContentRegionAvailWidth(); | ||||
|  | ||||
|         bool connected = (_this->client && _this->client->isOpen()); | ||||
| @@ -143,7 +144,9 @@ private: | ||||
|         if (_this->running) { style::beginDisabled(); } | ||||
|         if (!connected && ImGui::Button("Connect##rfspace_source", ImVec2(menuWidth, 0))) { | ||||
|             try { | ||||
|                 if (_this->client) { _this->client.reset(); } | ||||
|                 _this->client = rfspace::connect(_this->hostname, _this->port, &_this->stream); | ||||
|                 _this->deviceInit(); | ||||
|             } | ||||
|             catch (std::exception e) { | ||||
|                 spdlog::error("Could not connect to SDR: {0}", e.what()); | ||||
| @@ -158,9 +161,36 @@ private: | ||||
|         if (connected) { | ||||
|             // TODO: Options here | ||||
|  | ||||
|             if (_this->running) { style::beginDisabled(); } | ||||
|  | ||||
|             ImGui::LeftLabel("Samplerate"); | ||||
|             ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|             if (ImGui::Combo("##rfspace_source_samp_rate", &_this->srId, _this->sampleRates.txt)) { | ||||
|                 _this->sampleRate = _this->sampleRates[_this->srId]; | ||||
|                 _this->client->setSampleRate(_this->sampleRate); | ||||
|                 core::setInputSampleRate(_this->sampleRate); | ||||
|                 // TODO: Save config | ||||
|             } | ||||
|  | ||||
|             if (_this->running) { style::endDisabled(); } | ||||
|  | ||||
|             ImGui::LeftLabel("Antenna Port"); | ||||
|             ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|             if (ImGui::Combo("##rfspace_source_rf_port", &_this->rfPortId, _this->rfPorts.txt)) { | ||||
|                 _this->client->setPort(_this->rfPorts[_this->rfPortId]); | ||||
|                 // TODO: Save config | ||||
|             } | ||||
|  | ||||
|             ImGui::LeftLabel("Gain"); | ||||
|             ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|             if (ImGui::SliderFloatWithSteps("##rfspace_source_gain", &_this->gain, -30, 0, 10, "%.0f dB")) { | ||||
|                 _this->client->setGain(_this->gain); | ||||
|                 // TODO: Save config (as int, in case we have to switch to an option list) | ||||
|             } | ||||
|  | ||||
|             ImGui::Text("Status:"); | ||||
|             ImGui::SameLine(); | ||||
|             ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Connected (DEV NAME HERE)"); | ||||
|             ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Connected (%s)", _this->deviceName.c_str()); | ||||
|         } | ||||
|         else { | ||||
|             ImGui::Text("Status:"); | ||||
| @@ -169,18 +199,53 @@ private: | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void deviceInit() { | ||||
|         // Get device name | ||||
|         if (deviceNames.find(client->deviceId) != deviceNames.end()) { | ||||
|             deviceName = deviceNames[client->deviceId]; | ||||
|         } | ||||
|         else { | ||||
|             deviceName = "Unknown"; | ||||
|         } | ||||
|          | ||||
|         // Create samplerate list (TODO: Depends on model) | ||||
|         auto srs = client->getValidSampleRates(); | ||||
|         sampleRates.clear(); | ||||
|         for (auto& sr : srs) { | ||||
|             sampleRates.define(sr, getBandwdithScaled(sr), sr); | ||||
|         } | ||||
|          | ||||
|         // Create RF port list (TODO: Depends on model) | ||||
|         rfPorts.clear(); | ||||
|         rfPorts.define("Port 1", rfspace::RFSPACE_RF_PORT_1); | ||||
|         rfPorts.define("Port 2", rfspace::RFSPACE_RF_PORT_2); | ||||
|  | ||||
|         // TODO: Load config | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|     bool running = false; | ||||
|     double sampleRate = 1000000; | ||||
|     double sampleRate = 1228800; | ||||
|     double freq; | ||||
|  | ||||
|     OptionList<uint32_t, uint32_t> sampleRates; | ||||
|     int srId = 0; | ||||
|  | ||||
|     OptionList<std::string, rfspace::RFPort> rfPorts; | ||||
|     int rfPortId = 0; | ||||
|  | ||||
|     float gain = 0; | ||||
|  | ||||
|     char hostname[1024]; | ||||
|     int port = 50000; | ||||
|  | ||||
|     int srId = 0; | ||||
|     std::vector<double> sampleRates; | ||||
|     std::string sampleRatesTxt; | ||||
|     std::string deviceName = "Unknown"; | ||||
|     std::map<rfspace::DeviceID, std::string> deviceNames = { | ||||
|         { rfspace::RFSPACE_DEV_ID_CLOUD_SDR, "CloudSDR" }, | ||||
|         { rfspace::RFSPACE_DEV_ID_CLOUD_IQ, "CloudIQ" }, | ||||
|         { rfspace::RFSPACE_DEV_ID_NET_SDR, "NetSDR" } | ||||
|     }; | ||||
|  | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
|     SourceManager::SourceHandler handler; | ||||
| @@ -208,11 +273,11 @@ MOD_EXPORT void _INIT_() { | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new SpyServerSourceModule(name); | ||||
|     return new RFSpaceSource(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { | ||||
|     delete (SpyServerSourceModule*)instance; | ||||
|     delete (RFSpaceSource*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|   | ||||
| @@ -10,22 +10,40 @@ namespace rfspace { | ||||
|         client = std::move(conn); | ||||
|         udpClient = std::move(udpConn); | ||||
|         output = out; | ||||
|  | ||||
|         // Allocate buffers | ||||
|         rbuffer = new uint8_t[RFSPACE_MAX_SIZE]; | ||||
|         sbuffer = new uint8_t[RFSPACE_MAX_SIZE]; | ||||
|         ubuffer = new uint8_t[RFSPACE_MAX_SIZE]; | ||||
|  | ||||
|         // Clear write stop of stream just in case | ||||
|         output->clearWriteStop(); | ||||
|  | ||||
|         uint8_t test = 0x5A; | ||||
|         udpClient->write(1, &test); | ||||
|         // Send UDP packet so that a router opens the port | ||||
|         sendDummyUDP(); | ||||
|  | ||||
|         // Start readers | ||||
|         client->readAsync(sizeof(tcpHeader), (uint8_t*)&tcpHeader, tcpHandler, this); | ||||
|         udpClient->readAsync(sizeof(udpHeader), (uint8_t*)&udpHeader, udpHandler, this); | ||||
|         udpClient->readAsync(RFSPACE_MAX_SIZE, ubuffer, udpHandler, this); | ||||
|  | ||||
|         // Start SDR | ||||
|         uint8_t args[4] = { 0x70, 2, 0, 0 }; | ||||
|         setControlItem(0x18, args, 4); | ||||
|         // Get device ID and wait for response | ||||
|         getControlItem(RFSPACE_CTRL_ITEM_PROD_ID, NULL, 0); | ||||
|         { | ||||
|             std::unique_lock<std::mutex> lck(devIdMtx); | ||||
|             if (!devIdCnd.wait_for(lck, std::chrono::milliseconds(RFSPACE_TIMEOUT_MS), [=](){ return devIdAvailable; })) { | ||||
|                 throw std::runtime_error("Could not identify remote device"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Default configuration | ||||
|         stop(); | ||||
|         setSampleRate(1228800); | ||||
|         setFrequency(8830000); | ||||
|         setGain(0); | ||||
|         setPort(RFSPACE_RF_PORT_1); | ||||
|  | ||||
|         // Start heartbeat | ||||
|         heartBeatThread = std::thread(&RFspaceClientClass::heartBeatWorker, this); | ||||
|     } | ||||
|  | ||||
|     RFspaceClientClass::~RFspaceClientClass() { | ||||
| @@ -35,7 +53,12 @@ namespace rfspace { | ||||
|         delete[] ubuffer; | ||||
|     } | ||||
|  | ||||
|     int RFspaceClientClass::getControlItem(uint16_t item, void* param, int len) { | ||||
|     void RFspaceClientClass::sendDummyUDP() { | ||||
|         uint8_t dummy = 0x5A; | ||||
|         udpClient->write(1, &dummy); | ||||
|     } | ||||
|  | ||||
|     int RFspaceClientClass::getControlItem(ControlItem item, void* param, int len) { | ||||
|         // Build packet | ||||
|         uint16_t* header = (uint16_t*)&sbuffer[0]; | ||||
|         uint16_t* item_val = (uint16_t*)&sbuffer[2]; | ||||
| @@ -43,26 +66,12 @@ namespace rfspace { | ||||
|         *item_val = item; | ||||
|  | ||||
|         // Send packet | ||||
|         SCIRRecv.release(); | ||||
|         client->write(4, sbuffer); | ||||
|  | ||||
|         // Wait for a response | ||||
|         if (!SCIRRecv.await(2000)) { | ||||
|             SCIRRecv.release(); | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         // Forward data | ||||
|         int toRead = ((SCIRSize - 4) < len) ? (SCIRSize - 4) : len; | ||||
|         memcpy(param, &rbuffer[4], toRead); | ||||
|  | ||||
|         // Release receiving thread | ||||
|         SCIRRecv.release(); | ||||
|  | ||||
|         return toRead; | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::setControlItem(uint16_t item, void* param, int len) { | ||||
|     void RFspaceClientClass::setControlItem(ControlItem item, void* param, int len) { | ||||
|         // Build packet | ||||
|         uint16_t* header = (uint16_t*)&sbuffer[0]; | ||||
|         uint16_t* item_val = (uint16_t*)&sbuffer[2]; | ||||
| @@ -74,10 +83,78 @@ namespace rfspace { | ||||
|         client->write(len + 4, sbuffer); | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::setControlItemWithChanID(ControlItem item, uint8_t chanId, void* param, int len) { | ||||
|         // Build packet | ||||
|         uint16_t* header = (uint16_t*)&sbuffer[0]; | ||||
|         uint16_t* item_val = (uint16_t*)&sbuffer[2]; | ||||
|         uint8_t* chan = &sbuffer[4]; | ||||
|         *header = (len + 5) | (RFSPACE_MSG_TYPE_H2T_SET_CTRL_ITEM << 13); | ||||
|         *item_val = item; | ||||
|         *chan = chanId; | ||||
|         memcpy(&sbuffer[5], param, len); | ||||
|  | ||||
|         // Send packet | ||||
|         client->write(len + 5, sbuffer); | ||||
|     } | ||||
|  | ||||
|     std::vector<uint32_t> RFspaceClientClass::getValidSampleRates() { | ||||
|         std::vector<uint32_t> sr; | ||||
|  | ||||
|         switch (deviceId) { | ||||
|         case RFSPACE_DEV_ID_CLOUD_SDR: | ||||
|         case RFSPACE_DEV_ID_CLOUD_IQ: | ||||
|             for (int n = 122880000 / (4 * 25); n >= 32000; n /= 2) { | ||||
|                 sr.push_back(n); | ||||
|             } | ||||
|             break; | ||||
|         case RFSPACE_DEV_ID_NET_SDR: | ||||
|             for (int n = 80000000 / (4 * 25); n >= 32000; n /= 2) { | ||||
|                 sr.push_back(n); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         return sr; | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::setFrequency(uint64_t freq) { | ||||
|         setControlItemWithChanID(RFSPACE_CTRL_ITEM_NCO_FREQUENCY, 0, &freq, 5); | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::setPort(RFPort port) { | ||||
|         uint8_t value = port; | ||||
|         spdlog::warn("{0}", value); | ||||
|         setControlItemWithChanID(RFSPACE_CTRL_ITEM_RF_PORT, 0, &value, sizeof(value)); | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::setGain(int8_t gain) { | ||||
|         setControlItemWithChanID(RFSPACE_CTRL_ITEM_RF_GAIN, 0, &gain, sizeof(gain)); | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::setSampleRate(uint32_t sampleRate) { | ||||
|         setControlItemWithChanID(RFSPACE_CTRL_ITEM_IQ_SAMP_RATE, 0, &sampleRate, sizeof(sampleRate)); | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::start(SampleFormat sampleFormat, SampleDepth sampleDepth) { | ||||
|         uint8_t args[4] = { sampleFormat, RFSPACE_STATE_RUN, sampleDepth, 0 }; | ||||
|         setControlItem(RFSPACE_CTRL_ITEM_STATE, args, sizeof(args)); | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::stop() { | ||||
|         uint8_t args[4] = { 0, RFSPACE_STATE_IDLE, 0, 0 }; | ||||
|         setControlItem(RFSPACE_CTRL_ITEM_STATE, args, sizeof(args)); | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::close() { | ||||
|         output->stopWriter(); | ||||
|         stopHeartBeat = true; | ||||
|         heartBeatCnd.notify_all(); | ||||
|         if (heartBeatThread.joinable()) { heartBeatThread.join(); } | ||||
|         client->close(); | ||||
|         udpClient->close(); | ||||
|         output->clearWriteStop(); | ||||
|     } | ||||
|  | ||||
|     bool RFspaceClientClass::isOpen() { | ||||
| @@ -94,12 +171,17 @@ namespace rfspace { | ||||
|             _this->client->read(size - 2, &_this->rbuffer[2]); | ||||
|         } | ||||
|  | ||||
|         spdlog::warn("TCP received: {0} {1}", type, size); | ||||
|         // spdlog::warn("TCP received: {0} {1}", type, size); | ||||
|  | ||||
|         // Process data | ||||
|         if (type == RFSPACE_MSG_TYPE_T2H_SET_CTRL_ITEM_RESP) { | ||||
|             _this->SCIRSize = size; | ||||
|             _this->SCIRRecv.trigger(); | ||||
|         // Check for a device ID | ||||
|         uint16_t* controlItem = (uint16_t*)&_this->rbuffer[2]; | ||||
|         if (type == RFSPACE_MSG_TYPE_T2H_SET_CTRL_ITEM_RESP && *controlItem == RFSPACE_CTRL_ITEM_PROD_ID) { | ||||
|             { | ||||
|                 std::lock_guard<std::mutex> lck(_this->devIdMtx); | ||||
|                 _this->deviceId = (DeviceID)*(uint32_t*)&_this->rbuffer[4]; | ||||
|                 _this->devIdAvailable = true; | ||||
|             } | ||||
|             _this->devIdCnd.notify_all(); | ||||
|         } | ||||
|  | ||||
|         // Restart an async read | ||||
| @@ -108,18 +190,31 @@ namespace rfspace { | ||||
|  | ||||
|     void RFspaceClientClass::udpHandler(int count, uint8_t* buf, void* ctx) { | ||||
|         RFspaceClientClass* _this = (RFspaceClientClass*)ctx; | ||||
|         uint8_t type = _this->udpHeader >> 13; | ||||
|         uint16_t size = _this->udpHeader & 0b1111111111111; | ||||
|         uint16_t hdr = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8); | ||||
|         uint8_t type = hdr >> 13; | ||||
|         uint16_t size = hdr & 0b1111111111111; | ||||
|  | ||||
|         spdlog::warn("UDP received: {0} {1}", type, size); | ||||
|  | ||||
|         // Read the rest of the data | ||||
|         if (size > 2) { | ||||
|             _this->client->read(size - 2, &_this->rbuffer[2]); | ||||
|         if (type == RFSPACE_MSG_TYPE_T2H_DATA_ITEM_0) { | ||||
|             int16_t* samples = (int16_t*)&buf[4]; | ||||
|             int sampCount = (size - 4) / (2 * sizeof(int16_t)); | ||||
|             volk_16i_s32f_convert_32f((float*)_this->output->writeBuf, samples, 32768.0f, sampCount * 2); | ||||
|             _this->output->swap(sampCount); | ||||
|         } | ||||
|  | ||||
|         // Restart an async read | ||||
|         _this->client->readAsync(sizeof(_this->udpHeader), (uint8_t*)&_this->udpHeader, udpHandler, _this); | ||||
|         _this->udpClient->readAsync(RFSPACE_MAX_SIZE, _this->ubuffer, udpHandler, _this); | ||||
|     } | ||||
|  | ||||
|     void RFspaceClientClass::heartBeatWorker() { | ||||
|         uint8_t dummy[4]; | ||||
|         while (true) { | ||||
|             getControlItem(RFSPACE_CTRL_ITEM_STATE, dummy, sizeof(dummy)); | ||||
|  | ||||
|             std::unique_lock<std::mutex> lck(heartBeatMtx); | ||||
|             bool to = heartBeatCnd.wait_for(lck, std::chrono::milliseconds(RFSPACE_HEARTBEAT_INTERVAL_MS), [=](){ return stopHeartBeat; }); | ||||
|              | ||||
|             if (to) { return; } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     RFspaceClient connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out) { | ||||
|   | ||||
| @@ -3,11 +3,14 @@ | ||||
| #include <dsp/stream.h> | ||||
| #include <dsp/types.h> | ||||
| #include <atomic> | ||||
| #include <queue> | ||||
|  | ||||
| #define RFSPACE_MAX_SIZE 8192 | ||||
| #define RFSPACE_MAX_SIZE                8192 | ||||
| #define RFSPACE_HEARTBEAT_INTERVAL_MS   1000 | ||||
| #define RFSPACE_TIMEOUT_MS              3000 | ||||
|  | ||||
| namespace rfspace { | ||||
|     enum { | ||||
|     enum H2TMessageType { | ||||
|         RFSPACE_MSG_TYPE_H2T_SET_CTRL_ITEM, | ||||
|         RFSPACE_MSG_TYPE_H2T_REQ_CTRL_ITEM, | ||||
|         RFSPACE_MSG_TYPE_H2T_REQ_CTRL_ITEM_RANGE, | ||||
| @@ -18,7 +21,7 @@ namespace rfspace { | ||||
|         RFSPACE_MSG_TYPE_H2T_DATA_ITEM_3, | ||||
|     }; | ||||
|  | ||||
|     enum { | ||||
|     enum T2HMessageType { | ||||
|         RFSPACE_MSG_TYPE_T2H_SET_CTRL_ITEM_RESP, | ||||
|         RFSPACE_MSG_TYPE_T2H_UNSOL_CTRL_ITEM, | ||||
|         RFSPACE_MSG_TYPE_T2H_REQ_CTRL_ITEM_RANGE_RESP, | ||||
| @@ -29,63 +32,67 @@ namespace rfspace { | ||||
|         RFSPACE_MSG_TYPE_T2H_DATA_ITEM_3, | ||||
|     }; | ||||
|  | ||||
|     class SyncEvent { | ||||
|     public: | ||||
|         bool await(int timeoutMS) { | ||||
|             // Mark as waiting | ||||
|             { | ||||
|                 std::lock_guard<std::mutex> lck(waitingmtx); | ||||
|                 waiting = true; | ||||
|             } | ||||
|     enum RFPort { | ||||
|         RFSPACE_RF_PORT_AUTO, | ||||
|         RFSPACE_RF_PORT_1, | ||||
|         RFSPACE_RF_PORT_2 | ||||
|     }; | ||||
|  | ||||
|             // Wait for data | ||||
|             std::unique_lock<std::mutex> lck(mtx); | ||||
|             return cnd.wait_for(lck, std::chrono::milliseconds(timeoutMS), [this]() { return triggered; }); | ||||
|     enum SampleFormat { | ||||
|         RFSPACE_SAMP_FORMAT_REAL    = 0x00, | ||||
|         RFSPACE_SAMP_FORMAT_COMPLEX = 0x80 | ||||
|     }; | ||||
|  | ||||
|             // Mark as not waiting | ||||
|             { | ||||
|                 std::lock_guard<std::mutex> lck(waitingmtx); | ||||
|                 waiting = false; | ||||
|             } | ||||
|         } | ||||
|     enum State { | ||||
|         RFSPACE_STATE_IDLE  = 1, | ||||
|         RFSPACE_STATE_RUN   = 2 | ||||
|     }; | ||||
|  | ||||
|         void release() { | ||||
|             // Mark as not waiting, and if last notify sender | ||||
|             { | ||||
|                 std::lock_guard<std::mutex> lck(mtx); | ||||
|                 triggered = false; | ||||
|             } | ||||
|             cnd.notify_all(); | ||||
|         } | ||||
|     enum SampleDepth { | ||||
|         RFSPACE_SAMP_FORMAT_16BIT   = 0x00, | ||||
|         RFSPACE_SAMP_FORMAT_24BIT   = 0x80 | ||||
|     }; | ||||
|  | ||||
|         void trigger() { | ||||
|             // Check if a waiter is waiting | ||||
|             { | ||||
|                 std::lock_guard<std::mutex> lck(waitingmtx); | ||||
|                 if (waiting <= 0) { return; } | ||||
|             } | ||||
|     enum DeviceID { | ||||
|         RFSPACE_DEV_ID_CLOUD_SDR    = 0x44534C43, | ||||
|         RFSPACE_DEV_ID_CLOUD_IQ     = 0x51494C43, | ||||
|         RFSPACE_DEV_ID_NET_SDR      = 0xDEADBEEF // TODO | ||||
|     }; | ||||
|  | ||||
|             // Notify waiters | ||||
|             { | ||||
|                 std::lock_guard<std::mutex> lck(mtx); | ||||
|                 triggered = true; | ||||
|             } | ||||
|             cnd.notify_all(); | ||||
|  | ||||
|             // Wait for waiter to handle | ||||
|             { | ||||
|                 std::unique_lock<std::mutex> lck(mtx); | ||||
|                 cnd.wait(lck, [this]() { return !triggered; }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         bool triggered; | ||||
|         std::mutex mtx; | ||||
|         std::condition_variable cnd; | ||||
|  | ||||
|         bool waiting; | ||||
|         std::mutex waitingmtx; | ||||
|     enum ControlItem { | ||||
|         RFSPACE_CTRL_ITEM_MODEL_NAME    = 0x0001, | ||||
|         RFSPACE_CTRL_ITEM_SERIAL        = 0x0002, | ||||
|         RFSPACE_CTRL_ITEM_IFACE_VER     = 0x0003, | ||||
|         RFSPACE_CTRL_ITEM_VERSION       = 0x0004, | ||||
|         RFSPACE_CTRL_ITEM_STATUS        = 0x0005, | ||||
|         RFSPACE_CTRL_ITEM_DEV_NAME      = 0x0008, | ||||
|         RFSPACE_CTRL_ITEM_PROD_ID       = 0x0009, | ||||
|         RFSPACE_CTRL_ITEM_OPTIONS       = 0x000A, | ||||
|         RFSPACE_CTRL_ITEM_SECURE_CODE   = 0x000B, | ||||
|         RFSPACE_CTRL_ITEM_FPGA_CONF     = 0x000C, | ||||
|         RFSPACE_CTRL_ITEM_STATE         = 0x0018, | ||||
|         RFSPACE_CTRL_ITEM_NCO_FREQUENCY = 0x0020, | ||||
|         RFSPACE_CTRL_ITEM_RF_PORT       = 0x0030, | ||||
|         RFSPACE_CTRL_ITEM_PORT_RANGE    = 0x0032, | ||||
|         RFSPACE_CTRL_ITEM_RF_GAIN       = 0x0038, | ||||
|         RFSPACE_CTRL_ITEM_DOWNCONV_GAIN = 0x003A, | ||||
|         RFSPACE_CTRL_ITEM_RF_FILTER     = 0x0044, | ||||
|         RFSPACE_CTRL_ITEM_AD_MODE       = 0x008A, | ||||
|         RFSPACE_CTRL_ITEM_SAMP_RATE_CAL = 0x00B0, | ||||
|         RFSPACE_CTRL_ITEM_TRIG_FREQ     = 0x00B2, | ||||
|         RFSPACE_CTRL_ITEM_TRIG_PHASE    = 0x00B3, | ||||
|         RFSPACE_CTRL_ITEM_SYNC_MODE     = 0x00B4, | ||||
|         RFSPACE_CTRL_ITEM_IQ_SAMP_RATE  = 0x00B8, | ||||
|         RFSPACE_CTRL_ITEM_UDP_PKT_SIZE  = 0x00C4, | ||||
|         RFSPACE_CTRL_ITEM_UDP_ADDR      = 0x00C5, | ||||
|         RFSPACE_CTRL_ITEM_TX_STATE      = 0x0118, | ||||
|         RFSPACE_CTRL_ITEM_TX_FREQUENCY  = 0x0120, | ||||
|         RFSPACE_CTRL_ITEM_TX_SAMP_RATE  = 0x01B8, | ||||
|         RFSPACE_CTRL_ITEM_SERIAL_OPEN   = 0x0200, | ||||
|         RFSPACE_CTRL_ITEM_SERIAL_CLOSE  = 0x0201, | ||||
|         RFSPACE_CTRL_ITEM_SER_BOOT_RATE = 0x0202, | ||||
|         RFSPACE_CTRL_ITEM_AUX_SIG_MODE  = 0x0280, | ||||
|         RFSPACE_CTRL_ITEM_ERROR_LOG     = 0x0410 | ||||
|     }; | ||||
|  | ||||
|     class RFspaceClientClass { | ||||
| @@ -93,18 +100,31 @@ namespace rfspace { | ||||
|         RFspaceClientClass(net::Conn conn, net::Conn udpConn, dsp::stream<dsp::complex_t>* out); | ||||
|         ~RFspaceClientClass(); | ||||
|  | ||||
|         int getControlItem(uint16_t item, void* param, int len); | ||||
|         void setControlItem(uint16_t item, void* param, int len); | ||||
|         void sendDummyUDP(); | ||||
|  | ||||
|         int getControlItem(ControlItem item, void* param, int len); | ||||
|         void setControlItem(ControlItem item, void* param, int len); | ||||
|         void setControlItemWithChanID(ControlItem item, uint8_t chanId, void* param, int len); | ||||
|  | ||||
|         std::vector<uint32_t> getValidSampleRates(); | ||||
|  | ||||
|         void setFrequency(uint64_t freq); | ||||
|         void setPort(RFPort port); | ||||
|         void setGain(int8_t gain); | ||||
|         void setSampleRate(uint32_t sampleRate); | ||||
|          | ||||
|         void start(SampleFormat sampleFormat, SampleDepth sampleDepth); | ||||
|         void stop(); | ||||
|  | ||||
|         void close(); | ||||
|         bool isOpen(); | ||||
|  | ||||
|     private: | ||||
|         void sendCommand(uint32_t command, void* data, int len); | ||||
|         void sendHandshake(std::string appName); | ||||
|         DeviceID deviceId; | ||||
|  | ||||
|     private: | ||||
|         static void tcpHandler(int count, uint8_t* buf, void* ctx); | ||||
|         static void udpHandler(int count, uint8_t* buf, void* ctx); | ||||
|         void heartBeatWorker(); | ||||
|  | ||||
|         net::Conn client; | ||||
|         net::Conn udpClient; | ||||
| @@ -118,8 +138,14 @@ namespace rfspace { | ||||
|         uint8_t* sbuffer = NULL; | ||||
|         uint8_t* ubuffer = NULL; | ||||
|  | ||||
|         SyncEvent SCIRRecv; | ||||
|         int SCIRSize; | ||||
|         std::thread heartBeatThread; | ||||
|         std::mutex heartBeatMtx; | ||||
|         std::condition_variable heartBeatCnd; | ||||
|         volatile bool stopHeartBeat = false; | ||||
|  | ||||
|         bool devIdAvailable = false; | ||||
|         std::condition_variable devIdCnd; | ||||
|         std::mutex devIdMtx; | ||||
|     }; | ||||
|  | ||||
|     typedef std::unique_ptr<RFspaceClientClass> RFspaceClient; | ||||
|   | ||||
| @@ -178,6 +178,7 @@ private: | ||||
|         if (_this->running) { style::beginDisabled(); } | ||||
|         if (!connected && ImGui::Button("Connect##spyserver_source", ImVec2(menuWidth, 0))) { | ||||
|             try { | ||||
|                 if (_this->client) { _this->client.reset(); } | ||||
|                 _this->client = spyserver::connect(_this->hostname, _this->port, &_this->stream); | ||||
|  | ||||
|                 if (!_this->client->waitForDevInfo(3000)) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user