mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 10:49:11 +01:00 
			
		
		
		
	Work on the spectran HTTP source
This commit is contained in:
		
							
								
								
									
										51
									
								
								core/src/utils/new_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								core/src/utils/new_event.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
typedef int HandlerID;
 | 
			
		||||
 | 
			
		||||
template <typename... Args>
 | 
			
		||||
class NewEvent {
 | 
			
		||||
    using Handler = std::function<void(Args...)>;
 | 
			
		||||
public:
 | 
			
		||||
    HandlerID bind(const Handler& handler) {
 | 
			
		||||
        std::lock_guard<std::mutex> lck(mtx);
 | 
			
		||||
        HandlerID id = genID();
 | 
			
		||||
        handlers[id] = handler;
 | 
			
		||||
        return id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<typename MHandler, class T>
 | 
			
		||||
    HandlerID bind(MHandler handler, T* ctx) {
 | 
			
		||||
        return bind([=](Args... args){
 | 
			
		||||
            (ctx->*handler)(args...);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void unbind(HandlerID id) {
 | 
			
		||||
        std::lock_guard<std::mutex> lck(mtx);
 | 
			
		||||
        if (handlers.find(id) == handlers.end()) {
 | 
			
		||||
            throw std::runtime_error("Could not unbind handler, unknown ID");
 | 
			
		||||
        }
 | 
			
		||||
        handlers.erase(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void operator()(Args... args) {
 | 
			
		||||
        std::lock_guard<std::mutex> lck(mtx);
 | 
			
		||||
        for (const auto& [desc, handler] : handlers) {
 | 
			
		||||
            handler(args...);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    HandlerID genID() {
 | 
			
		||||
        int id;
 | 
			
		||||
        for (id = 1; handlers.find(id) != handlers.end(); id++);
 | 
			
		||||
        return id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::map<HandlerID, Handler> handlers;
 | 
			
		||||
    std::mutex mtx;
 | 
			
		||||
};
 | 
			
		||||
@@ -28,7 +28,7 @@ public:
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        strcpy(hostname, "localhost");
 | 
			
		||||
        sampleRate = 41000000.0;
 | 
			
		||||
        sampleRate = 5750000.0;
 | 
			
		||||
 | 
			
		||||
        handler.ctx = this;
 | 
			
		||||
        handler.selectHandler = menuSelected;
 | 
			
		||||
@@ -103,8 +103,14 @@ private:
 | 
			
		||||
 | 
			
		||||
    static void tune(double freq, void* ctx) {
 | 
			
		||||
        SpectranHTTPSourceModule* _this = (SpectranHTTPSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) {
 | 
			
		||||
            // TODO
 | 
			
		||||
        bool connected = (_this->client && _this->client->isOpen());
 | 
			
		||||
        if (connected) {
 | 
			
		||||
            int64_t newfreq = round(freq);
 | 
			
		||||
            if (newfreq != _this->lastReportedFreq && _this->gotReport) {
 | 
			
		||||
                flog::debug("Sending tuning command");
 | 
			
		||||
                _this->lastReportedFreq = newfreq;
 | 
			
		||||
                _this->client->setCenterFrequency(newfreq);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        _this->freq = freq;
 | 
			
		||||
        flog::info("SpectranHTTPSourceModule '{0}': Tune: {1}!", _this->name, freq);
 | 
			
		||||
@@ -138,6 +144,7 @@ private:
 | 
			
		||||
            _this->tryConnect();
 | 
			
		||||
        }
 | 
			
		||||
        else if (connected && SmGui::Button("Disconnect##spectran_http_source")) {
 | 
			
		||||
            _this->client->onCenterFrequencyChanged.unbind(_this->onFreqChangedId);
 | 
			
		||||
            _this->client->close();
 | 
			
		||||
        }
 | 
			
		||||
        if (_this->running) { style::endDisabled(); }
 | 
			
		||||
@@ -154,13 +161,23 @@ private:
 | 
			
		||||
 | 
			
		||||
    void tryConnect() {
 | 
			
		||||
        try {
 | 
			
		||||
            gotReport = false;
 | 
			
		||||
            client = std::make_shared<SpectranHTTPClient>(hostname, port, &stream);
 | 
			
		||||
            onFreqChangedId = client->onCenterFrequencyChanged.bind(&SpectranHTTPSourceModule::onFreqChanged, this);
 | 
			
		||||
            client->startWorker();
 | 
			
		||||
        }
 | 
			
		||||
        catch (std::runtime_error e) {
 | 
			
		||||
            flog::error("Could not connect: {0}", e.what());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void onFreqChanged(double newFreq) {
 | 
			
		||||
        if (lastReportedFreq == newFreq) { return; }
 | 
			
		||||
        lastReportedFreq = newFreq;
 | 
			
		||||
        tuner::tune(tuner::TUNER_MODE_IQ_ONLY, "", newFreq);
 | 
			
		||||
        gotReport = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string name;
 | 
			
		||||
    bool enabled = true;
 | 
			
		||||
    double sampleRate;
 | 
			
		||||
@@ -168,11 +185,15 @@ private:
 | 
			
		||||
    bool running = false;
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<SpectranHTTPClient> client;
 | 
			
		||||
    HandlerID onFreqChangedId;
 | 
			
		||||
 | 
			
		||||
    double freq;
 | 
			
		||||
 | 
			
		||||
    int64_t lastReportedFreq = 0;
 | 
			
		||||
    bool gotReport;
 | 
			
		||||
 | 
			
		||||
    char hostname[1024];
 | 
			
		||||
    int port = 80;
 | 
			
		||||
    int port = 54664;
 | 
			
		||||
    dsp::stream<dsp::complex_t> stream;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@ SpectranHTTPClient::SpectranHTTPClient(std::string host, int port, dsp::stream<d
 | 
			
		||||
    this->stream = stream;
 | 
			
		||||
 | 
			
		||||
    // Connect to server
 | 
			
		||||
    this->host = host;
 | 
			
		||||
    this->port = port;
 | 
			
		||||
    sock = net::connect(host, port);
 | 
			
		||||
    http = net::http::Client(sock);
 | 
			
		||||
 | 
			
		||||
@@ -14,6 +16,13 @@ SpectranHTTPClient::SpectranHTTPClient(std::string host, int port, dsp::stream<d
 | 
			
		||||
    net::http::ResponseHeader rshdr;
 | 
			
		||||
    http.recvResponseHeader(rshdr, 5000);
 | 
			
		||||
 | 
			
		||||
    if (rshdr.getStatusCode() != net::http::STATUS_CODE_OK) {
 | 
			
		||||
        flog::error("HTTP request did not return ok: {}", rshdr.getStatusString());
 | 
			
		||||
        throw std::runtime_error("HTTP request did not return ok");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SpectranHTTPClient::startWorker() {
 | 
			
		||||
    // Start chunk worker
 | 
			
		||||
    workerThread = std::thread(&SpectranHTTPClient::worker, this);
 | 
			
		||||
}
 | 
			
		||||
@@ -33,6 +42,27 @@ void SpectranHTTPClient::close() {
 | 
			
		||||
    stream->clearWriteStop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SpectranHTTPClient::setCenterFrequency(uint64_t freq) {
 | 
			
		||||
    // Connect to control endpoint (TODO: Switch to an always connected endpoint)
 | 
			
		||||
    auto controlSock = net::connect(host, port);
 | 
			
		||||
    auto controlHttp = net::http::Client(controlSock);
 | 
			
		||||
 | 
			
		||||
    // Make request
 | 
			
		||||
    net::http::RequestHeader rqhdr(net::http::METHOD_PUT, "/control", host);
 | 
			
		||||
    char buf[1024];
 | 
			
		||||
    sprintf(buf, "{\"frequencyCenter\":%d,\"frequencySpan\":%d,\"type\":\"capture\"}", freq, _span);
 | 
			
		||||
    std::string data = buf;
 | 
			
		||||
    char lenBuf[16];
 | 
			
		||||
    sprintf(lenBuf, "%d", data.size());
 | 
			
		||||
    rqhdr.setField("Content-Length", lenBuf);
 | 
			
		||||
    controlHttp.sendRequestHeader(rqhdr);
 | 
			
		||||
    controlSock->sendstr(data);
 | 
			
		||||
    net::http::ResponseHeader rshdr;
 | 
			
		||||
    controlHttp.recvResponseHeader(rshdr, 5000);
 | 
			
		||||
 | 
			
		||||
    flog::debug("Response: {}", rshdr.getStatusString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SpectranHTTPClient::worker() {
 | 
			
		||||
    while (sock->isOpen()) {
 | 
			
		||||
        // Get chunk header
 | 
			
		||||
@@ -52,6 +82,26 @@ void SpectranHTTPClient::worker() {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Decode JSON (yes, this is hacky, but it must be extremely fast)
 | 
			
		||||
        auto startFreqBegin = jsonData.find("\"startFrequency\":");
 | 
			
		||||
        auto startFreqEnd = jsonData.find(',', startFreqBegin);
 | 
			
		||||
        std::string startFreqStr = jsonData.substr(startFreqBegin + 17, startFreqEnd - startFreqBegin - 17);
 | 
			
		||||
        int64_t startFreq = std::stoll(startFreqStr);
 | 
			
		||||
 | 
			
		||||
        auto endFreqBegin = jsonData.find("\"endFrequency\":");
 | 
			
		||||
        auto endFreqEnd = jsonData.find(',', endFreqBegin);
 | 
			
		||||
        std::string endFreqStr = jsonData.substr(endFreqBegin + 15, endFreqEnd - endFreqBegin - 15);
 | 
			
		||||
        int64_t endFreq = std::stoll(endFreqStr);
 | 
			
		||||
 | 
			
		||||
        // Calculate and update center freq
 | 
			
		||||
        _span = endFreq - startFreq;
 | 
			
		||||
        int64_t centerFreq = round(((double)endFreq + (double)startFreq) / 2.0);
 | 
			
		||||
        if (centerFreq != _centerFreq) {
 | 
			
		||||
            flog::debug("{}, {}, {}", _span, centerFreq);
 | 
			
		||||
            _centerFreq = centerFreq;
 | 
			
		||||
            onCenterFrequencyChanged(centerFreq);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Read (and check for) record separator
 | 
			
		||||
        uint8_t rs;
 | 
			
		||||
        int rslen = sock->recv(&rs, 1, true, 5000);
 | 
			
		||||
@@ -72,10 +122,11 @@ void SpectranHTTPClient::worker() {
 | 
			
		||||
            i += read;
 | 
			
		||||
            sampLen += read;
 | 
			
		||||
        }
 | 
			
		||||
        int sampCount = sampLen / 8;
 | 
			
		||||
 | 
			
		||||
        // Swap to stream
 | 
			
		||||
        if (streamingEnabled) {
 | 
			
		||||
            if (!stream->swap(sampLen / 8)) { return; }
 | 
			
		||||
            if (!stream->swap(sampCount)) { return; }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Read trailing CRLF
 | 
			
		||||
 
 | 
			
		||||
@@ -4,22 +4,34 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <utils/proto/http.h>
 | 
			
		||||
#include <utils/new_event.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
class SpectranHTTPClient {
 | 
			
		||||
public:
 | 
			
		||||
    SpectranHTTPClient(std::string host, int port, dsp::stream<dsp::complex_t>* stream);
 | 
			
		||||
 | 
			
		||||
    void startWorker();
 | 
			
		||||
    void streaming(bool enabled);
 | 
			
		||||
    bool isOpen();
 | 
			
		||||
    void close();
 | 
			
		||||
 | 
			
		||||
    void setCenterFrequency(uint64_t freq);
 | 
			
		||||
 | 
			
		||||
    NewEvent<uint64_t> onCenterFrequencyChanged;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void worker();
 | 
			
		||||
 | 
			
		||||
    std::string host;
 | 
			
		||||
    int port;
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<net::Socket> sock;
 | 
			
		||||
    net::http::Client http;
 | 
			
		||||
    dsp::stream<dsp::complex_t>* stream;
 | 
			
		||||
    std::thread workerThread;
 | 
			
		||||
 | 
			
		||||
    bool streamingEnabled = false;
 | 
			
		||||
    int64_t _centerFreq = 0;
 | 
			
		||||
    uint64_t _span = 5000000;
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user