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 info@wavelet-lab.com +* +* 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 info@wavelet-lab.com +* +* 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 \