Timur Davydov has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-trx/+/42656?usp=email )


Change subject: device: add WebSDR radio backend
......................................................................

device: add WebSDR radio backend

Introduce a new optional WebSDR device backend, enabled via
--with-websdr.

Add WebSDRDevice implementation and build integration, providing
a RadioDevice interface backed by callback hooks for control and
sample I/O.

Intended for Web-based deployments where osmo-trx interacts with
SDR hardware via a WebSDR/WebUSB frontend.

Change-Id: Ie459cbd70388dd8ff5b89221d30770bab0bd9014
---
M Transceiver52M/device/Makefile.am
A Transceiver52M/device/websdr/Makefile.am
A Transceiver52M/device/websdr/WebSDRDevice.cpp
A Transceiver52M/device/websdr/WebSDRDevice.h
M configure.ac
5 files changed, 352 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-trx refs/changes/56/42656/1

diff --git a/Transceiver52M/device/Makefile.am 
b/Transceiver52M/device/Makefile.am
index b5a5cec..62a013e 100644
--- a/Transceiver52M/device/Makefile.am
+++ b/Transceiver52M/device/Makefile.am
@@ -21,3 +21,7 @@
 if DEVICE_BLADE
 SUBDIRS += bladerf
 endif
+
+if DEVICE_WEBSDR
+SUBDIRS += websdr
+endif
diff --git a/Transceiver52M/device/websdr/Makefile.am 
b/Transceiver52M/device/websdr/Makefile.am
new file mode 100644
index 0000000..86e4a4d
--- /dev/null
+++ b/Transceiver52M/device/websdr/Makefile.am
@@ -0,0 +1,11 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
+AM_CXXFLAGS = $(LIBOSMOCORE_CFLAGS)
+
+noinst_HEADERS = WebSDRDevice.h
+
+noinst_LTLIBRARIES = libdevice.la
+
+libdevice_la_SOURCES = WebSDRDevice.cpp
+libdevice_la_LIBADD = 
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la
diff --git a/Transceiver52M/device/websdr/WebSDRDevice.cpp 
b/Transceiver52M/device/websdr/WebSDRDevice.cpp
new file mode 100644
index 0000000..ebb97b4
--- /dev/null
+++ b/Transceiver52M/device/websdr/WebSDRDevice.cpp
@@ -0,0 +1,237 @@
+/*
+* Copyright 2026 Wavelet Lab <[email protected]>
+*
+* SPDX-License-Identifier: AGPL-3.0+
+*
+* This software is distributed under the terms of the GNU Affero Public 
License.
+* See the COPYING file in the main directory for details.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+       This program is free software: you can redistribute it and/or modify
+       it under the terms of the GNU Affero General Public License as 
published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU Affero General Public License for more details.
+
+       You should have received a copy of the GNU Affero General Public License
+       along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include "Logger.h"
+#include "Threads.h"
+#include "WebSDRDevice.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+using namespace std;
+
+extern uint8_t g_tx_buffer[10000];
+extern uint32_t g_tx_ts;
+extern uint32_t g_tx_len;
+
+#ifdef __EMSCRIPTEN__
+
+#include <emscripten.h>
+
+EM_JS(bool, on_start_wrapper, (), {
+       return on_start();
+});
+
+EM_JS(bool, on_stop_wrapper, (), {
+       return on_stop();
+});
+
+EM_JS(bool, on_set_rx_frequency_wrapper, (unsigned frequency), {
+       return on_set_rx_frequency(frequency);
+});
+
+EM_JS(bool, on_set_tx_frequency_wrapper, (unsigned frequency), {
+       return on_set_tx_frequency(frequency);
+});
+
+EM_JS(void, write_log_wrapper, (const char *msg), {
+       write_log(msg);
+});
+
+EM_JS(int, on_write_samples_wrapper, (short* msg, int samples, uint64_t 
timestamp), {
+       return on_write_samples(msg, samples, timestamp);
+});
+
+#else /* __EMSCRIPTEN__ */
+
+extern "C" {
+bool on_start();
+bool on_stop();
+bool on_set_rx_frequency(unsigned frequency);
+bool on_set_tx_frequency(unsigned frequency);
+int on_write_samples(short* msg, int samples, uint64_t timestamp);
+
+static bool on_start_wrapper() { return on_start(); }
+static bool on_stop_wrapper() { return on_stop(); }
+static bool on_set_rx_frequency_wrapper(unsigned frequency) { return 
on_set_rx_frequency(frequency); }
+static bool on_set_tx_frequency_wrapper(unsigned frequency) { return 
on_set_tx_frequency(frequency); }
+static int on_write_samples_wrapper(short* msg, int samples, uint64_t 
timestamp) { return on_write_samples(msg, samples, timestamp); }
+}
+
+#endif /* __EMSCRIPTEN__ */
+
+WebSDRDevice::WebSDRDevice(InterfaceType iface, const struct trx_cfg *cfg) :
+       RadioDevice(iface, cfg)
+{
+       rx_gains.resize(chans);
+       tx_gains.resize(chans);
+
+       rx_freqs.resize(chans);
+       tx_freqs.resize(chans);
+}
+
+int WebSDRDevice::open()
+{
+       return NORMAL;
+}
+
+bool WebSDRDevice::start()
+{
+       return on_start_wrapper();
+}
+
+bool WebSDRDevice::stop()
+{
+       return on_stop_wrapper();
+}
+
+int WebSDRDevice::readSamples(std::vector<short *> &bufs, int len, bool 
*overrun,
+                             TIMESTAMP timestamp, bool *underrun)
+{
+       return 0;
+}
+
+int WebSDRDevice::writeSamples(std::vector<short *> &bufs, int len,
+                              bool *underrun, unsigned long long timestamp)
+{
+       memcpy(g_tx_buffer, bufs[0], 2 * len * sizeof(int16_t));
+       g_tx_ts = timestamp;
+       g_tx_len = len;
+       return len;
+}
+
+bool WebSDRDevice::setTxFreq(double wFreq, size_t chan) {
+       const bool res = 
on_set_tx_frequency_wrapper(static_cast<unsigned>(wFreq));
+       if (res)
+               tx_freqs[chan] = wFreq;
+
+       return res;
+}
+
+bool WebSDRDevice::setRxFreq(double wFreq, size_t chan) {
+       const bool res = 
on_set_rx_frequency_wrapper(static_cast<unsigned>(wFreq));
+       if (res)
+               rx_freqs[chan] = wFreq;
+
+       return res;
+}
+
+double WebSDRDevice::setRxGain(double dB, size_t chan)
+{
+       rx_gains[chan] = dB;
+       return dB;
+}
+
+double WebSDRDevice::getRxGain(size_t chan)
+{
+       return rx_gains[chan];
+}
+
+double WebSDRDevice::maxRxGain()
+{
+       return 0;
+}
+
+double WebSDRDevice::minRxGain()
+{
+       return 0;
+}
+
+int WebSDRDevice::getNominalTxPower(size_t chan)
+{
+       return 0;
+}
+
+bool WebSDRDevice::setRxAntenna(const std::string &ant, size_t chan)
+{
+       rx_paths[chan] = ant;
+       return true;
+}
+
+std::string WebSDRDevice::getRxAntenna(size_t chan)
+{
+       return rx_paths[chan];
+}
+
+bool WebSDRDevice::setTxAntenna(const std::string &ant, size_t chan)
+{
+       tx_paths[chan] = ant;
+       return true;
+}
+
+std::string WebSDRDevice::getTxAntenna(size_t chan)
+{
+       return tx_paths[chan];
+}
+
+GSM::Time WebSDRDevice::minLatency() {
+       return GSM::Time(1,1);
+}
+
+double WebSDRDevice::getTxFreq(size_t chan) {
+       return tx_freqs[chan];
+}
+
+double WebSDRDevice::getRxFreq(size_t chan) {
+       return rx_freqs[chan];
+}
+
+double WebSDRDevice::setPowerAttenuation(int atten, size_t chan) {
+       tx_gains[chan] = atten;
+       return atten;
+}
+
+double WebSDRDevice::getPowerAttenuation(size_t chan) {
+       return tx_gains[chan];
+}
+
+double WebSDRDevice::maxTxGain()
+{
+       return 0;
+}
+
+double WebSDRDevice::minTxGain()
+{
+       return 0;
+}
+
+RadioDevice *RadioDevice::make(InterfaceType iface, const struct trx_cfg *cfg)
+{
+       if (cfg->tx_sps != cfg->rx_sps) {
+               return NULL;
+       }
+       if (cfg->num_chans != 1) {
+               return NULL;
+       }
+       if (cfg->offset != 0.0) {
+               return NULL;
+       }
+       return new WebSDRDevice(iface, cfg);
+}
diff --git a/Transceiver52M/device/websdr/WebSDRDevice.h 
b/Transceiver52M/device/websdr/WebSDRDevice.h
new file mode 100644
index 0000000..b4e0c04
--- /dev/null
+++ b/Transceiver52M/device/websdr/WebSDRDevice.h
@@ -0,0 +1,98 @@
+/*
+* Copyright 2026 Wavelet Lab <[email protected]>
+*
+* SPDX-License-Identifier: AGPL-3.0+
+*
+* This software is distributed under multiple licenses; see the COPYING file in
+* the main directory for licensing information for this specific distribution.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*
+*/
+
+#ifndef _WEBSDR_DEVICE_H_
+#define _WEBSDR_DEVICE_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "radioDevice.h"
+
+#include <sys/time.h>
+#include <math.h>
+#include <string>
+#include <iostream>
+
+
+/** A class to handle a WebSDR device */
+class WebSDRDevice: public RadioDevice {
+public:
+
+       WebSDRDevice(InterfaceType iface, const struct trx_cfg *cfg);
+
+       int open();
+       bool start();
+       bool stop();
+
+       enum TxWindowType getWindowType() { return TX_WINDOW_FIXED; }
+
+       int readSamples(std::vector<short *> &buf, int len, bool *overrun,
+                       TIMESTAMP timestamp = 0xffffffff, bool *underrun = 
NULL);
+       int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
+                        TIMESTAMP timestamp = 0xffffffff);
+
+       bool updateAlignment(TIMESTAMP timestamp) { return true; }
+
+       bool setTxFreq(double wFreq, size_t chan = 0);
+       bool setRxFreq(double wFreq, size_t chan = 0);
+
+       TIMESTAMP initialWriteTimestamp(void) { return 0; }
+       TIMESTAMP initialReadTimestamp(void) { return 0; }
+
+       double fullScaleInputValue() { return 32767.0 * 0.7071; }
+       double fullScaleOutputValue() { return 32767.0; }
+
+       double setRxGain(double dB, size_t chan = 0);
+       double getRxGain(size_t chan = 0);
+       double maxRxGain(void);
+       double minRxGain(void);
+       double rssiOffset(size_t chan) { return 0.0f;  }
+
+       int getNominalTxPower(size_t chan = 0);
+
+       bool setRxAntenna(const std::string &ant, size_t chan = 0);
+       std::string getRxAntenna(size_t chan = 0);
+
+       bool setTxAntenna(const std::string &ant, size_t chan = 0);
+       std::string getTxAntenna(size_t chan = 0);
+
+       bool requiresRadioAlign() { return false; }
+
+       GSM::Time minLatency();
+
+       double getTxFreq(size_t chan = 0);
+       double getRxFreq(size_t chan = 0);
+
+       double getSampleRate() { return 0; }
+
+       double setPowerAttenuation(int atten, size_t chan);
+       double getPowerAttenuation(size_t chan=0);
+       double maxTxGain(void);
+       double minTxGain(void);
+
+private:
+       /* Cached TX/RX gains */
+       std::vector<double> tx_gains, rx_gains;
+
+       /* Cached TX/RX frequencies */
+       std::vector<double> tx_freqs, rx_freqs;
+
+};
+
+#endif // _WEBSDR_DEVICE_H_
diff --git a/configure.ac b/configure.ac
index bf3bc5e..b0d79a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -310,6 +310,7 @@
 AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
 AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "xyes"])
 AM_CONDITIONAL(DEVICE_BLADE, [test "x$with_bladerf" = "xyes"])
+AM_CONDITIONAL(DEVICE_WEBSDR, [test "x$with_websdr" = "xyes"])
 AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test 
"x$with_neon_vfpv4" = "xyes"])
 AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
 AM_CONDITIONAL(ENABLE_MS_TRX, [test "x$with_mstrx" = "xyes"])
@@ -411,6 +412,7 @@
     Transceiver52M/device/lms/Makefile \
     Transceiver52M/device/ipc/Makefile \
     Transceiver52M/device/bladerf/Makefile \
+    Transceiver52M/device/websdr/Makefile \
     tests/Makefile \
     tests/CommonLibs/Makefile \
     tests/Transceiver52M/Makefile \

--
To view, visit https://gerrit.osmocom.org/c/osmo-trx/+/42656?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: osmo-trx
Gerrit-Branch: master
Gerrit-Change-Id: Ie459cbd70388dd8ff5b89221d30770bab0bd9014
Gerrit-Change-Number: 42656
Gerrit-PatchSet: 1
Gerrit-Owner: Timur Davydov <[email protected]>

Reply via email to