Timur Davydov has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-trx/+/42411?usp=email )
Change subject: device/websdr: add WebSDR transceiver backend and build target ......................................................................
device/websdr: add WebSDR transceiver backend and build target
- add libosmo-trx-websdr library and pkg-config file - integrate WebSDR device into the autotools build system - update .gitignore for wasm and pkg-config artifacts
Change-Id: Ia0d340c323c2eea28fbe82601ba0af7cfbd68f6d --- M .gitignore M Transceiver52M/Makefile.am M Transceiver52M/device/Makefile.am A Transceiver52M/device/websdr/Makefile.am A Transceiver52M/device/websdr/TransceiverWebSdr.cpp A Transceiver52M/device/websdr/TransceiverWebSdr.h A Transceiver52M/device/websdr/libosmo-trx-websdr.pc.in A Transceiver52M/device/websdr/osmo-trx-websdr.cpp A Transceiver52M/device/websdr/osmo-trx-websdr.h A Transceiver52M/device/websdr/radioInterfaceWebSdr.cpp A Transceiver52M/device/websdr/radioInterfaceWebSdr.h M Transceiver52M/radioInterface.h M configure.ac 13 files changed, 852 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-trx refs/changes/11/42411/1
diff --git a/.gitignore b/.gitignore index fc567db..1995586 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ *.o *.lo *.la +*.pc +*.wasm +*.out.js Transceiver52M/osmo-trx-uhd Transceiver52M/osmo-trx-usrp1 Transceiver52M/osmo-trx-lms @@ -35,6 +38,7 @@
# automake/autoconf *.in +!*.pc.in .deps .libs .dirstamp diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index 74152b3..ac410f1 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -201,3 +201,15 @@ $(COMMON_LDADD) osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS) endif + +if DEVICE_WEBSDR +include_HEADERS = device/websdr/osmo-trx-websdr.h +lib_LTLIBRARIES = libosmo-trx-websdr.la +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = device/websdr/libosmo-trx-websdr.pc +libosmo_trx_websdr_la_SOURCES = +libosmo_trx_websdr_la_LIBADD = \ + $(builddir)/device/websdr/libdevice.la \ + $(COMMON_LDADD) + $(NULL) +endif 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..422b16f --- /dev/null +++ b/Transceiver52M/device/websdr/Makefile.am @@ -0,0 +1,14 @@ +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common -I${srcdir}/../.. -I${srcdir}/../../arch/common +AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS) + +noinst_HEADERS = osmo-trx-websdr.h + +noinst_LTLIBRARIES = libdevice.la + +libdevice_la_SOURCES = \ + radioInterfaceWebSdr.cpp \ + TransceiverWebSdr.cpp \ + osmo-trx-websdr.cpp \ + $(NULL) \ No newline at end of file diff --git a/Transceiver52M/device/websdr/TransceiverWebSdr.cpp b/Transceiver52M/device/websdr/TransceiverWebSdr.cpp new file mode 100644 index 0000000..0518de4 --- /dev/null +++ b/Transceiver52M/device/websdr/TransceiverWebSdr.cpp @@ -0,0 +1,322 @@ +/* + * WebSDR radio transceiver implementation + * + * Copyright 2008, 2009, 2010 Free Software Foundation, Inc. + * Copyright 2026 Wavelet Lab info@wavelet-lab.com + * + * SPDX-License-Identifier: GPL-3.0+ + * + * This software is distributed under the terms of the GNU 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include "TransceiverWebSdr.h" + +#include <fstream> +#include <grgsm_vitac/grgsm_vitac.h> + +extern "C" { +#include "proto_trxd.h" +#include "osmocom/core/bits.h" +} + +using namespace GSM; + +enum { + RET_OK = 0, + RET_OK_UPDSTAT = 1, + RET_FAILED = -1 +}; + +TransceiverWebSdr::TransceiverWebSdr(const struct trx_cfg *cfg, + GSM::Time wTransmitLatency, + RadioInterfaceWebSdr *wRadioInterface) + : Transceiver(cfg, wTransmitLatency, wRadioInterface) +{ +} + +/* + * Initialize transceiver + * + * Start or restart the control loop. Any further control is handled through the + * socket API. Randomize the central radio clock set the downlink burst + * counters. Note that the clock will not update until the radio starts, but we + * are still expected to report clock indications through control channel + * activity. + */ +bool TransceiverWebSdr::init() +{ + if (!mChans) { + LOG(FATAL) << "No channels assigned"; + return false; + } + + if (!sigProcLibSetup()) { + LOG(FATAL) << "Failed to initialize signal processing library"; + return false; + } + + initvita(); + + /* Filler table retransmissions - support only on channel 0 */ + if (cfg->filler == FILLER_DUMMY) + mStates[0].mRetrans = true; + + /* Randomize the central clock */ + GSM::Time startTime(random() % gHyperframe, 0); + mRadioInterfaceWebSdr->getClock()->set(startTime); + mTransmitDeadlineClock = startTime; + mLastClockUpdateTime = startTime; + mLatencyUpdateTime = startTime; + + return true; +} + +bool TransceiverWebSdr::start() +{ + if (mOn) { + LOG(ERR) << "Transceiver already running"; + return true; + } + + LOG(NOTICE) << "Starting the transceiver"; + + GSM::Time time = mRadioInterfaceWebSdr->getClock()->get(); + mTransmitDeadlineClock = time; + mLastClockUpdateTime = time; + mLatencyUpdateTime = time; + + if (!mRadioInterfaceWebSdr->start()) { + LOG(FATAL) << "Device failed to start"; + return false; + } + + mForceClockInterface = true; + mOn = true; + return true; +} + +void TransceiverWebSdr::stop() +{ + if (!mOn) + return; + + LOG(NOTICE) << "Stopping the transceiver"; + LOG(INFO) << "Stopping the device"; + mRadioInterfaceWebSdr->stop(); + + mOn = false; + LOG(NOTICE) << "Transceiver stopped"; +} + +int TransceiverWebSdr::driveReceiveRadioShortVector(short **buffers, int samples, int underrun, + TIMESTAMP ts, unsigned samples_skipped) +{ + int rc; + + rc = mRadioInterfaceWebSdr->fillPullBuffer(buffers, samples, underrun, ts); + if (rc) + return rc; + + rc = mRadioInterfaceWebSdr->driveReceiveRadioWithBuffer(samples_skipped); + if (rc) + return rc; + + if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216, 0)) { + if (mForceClockInterface) + LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications"; + mForceClockInterface = false; + if (on_write_clock((unsigned long long)(mTransmitDeadlineClock.FN() + 2))) { + mLastClockUpdateTime = mTransmitDeadlineClock; + } else { + return false; + } + } + return true; +} + +int TransceiverWebSdr::driveReceiveFIFOBurst(size_t chan, char *buf, int maxLen) +{ + struct trx_ul_burst_ind bi; + int rc; + + if ((rc = pullRadioVector(chan, &bi)) < 0) { + if (rc == -EAGAIN) + return 0; + if (rc == -ENOENT) { /* timeslot off, continue processing */ + LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off"; + return rc; + } + return rc; /* other errors: we want to stop the process */ + } + + if (!bi.idle) + logRxBurst(chan, &bi); + + switch (mVersionTRXD[chan]) { + case 0: + return trxd_prepare_burst_ind_v0(&bi, buf, maxLen); + case 1: + return trxd_prepare_burst_ind_v1(&bi, buf, maxLen); + default: + //OSMO_ASSERT(false); + return -EFAULT; + } +} + +void TransceiverWebSdr::driveTxFIFO() +{ + RadioClock *radioClock = (mRadioInterfaceWebSdr->getClock()); + + if (mOn) { + LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get(); + while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) { + // time to push burst to transmit FIFO + pushRadioVector(mTransmitDeadlineClock); + mTransmitDeadlineClock.incTN(); + } + } +} + +bool TransceiverWebSdr::driveTxPriorityQueue(size_t chan, char *buffer, int msgLen) +{ + int res = post_tx_burst(chan, buffer, msgLen); + if (res == RET_OK_UPDSTAT) { + return true; + } else if (res == RET_OK) { + return true; + } else { + return false; + } +} + +int TransceiverWebSdr::processCommand(int chan, const char *buffer, char *response, size_t response_size) +{ + return ctrl_cmd_handle(chan, buffer, response, response_size); +} + +bool TransceiverWebSdr::on_write_clock(unsigned long long clock) +{ + LOG(ALERT) << "IND CLOCK " << clock; + mIndClockValid = true; + mIndClock = clock; + + return true; +} + + +bool TransceiverWebSdr::peekClock(unsigned *out) +{ + bool res = mIndClockValid; + *out = mIndClock; + mIndClockValid = false; + return res; +} + +int TransceiverWebSdr::post_tx_burst(size_t chan, char *buffer, int msgLen) +{ + int burstLen; + struct trxd_hdr_v01_dl *dl; + uint32_t fn; + uint8_t tn; + int res = RET_OK; + + switch (msgLen) { + case sizeof(*dl) + gSlotLen: /* GSM burst */ + burstLen = gSlotLen; + break; + case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */ + if (cfg->tx_sps != 4) { + LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << cfg->tx_sps; + return RET_FAILED; + } + burstLen = EDGE_BURST_NBITS; + break; + default: + LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len=" << msgLen << ")"; + return RET_FAILED; + } + + dl = (struct trxd_hdr_v01_dl *)buffer; + + /* Convert TDMA FN to the host endianness */ + fn = osmo_load32be(&dl->common.fn); + tn = dl->common.tn; + + /* Make sure we support the received header format */ + switch (dl->common.version) { + case 0: + /* Version 1 has the same format */ + case 1: + break; + default: + LOGCHAN(chan, DTRXDDL, ERROR) + << "Rx TRXD message with unknown header version " << unsigned(dl->common.version); + return RET_FAILED; + } + + LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version) << "): fn=" << fn + << ", tn=" << unsigned(tn) << ", burst_len=" << burstLen; + + TransceiverState *state = &mStates[chan]; + GSM::Time currTime = GSM::Time(fn, tn); + + /* Verify proper FN order in DL stream */ + if (state->first_dl_fn_rcv[tn]) { + int32_t delta = GSM::FNDelta(currTime.FN(), state->last_dl_time_rcv[tn].FN()); + if (delta == 1) { + /* usual expected scenario, continue code flow */ + } else if (delta == 0) { + LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with repeated FN " << currTime; + state->ctrs.tx_trxd_fn_repeated++; + return RET_OK_UPDSTAT; + } else if (delta < 0) { + LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with previous FN " << currTime << " vs last " + << state->last_dl_time_rcv[tn]; + state->ctrs.tx_trxd_fn_outoforder++; + res = RET_OK_UPDSTAT; + /* Allow adding radio vector below, since it gets sorted in the queue */ + } else if (chan == 0 && state->mFiller == FILLER_ZERO) { + /* delta > 1. Some FN was lost in the middle. We can only easily rely + * on consecutive FNs in TRX0 since it must transmit continuously in all + * setups. Also, osmo-trx supports optionally filling empty bursts on + * its own. In that case bts-trx is not obliged to submit all bursts. + */ + LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with future FN " << currTime << " vs last " + << state->last_dl_time_rcv[tn] << ", " << delta - 1 << " FN lost"; + state->ctrs.tx_trxd_fn_skipped += delta - 1; + res = RET_OK_UPDSTAT; + } + if (delta > 0) + state->last_dl_time_rcv[tn] = currTime; + } else { /* Initial check, simply store state */ + state->first_dl_fn_rcv[tn] = true; + state->last_dl_time_rcv[tn] = currTime; + } + + BitVector newBurst(burstLen); + BitVector::iterator itr = newBurst.begin(); + uint8_t *bufferItr = dl->soft_bits; + while (itr < newBurst.end()) + *itr++ = *bufferItr++; + + addRadioVector(chan, newBurst, dl->tx_att, currTime); + + return res; +} diff --git a/Transceiver52M/device/websdr/TransceiverWebSdr.h b/Transceiver52M/device/websdr/TransceiverWebSdr.h new file mode 100644 index 0000000..cf17282 --- /dev/null +++ b/Transceiver52M/device/websdr/TransceiverWebSdr.h @@ -0,0 +1,70 @@ +/* + * WebSDR radio transceiver interface + * + * Copyright 2008 Free Software Foundation, Inc. + * Copyright 2026 Wavelet Lab info@wavelet-lab.com + * + * SPDX-License-Identifier: GPL-3.0+ + * + * This software is distributed under the terms of the GNU 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef TRANSCEIVER_WEBSDR_H +#define TRANSCEIVER_WEBSDR_H + +#include "Transceiver.h" +#include "radioInterfaceWebSdr.h" + +class TransceiverWebSdr : public Transceiver { +public: + TransceiverWebSdr(const struct trx_cfg *cfg, + GSM::Time wTransmitLatency, + RadioInterfaceWebSdr *wRadioInterface); + + bool init(void); + + /* Command handling */ + int processCommand(int chan, const char *buffer, char *response, size_t response_size); + + /* RX path methods */ + int driveReceiveRadioShortVector(short **buffers, int samples, int underrun, + TIMESTAMP ts, unsigned samples_skipped); + + /* Obtain receive bursts from QUEUE */ + int driveReceiveFIFOBurst(size_t chan, char *buf, int maxLen); + + /* TX path methods */ + void driveTxFIFO(); + bool driveTxPriorityQueue(size_t chan, char *buffer, int msgLen); + bool on_write_clock(unsigned long long clock); + bool peekClock(unsigned *out); + int post_tx_burst(size_t chan, char* buffer, int msgLen); + +protected: + RadioInterfaceWebSdr *mRadioInterfaceWebSdr; /* associated radioInterfaceWebSdr object */ + + bool mIndClockValid; + unsigned mIndClock; + + bool start(); + void stop(); +}; + +#endif /* TRANSCEIVER_WEBSDR_H */ \ No newline at end of file diff --git a/Transceiver52M/device/websdr/libosmo-trx-websdr.pc.in b/Transceiver52M/device/websdr/libosmo-trx-websdr.pc.in new file mode 100644 index 0000000..5ac04b4 --- /dev/null +++ b/Transceiver52M/device/websdr/libosmo-trx-websdr.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom TRX WebSdr Transceiver Common Library +Description: WebSdr Transceiver common code for osmo-bts +Version: @VERSION@ +Requires: +Requires.private: libosmocore libosmoctrl libosmovty +Libs: -L${libdir} -losmo-trx-websdr +Libs.private: @LIBOSMOCORE_LIBS@ @LIBOSMOCTRL_LIBS@ @LIBOSMOVTY_LIBS@ +Cflags: -I${includedir}/ @LIBOSMOCORE_CFLAGS@ @LIBOSMOCTRL_CFLAGS@ @LIBOSMOVTY_CFLAGS@ diff --git a/Transceiver52M/device/websdr/osmo-trx-websdr.cpp b/Transceiver52M/device/websdr/osmo-trx-websdr.cpp new file mode 100644 index 0000000..39e1c6b --- /dev/null +++ b/Transceiver52M/device/websdr/osmo-trx-websdr.cpp @@ -0,0 +1,216 @@ +/* + * Osmo TRX WebSDR API implementation + * + * Copyright (C) 2013 Thomas Tsou tom@tsou.cc + * Copyright (C) 2026 Wavelet Lab info@wavelet-lab.com + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "TransceiverWebSdr.h" + +#include "radioDevice.h" + +extern "C" { +#include "convolve.h" +#include "convert.h" +#include "trx_vty.h" +} + +#include "osmo-trx-websdr.h" + +#define charp2str(a) ((a) ? std::string(a) : std::string("")) + +int g_no_tx_call; +uint8_t g_tx_buffer[10000]; +uint32_t g_tx_ts; +uint32_t g_tx_len; + +static struct trx_ctx trx_ctx; +static struct trx_ctx* g_trx_ctx = &trx_ctx; + +static RadioDevice *radioDevice; +static RadioInterfaceWebSdr *radio; +static TransceiverWebSdr *transceiverWebSdr; + +RadioInterfaceWebSdr *makeRadioInterface(struct trx_ctx *trx, RadioDevice *radioDevice, int type) +{ + RadioInterfaceWebSdr *radio = NULL; + + switch (type) { + case RadioDevice::NORMAL: + radio = new RadioInterfaceWebSdr(radioDevice, trx->cfg.tx_sps, trx->cfg.rx_sps); + break; + default: + LOG(ALERT) << "Unsupported radio interface configuration"; + return NULL; + } + + if (!radio->init(type)) { + LOG(ALERT) << "Failed to initialize radio interface"; + return NULL; + } + + return radio; +} + +int makeTransceiver(struct trx_ctx *trx, RadioInterfaceWebSdr *radio) +{ + VectorFIFO *fifo; + + transceiverWebSdr = new TransceiverWebSdr(&trx->cfg, GSM::Time(3,0), radio); + if (!transceiverWebSdr->init()) { + LOG(ALERT) << "Failed to initialize transceiver"; + return -1; + } + + for (size_t i = 0; i < trx->cfg.num_chans; i++) { + fifo = radio->receiveFIFO(i); + if (fifo && transceiverWebSdr->receiveFIFO(fifo, i)) + continue; + + LOG(ALERT) << "Could not attach FIFO to channel " << i; + return -1; + } + return 0; +} + +static void trx_stop() +{ + LOG(NOTICE) << "Shutting down transceiver..." << std::endl; + + delete transceiverWebSdr; + delete radio; + delete radioDevice; +} + +static int trx_start(struct trx_ctx *trx) +{ + int type, chans; + unsigned int i; + std::vectorstd::string rx_paths, tx_paths; + RadioDevice::InterfaceType iface = RadioDevice::NORMAL; + + /* Generate vector of rx/tx_path: */ + for (i = 0; i < trx->cfg.num_chans; i++) { + rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path)); + tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path)); + } + + radioDevice = RadioDevice::make(iface, &trx->cfg); + type = radioDevice->open(); + if (type < 0) { + LOG(ALERT) << "Failed to create radio device" << std::endl; + goto shutdown; + } + + /* Setup the appropriate device interface */ + radio = makeRadioInterface(trx, radioDevice, type); + if (!radio) + goto shutdown; + + /* Create the transceiver core */ + if (makeTransceiver(trx, radio) < 0) + goto shutdown; + + chans = transceiverWebSdr->numChans(); + LOG(NOTICE) << "-- Transceiver active with " + << chans << " channel(s)" << std::endl; + + return 0; + +shutdown: + trx_stop(); + return -1; +} + +static int trx_start_and_configure() +{ + convolve_init(); + convert_init(); + + g_trx_ctx->cfg.tx_sps = 4; + g_trx_ctx->cfg.rx_sps = 4; + g_trx_ctx->cfg.base_port = 5700; + g_trx_ctx->cfg.bind_addr = (char*)"127.0.0.1"; + g_trx_ctx->cfg.remote_addr = (char*)"127.0.1.1"; + g_trx_ctx->cfg.filler = FILLER_DUMMY; + + g_trx_ctx->cfg.num_chans = 1; + g_trx_ctx->cfg.chans[0].idx = 0; + g_trx_ctx->cfg.chans[0].trx = g_trx_ctx; + g_trx_ctx->cfg.chans[0].rx_path = (char*)"auto"; + g_trx_ctx->cfg.chans[0].tx_path = (char*)"auto"; + + g_trx_ctx->cfg.multi_arfcn = 0; + + srandom(time(NULL)); + + g_no_tx_call = true; + + if(trx_start(g_trx_ctx) < 0) + return EXIT_FAILURE; + + return 0; +} + +/* Osmo TRX WebSDR API */ + +#ifdef __cplusplus +extern "C" { +#endif + +int osmotrxlib_init() +{ + return trx_start_and_configure(); +} + +void osmotrxlib_deinit() +{ + trx_stop(); +} + +int osmotrxlib_process_command(const char* buffer, char* response, size_t response_size) +{ + return transceiverWebSdr->processCommand(0, buffer, response, response_size); +} + +int osmotrxlib_push_rx_short_vector(short* buffers, int samples, int underrun, + unsigned ts, unsigned samples_skipped) +{ + return transceiverWebSdr->driveReceiveRadioShortVector(&buffers, samples, underrun, ts, samples_skipped); +} + +int osmotrxlib_get_rx_burst(char* buf, int maxLen) +{ + return transceiverWebSdr->driveReceiveFIFOBurst(0, buf, maxLen); +} + +int osmotrxlib_get_tx_short_vector() +{ + transceiverWebSdr->driveTxFIFO(); + return 0; +} + +int osmotrxlib_put_tx_burst(char* buffer, int msgLen) +{ + return transceiverWebSdr->driveTxPriorityQueue(0, buffer, msgLen); +} + +#ifdef __cplusplus +} +#endif diff --git a/Transceiver52M/device/websdr/osmo-trx-websdr.h b/Transceiver52M/device/websdr/osmo-trx-websdr.h new file mode 100644 index 0000000..6248d11 --- /dev/null +++ b/Transceiver52M/device/websdr/osmo-trx-websdr.h @@ -0,0 +1,46 @@ +/* + * Osmo TRX WebSDR API + * + * Copyright (C) 2026 Wavelet Lab info@wavelet-lab.com + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef OSMO_TRX_WEBSDR_API_H +#define OSMO_TRX_WEBSDR_API_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int osmotrxlib_init(); +void osmotrxlib_deinit(); + +int osmotrxlib_process_command(const char* buffer, char *response, size_t response_size); + +// RX path +int osmotrxlib_push_rx_short_vector(short* buffers, int samples, int underrun, + unsigned ts, unsigned samples_skipped); +int osmotrxlib_get_rx_burst(char* buf, int maxLen); + +// TX path +int osmotrxlib_get_tx_short_vector(); +int osmotrxlib_put_tx_burst(char* buffer, int msgLen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Transceiver52M/device/websdr/radioInterfaceWebSdr.cpp b/Transceiver52M/device/websdr/radioInterfaceWebSdr.cpp new file mode 100644 index 0000000..7c440ab --- /dev/null +++ b/Transceiver52M/device/websdr/radioInterfaceWebSdr.cpp @@ -0,0 +1,110 @@ +/* + * WebSDR radio device implementation + * + * Copyright (C) 2008-2014 Free Software Foundation, Inc. + * Copyright (C) 2015 Ettus Research LLC + * Copyright (C) 2026 Wavelet Lab info@wavelet-lab.com + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * 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/. + * See the COPYING file in the main directory for details. + */ + +#include "radioInterfaceWebSdr.h" + +extern "C" { +#include "convert.h" +} + +#define CHUNK 625 +#define NUMCHUNKS 4 + +int RadioInterfaceWebSdr::driveReceiveRadioWithBuffer(unsigned samples_skipped) +{ + radioVector *burst = NULL; + + GSM::Time rcvClock = mClock.get(); + rcvClock.decTN(receiveOffset); + unsigned tN = rcvClock.TN(); + int recvSz = recvBuffer[0]->getAvailSamples(); + const int symbolsPerSlot = gSlotLen + 8; + int burstSize; + unsigned lost_tN = 0; + + if (mSPSRx == NUMCHUNKS) { + burstSize = CHUNK; + } else { + burstSize = symbolsPerSlot + (tN % NUMCHUNKS == 0); + } + + lost_tN = samples_skipped / burstSize; + + while (lost_tN-- != 0) { + mClock.incTN(); + rcvClock.incTN(); + } + + /* + * Pre-allocate head room for the largest correlation size + * so we can later avoid a re-allocation and copy + */ + size_t head = GSM::gRACHSynchSequenceTS0.size(); + + /* + * Form receive bursts and pass up to transceiver. Use repeating + * pattern of 157-156-156-156 symbols per timeslot + */ + while (recvSz > burstSize) { + for (size_t i = 0; i < mChans; i++) { + burst = new radioVector(rcvClock, burstSize, head); + unRadioifyVector(burst->getVector(), i); + + if (mReceiveFIFO[i].size() < 32) + mReceiveFIFO[i].write(burst); + else + delete burst; + } + + mClock.incTN(); + rcvClock.incTN(); + recvSz -= burstSize; + + tN = rcvClock.TN(); + + if (mSPSRx != NUMCHUNKS) + burstSize = (symbolsPerSlot + (tN % NUMCHUNKS == 0)) * mSPSRx; + } + + return 0; +} + +int RadioInterfaceWebSdr::fillPullBuffer(short **buffers, int samples, int recvunderrun, TIMESTAMP ts) +{ + if (recvBuffer[0]->getFreeSegments() <= 0) + return -EBUSY; + + size_t segmentLen = recvBuffer[0]->getSegmentLen(); + if (segmentLen != samples) + return -EINVAL; + + for (size_t i = 0; i < mChans; i++) { + convert_short_float(recvBuffer[i]->getWriteSegment(), buffers[i], segmentLen * 2); + } + + osmo_trx_sync_or_and_fetch(&underrun, recvunderrun); + readTimestamp += segmentLen; + + return 0; +} diff --git a/Transceiver52M/device/websdr/radioInterfaceWebSdr.h b/Transceiver52M/device/websdr/radioInterfaceWebSdr.h new file mode 100644 index 0000000..b9aa5ba --- /dev/null +++ b/Transceiver52M/device/websdr/radioInterfaceWebSdr.h @@ -0,0 +1,32 @@ +/* + * WebSDR radio device interface + * + * Copyright 2008 Free Software Foundation, Inc. + * Copyright 2026 Wavelet Lab info@wavelet-lab.com + * + * 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 RADIO_INTERFACE_WEBSDR_H +#define RADIO_INTERFACE_WEBSDR_H + +#include "radioInterface.h" + +class RadioInterfaceWebSdr : public RadioInterface { +public: + RadioInterfaceWebSdr(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps); + virtual ~RadioInterfaceWebSdr(); + + int fillPullBuffer(short** buffers, int samples, + int underrun, TIMESTAMP ts); + int driveReceiveRadioWithBuffer(unsigned samples_skipped); +}; + +#endif /* RADIO_INTERFACE_WEBSDR_H */ diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h index cdcfd07..5299a1e 100644 --- a/Transceiver52M/radioInterface.h +++ b/Transceiver52M/radioInterface.h @@ -57,7 +57,7 @@
bool mOn; ///< indicates radio is on
-private: +protected:
/** format samples to USRP */ int radioifyVector(signalVector &wVector, size_t chan, bool zero); diff --git a/configure.ac b/configure.ac index 01b2f34..7c88440 100644 --- a/configure.ac +++ b/configure.ac @@ -144,6 +144,11 @@ [enable bladeRF]) ])
+AC_ARG_WITH(websdr, [ + AS_HELP_STRING([--with-websdr], + [enable WebUSB based transceiver]) +]) + AC_ARG_WITH(mstrx, [ AS_HELP_STRING([--with-mstrx], [enable MS TRX]) @@ -282,6 +287,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"]) @@ -381,6 +387,8 @@ Transceiver52M/device/lms/Makefile \ Transceiver52M/device/ipc/Makefile \ Transceiver52M/device/bladerf/Makefile \ + Transceiver52M/device/websdr/Makefile \ + Transceiver52M/device/websdr/libosmo-trx-websdr.pc \ tests/Makefile \ tests/CommonLibs/Makefile \ tests/Transceiver52M/Makefile \