Compare commits

..

9 Commits

Author SHA1 Message Date
Eric
935c8cb7c9 new ms
Change-Id: I7c5abe57182e7ef508cac4068c0b41f905d39fd6
2022-07-11 20:33:37 +02:00
Eric
f590eeb436 blade ms support
Change-Id: Icbe9197f70f26619a35e1a849984b2f9f8996cca
2022-07-11 20:33:37 +02:00
Eric
4444e84206 bladerf support
This currently only supports the bladerf 2.0 with 4sps.

Change-Id: I5af0a241c8a174c18faf4952761df58140b29957
2022-07-11 20:33:37 +02:00
Eric
cb3a37a060 ignore vscode dirs
Change-Id: Iad9fd20924b7cfc6dbbfb708aa9c692a3cab574c
2022-07-11 20:33:37 +02:00
Eric
a1b77e9b24 xray ignores
tiny functions, do not want.

Change-Id: Ie55458f31d16e76e84855ed2c634a9dd9a5e139b
2022-07-11 20:33:37 +02:00
Eric
d9883b11a1 clean up mutex, scopedlock, and signal classes
This also uncovers very interesting design decisions like the copying of
mutexes and condition vars depending on recursive locks that were
previously hidden by shady c function calls..
We have perfectly good c++11 versions for all of that.

While we're at it, also use the initialization list for the other (still
copy constructable) vectors, which cleans up the radio interfaces.

Change-Id: Idc9e3b1144c5b93f5dad2f8e0e30f1058477aa52
2022-07-11 20:33:37 +02:00
Eric Wild
9d76313a1f vita works
-uhd fc32 like scaling
-adjusted start offset because our bursts do not start with 8 guard
symbols
2022-07-11 20:33:30 +02:00
Eric Wild
46bbdf0ace rename noisevector class -> avgvector
The vectors feature is averaging, and not adding noise.
2022-05-09 01:18:20 +02:00
Eric Wild
d40300ec63 4sps kinda works
fcch corr before sch sync close enough pointless?
2022-05-09 01:18:13 +02:00
51 changed files with 3343 additions and 2877 deletions

3
.gitignore vendored
View File

@@ -7,13 +7,10 @@ Transceiver52M/osmo-trx-usrp1
Transceiver52M/osmo-trx-lms Transceiver52M/osmo-trx-lms
Transceiver52M/osmo-trx-ipc Transceiver52M/osmo-trx-ipc
Transceiver52M/osmo-trx-blade Transceiver52M/osmo-trx-blade
Transceiver52M/osmo-trx-ipc2
Transceiver52M/osmo-trx-syncthing-blade Transceiver52M/osmo-trx-syncthing-blade
Transceiver52M/osmo-trx-syncthing-uhd Transceiver52M/osmo-trx-syncthing-uhd
Transceiver52M/osmo-trx-syncthing-ipc
Transceiver52M/osmo-trx-ms-blade Transceiver52M/osmo-trx-ms-blade
Transceiver52M/osmo-trx-ms-uhd Transceiver52M/osmo-trx-ms-uhd
Transceiver52M/osmo-trx-ms-ipc
.clang-format .clang-format

View File

@@ -45,6 +45,7 @@ struct trx_cfg {
enum ReferenceType clock_ref; enum ReferenceType clock_ref;
enum FillerType filler; enum FillerType filler;
bool multi_arfcn; bool multi_arfcn;
bool ms;
double offset; double offset;
double freq_offset_khz; double freq_offset_khz;
double rssi_offset; double rssi_offset;

View File

@@ -30,6 +30,7 @@ AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_C
noinst_LTLIBRARIES = libtransceiver_common.la noinst_LTLIBRARIES = libtransceiver_common.la
COMMON_SOURCES = \ COMMON_SOURCES = \
fbsb.cpp \
l1if.cpp \ l1if.cpp \
radioInterface.cpp \ radioInterface.cpp \
radioVector.cpp \ radioVector.cpp \
@@ -37,7 +38,6 @@ COMMON_SOURCES = \
radioBuffer.cpp \ radioBuffer.cpp \
sigProcLib.cpp \ sigProcLib.cpp \
signalVector.cpp \ signalVector.cpp \
Transceiver.cpp \
ChannelizerBase.cpp \ ChannelizerBase.cpp \
Channelizer.cpp \ Channelizer.cpp \
Synthesis.cpp \ Synthesis.cpp \
@@ -61,6 +61,7 @@ noinst_HEADERS = \
sigProcLib.h \ sigProcLib.h \
signalVector.h \ signalVector.h \
Transceiver.h \ Transceiver.h \
Transceiver2.h \
Resampler.h \ Resampler.h \
ChannelizerBase.h \ ChannelizerBase.h \
Channelizer.h \ Channelizer.h \
@@ -74,23 +75,15 @@ COMMON_LDADD = \
$(ARCH_LA) \ $(ARCH_LA) \
$(GSM_LA) \ $(GSM_LA) \
$(COMMON_LA) \ $(COMMON_LA) \
$(TRXCON_LA) \
$(FFTWF_LIBS) \ $(FFTWF_LIBS) \
$(LIBOSMOCORE_LIBS) \ $(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODING_LIBS) \
$(LIBOSMOCTRL_LIBS) \ $(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS) $(LIBOSMOVTY_LIBS)
bin_PROGRAMS = bin_PROGRAMS =
if DEVICE_UHD if DEVICE_UHD
bin_PROGRAMS += osmo-trx-uhd
osmo_trx_uhd_SOURCES = osmo-trx.cpp
osmo_trx_uhd_LDADD = \
$(builddir)/device/uhd/libdevice.la \
$(COMMON_LDADD) \
$(UHD_LIBS)
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
bin_PROGRAMS += osmo-trx-ms-uhd bin_PROGRAMS += osmo-trx-ms-uhd
osmo_trx_ms_uhd_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_upper.cpp ms/ms_commandhandler.cpp osmo_trx_ms_uhd_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_upper.cpp ms/ms_commandhandler.cpp
osmo_trx_ms_uhd_LDADD = \ osmo_trx_ms_uhd_LDADD = \
@@ -101,44 +94,14 @@ osmo_trx_ms_uhd_LDADD = \
osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
bin_PROGRAMS += osmo-trx-syncthing-uhd bin_PROGRAMS += osmo-trx-syncthing-uhd
osmo_trx_syncthing_uhd_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp osmo_trx_syncthing_uhd_SOURCES = $(osmo_trx_ms_uhd_SOURCES) ms/ms_rx_burst.cpp
osmo_trx_syncthing_uhd_LDADD = \ osmo_trx_syncthing_uhd_LDADD = $(osmo_trx_ms_uhd_LDADD)
$(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \
$(UHD_LIBS)
osmo_trx_syncthing_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DSYNCTHINGONLY -DBUILDUHD osmo_trx_syncthing_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DSYNCTHINGONLY -DBUILDUHD
#osmo_trx_syncthing_LDFLAGS = -fsanitize=address,undefined -shared-libsan #osmo_trx_syncthing_LDFLAGS = -fsanitize=address,undefined -shared-libsan
endif
if DEVICE_USRP1
bin_PROGRAMS += osmo-trx-usrp1
osmo_trx_usrp1_SOURCES = osmo-trx.cpp
osmo_trx_usrp1_LDADD = \
$(builddir)/device/usrp1/libdevice.la \
$(COMMON_LDADD) \
$(USRP_LIBS)
osmo_trx_usrp1_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS)
endif
if DEVICE_LMS
bin_PROGRAMS += osmo-trx-lms
osmo_trx_lms_SOURCES = osmo-trx.cpp
osmo_trx_lms_LDADD = \
$(builddir)/device/lms/libdevice.la \
$(COMMON_LDADD) \
$(LMS_LIBS)
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
endif endif
if DEVICE_BLADE if DEVICE_BLADE
bin_PROGRAMS += osmo-trx-blade
osmo_trx_blade_SOURCES = osmo-trx.cpp
osmo_trx_blade_LDADD = \
$(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \
$(BLADE_LIBS)
osmo_trx_blade_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
bin_PROGRAMS += osmo-trx-ms-blade bin_PROGRAMS += osmo-trx-ms-blade
osmo_trx_ms_blade_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_upper.cpp ms/ms_commandhandler.cpp osmo_trx_ms_blade_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_upper.cpp ms/ms_commandhandler.cpp
osmo_trx_ms_blade_LDADD = \ osmo_trx_ms_blade_LDADD = \
@@ -149,48 +112,19 @@ osmo_trx_ms_blade_LDADD = \
osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
bin_PROGRAMS += osmo-trx-syncthing-blade bin_PROGRAMS += osmo-trx-syncthing-blade
osmo_trx_syncthing_blade_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp osmo_trx_syncthing_blade_SOURCES = $(osmo_trx_ms_blade_SOURCES) ms/ms_rx_burst.cpp
osmo_trx_syncthing_blade_LDADD = \ osmo_trx_syncthing_blade_LDADD = $(osmo_trx_ms_blade_LDADD)
$(builddir)/device/bladerf/libdevice.la \ osmo_trx_syncthing_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DSYNCTHINGONLY -DBUILDBLADE -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8
$(COMMON_LDADD) \
$(BLADE_LIBS)
osmo_trx_syncthing_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DSYNCTHINGONLY -DBUILDBLADE -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8 -I../device/ipc
#osmo_trx_syncthing_LDFLAGS = -fsanitize=address,undefined -shared-libsan #osmo_trx_syncthing_LDFLAGS = -fsanitize=address,undefined -shared-libsan
endif endif
if DEVICE_IPC
bin_PROGRAMS += osmo-trx-ipc
osmo_trx_ipc_SOURCES = osmo-trx.cpp
osmo_trx_ipc_LDADD = \
$(builddir)/device/ipc/libdevice.la \
$(COMMON_LDADD)
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)
bin_PROGRAMS += osmo-trx-ipc2
osmo_trx_ipc2_SOURCES = osmo-trx.cpp
osmo_trx_ipc2_LDADD = \
$(builddir)/device/ipc2/libdevice.la \
$(COMMON_LDADD)
osmo_trx_ipc2_CPPFLAGS = $(AM_CPPFLAGS)
bin_PROGRAMS += osmo-trx-ms-ipc
osmo_trx_ms_ipc_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_upper.cpp ms/ms_commandhandler.cpp
osmo_trx_ms_ipc_LDADD = \
$(COMMON_LDADD) \
$(TRXCON_LA)
osmo_trx_ms_ipc_CPPFLAGS = $(AM_CPPFLAGS) -DBUILDIPC -I./device/ipc2 -I../device/ipc2
bin_PROGRAMS += osmo-trx-syncthing-ipc
osmo_trx_syncthing_ipc_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
osmo_trx_syncthing_ipc_LDADD = $(COMMON_LDADD)
osmo_trx_syncthing_ipc_CPPFLAGS = $(AM_CPPFLAGS) -DSYNCTHINGONLY -DBUILDIPC -I./device/ipc2 -I../device/ipc2
endif
noinst_HEADERS += \ noinst_HEADERS += \
ms/syncthing.h \ ms/syncthing.h \
ms/bladerf_specific.h \ ms/bladerf_specific.h \
ms/uhd_specific.h \ ms/uhd_specific.h \
ms/ms_rx_upper.h \ ms/ms_rx_upper.h \
ms/ms_state.h \
itrq.h itrq.h
# -fsanitize=address,undefined -shared-libsan -O0 # -fsanitize=address,undefined -shared-libsan -O0
# #

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,258 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* 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 "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
#include <sys/types.h>
#include <sys/socket.h>
extern "C" {
#include <osmocom/core/signal.h>
#include <osmocom/core/select.h>
#include "config_defs.h"
}
class Transceiver2;
/** Channel descriptor for transceiver object and channel number pair */
struct TransceiverChannel {
TransceiverChannel(Transceiver2 *trx, int num)
{
this->trx = trx;
this->num = num;
}
~TransceiverChannel()
{
}
Transceiver2 *trx;
size_t num;
};
/** Internal transceiver state variables */
struct TransceiverState {
TransceiverState();
~TransceiverState();
/* Initialize a multiframe slot in the filler table */
void init(size_t slot, signalVector *burst, bool fill);
int chanType[8];
/* The filler table */
signalVector *fillerTable[102][8];
int fillerModulus[8];
bool mRetrans;
/* Received noise energy levels */
avgVector mFreqOffsets;
/* Store pointers to previous frame */
radioVector *prevFrame[8];
/* Transceiver mode */
int mode;
};
/** The Transceiver class, responsible for physical layer of basestation */
class Transceiver2 {
private:
size_t mChans;
int rx_sps, tx_sps;
std::string mAddr;
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
Thread *mLowerLoopThread; ///< thread to pull bursts into receive FIFO
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
RadioInterface *mRadioInterface; ///< associated radioInterface object
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
/** Update filler table */
void updateFillerTable(size_t chan, radioVector *burst);
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI,
int &timingOffset, size_t chan = 0);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
/** return the expected burst type for the specified timestamp */
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
/** send messages over the clock socket */
void writeClockInterface(void);
bool detectSCH(TransceiverState *state,
signalVector &burst,
struct estim_burst_params *ebp);
bool decodeSCH(SoftVector *burst, GSM::Time *time);
bool correctFCCH(TransceiverState *state, signalVector *burst);
bool mOn; ///< flag to indicate that transceiver is powered on
double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency
int mPower; ///< the transmit power in dB
unsigned mTSC; ///< the midamble sequence code
unsigned mMaxExpectedDelay; ///< maximum TOA offset in GSM symbols
unsigned long long mRxSlotMask[8]; ///< MS - enabled multiframe slot mask
int mBSIC; ///< MS - detected BSIC
std::vector<TransceiverState> mStates;
public:
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX manager, as a string
@param wSPS number of samples per GSM symbol
@param wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
Transceiver2(int wBasePort,
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
/** Destructor */
~Transceiver2();
/** start the Transceiver */
void start();
bool init(bool filler);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
{
if (chan >= mReceiveFIFO.size())
return false;
mReceiveFIFO[chan] = wFIFO;
return true;
}
/** accessor for number of channels */
size_t numChans() const { return mChans; };
/** Codes for channel combinations */
typedef enum {
FILL, ///< Channel is transmitted, but unused
I, ///< TCH/FS
II, ///< TCH/HS, idle every other slot
III, ///< TCH/HS
IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
VI, ///< CCCH+BCCH, uplink RACH
VII, ///< SDCCH/8 + SACCH/8
VIII, ///< TCH/F + FACCH/F + SACCH/M
IX, ///< TCH/F + SACCH/M
X, ///< TCH/FD + SACCH/MD
XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
XII, ///< PCCCH+PDTCH+PACCH+PTCCH
XIII, ///< PDTCH+PACCH+PTCCH
NONE, ///< Channel is inactive, default
LOOPBACK ///< similar go VII, used in loopback testing
} ChannelCombination;
enum {
TRX_MODE_OFF,
TRX_MODE_BTS,
TRX_MODE_MS_ACQUIRE,
TRX_MODE_MS_TRACK,
};
void commandhandler(char *buffer, char *response, int chan);
void stop();
protected:
/** drive lower receive I/O and burst generation */
void driveReceiveRadio();
/** drive demodulation of GSM bursts */
void driveReceiveFIFO(size_t chan);
/** drive transmission of GSM bursts */
void driveTxFIFO();
/** drive handling of control messages from GSM core */
void driveControl(size_t chan);
/**
drive modulation and sorting of GSM bursts from GSM core
@return true if a burst was transferred successfully
*/
bool driveTxPriorityQueue(size_t chan);
friend void *RxUpperLoopAdapter(TransceiverChannel *);
friend void *TxUpperLoopAdapter(TransceiverChannel *);
friend void *LowerLoopAdapter(Transceiver2 *);
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
void reset();
/** set priority on current thread */
//void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
};
void *RxUpperLoopAdapter(TransceiverChannel *);
/** Main drive threads */
void *LowerLoopAdapter(Transceiver2 *);
/** control message handler thread loop */
void *ControlServiceLoopAdapter(TransceiverChannel *);
/** transmit queueing thread loop */
void *TxUpperLoopAdapter(TransceiverChannel *);

View File

@@ -3,7 +3,7 @@ include $(top_srcdir)/Makefile.common
SUBDIRS = common SUBDIRS = common
if DEVICE_IPC if DEVICE_IPC
SUBDIRS += ipc ipc2 SUBDIRS += ipc
endif endif
if DEVICE_USRP1 if DEVICE_USRP1

View File

@@ -249,7 +249,7 @@ void blade_device::set_rates()
{ {
//dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)); //dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
struct bladerf_rational_rate rate = {0, static_cast<uint64_t>((1625e3 * 4)), 6}, actual; struct bladerf_rational_rate rate = {0, static_cast<uint64_t>((1625e3 * 4))*64, 6*64}, actual;
auto status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rate, &actual); auto status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
CHKRET() CHKRET()
status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0), &rate, &actual); status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
@@ -423,6 +423,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
LOGC(DDEV, INFO) << "Selected clock source is " << ((ref == REF_INTERNAL) ? "internal" : "external 10Mhz"); LOGC(DDEV, INFO) << "Selected clock source is " << ((ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
bladerf_set_tuning_mode(dev, BLADERF_TUNING_MODE_FPGA);
set_rates(); set_rates();
/* /*
@@ -547,7 +548,7 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
if (rc < 0) { if (rc < 0) {
LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc); LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp); LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
return 0; return len;
} }
@@ -684,30 +685,35 @@ bool blade_device::setTxFreq(double wFreq, size_t chan)
return true; return true;
} }
bool blade_device::setRxOffset(double wOffset, size_t chan)
{
return true;
}
bool blade_device::setRxFreq(double wFreq, size_t chan) bool blade_device::setRxFreq(double wFreq, size_t chan)
{ {
uint16_t req_arfcn; // uint16_t req_arfcn;
enum gsm_band req_band; // enum gsm_band req_band;
if (chan >= rx_freqs.size()) { // if (chan >= rx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; // LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false; // return false;
} // }
ScopedLock lock(tune_lock); // ScopedLock lock(tune_lock);
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1); // req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
if (req_arfcn == 0xffff) { // if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz"; // LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
return false; // return false;
} // }
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) { // if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq // LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )"; // << " Hz (ARFCN " << req_arfcn << " )";
return false; // return false;
} // }
if (!set_band(req_band)) // if (!set_band(req_band))
return false; // return false;
return set_freq(wFreq, chan, false); return set_freq(wFreq, chan, false);
} }

View File

@@ -97,6 +97,7 @@ public:
int getNominalTxPower(size_t chan = 0); int getNominalTxPower(size_t chan = 0);
bool setRxOffset(double wOffset, size_t chan);
double getTxFreq(size_t chan); double getTxFreq(size_t chan);
double getRxFreq(size_t chan); double getRxFreq(size_t chan);
double getRxFreq(); double getRxFreq();

View File

@@ -101,6 +101,9 @@ class RadioDevice {
/** Set the receiver frequency */ /** Set the receiver frequency */
virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0; virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
/** Adjust the receiver offset */
virtual bool setRxOffset(double wOffset, size_t chan = 0) = 0;
/** Returns the starting write Timestamp*/ /** Returns the starting write Timestamp*/
virtual TIMESTAMP initialWriteTimestamp(void)=0; virtual TIMESTAMP initialWriteTimestamp(void)=0;

View File

@@ -224,6 +224,8 @@ class IPCDevice : public RadioDevice {
/** return whether user drives synchronization of Tx/Rx of USRP */ /** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency() override; virtual GSM::Time minLatency() override;
bool setRxOffset(double wOffset, size_t chan = 0) override {return true;}
/** Return internal status values */ /** Return internal status values */
virtual inline double getTxFreq(size_t chan = 0) override virtual inline double getTxFreq(size_t chan = 0) override
{ {

View File

@@ -1,314 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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 <sys/time.h>
#include <osmocom/core/timer_compat.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "Logger.h"
#include "Threads.h"
#include "IPCDevice.h"
#include "smpl_buf.h"
#define SAMPLE_BUF_SZ (1 << 20)
static const auto ONE_BIT_DURATION ((12./5200.)/(156.25*4.));
static const auto ONE_SAMPLE_DURATION_US ((ONE_BIT_DURATION/4.)*1000*1000);
using namespace std;
IPCDevice2::IPCDevice2(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), rx_buffers(chans),
started(false), tx_gains(chans), rx_gains(chans)
{
LOGC(DDEV, INFO) << "creating IPC device...";
if (!(tx_sps == 4) || !(rx_sps == 4)) {
LOGC(DDEV, FATAL) << "IPC shm if create failed!";
exit(0);
}
/* Set up per-channel Rx timestamp based Ring buffers */
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
if (!m.create()) {
LOGC(DDEV, FATAL) << "IPC shm if create failed!";
exit(0);
}
}
IPCDevice2::~IPCDevice2()
{
LOGC(DDEV, INFO) << "Closing IPC device";
/* disable all channels */
for (size_t i = 0; i < rx_buffers.size(); i++)
delete rx_buffers[i];
}
int IPCDevice2::open(const std::string &args, int ref, bool swap_channels)
{
std::string k, v;
/* configure antennas */
if (!set_antennas()) {
LOGC(DDEV, FATAL) << "IPC antenna setting failed";
goto out_close;
}
return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
out_close:
LOGC(DDEV, FATAL) << "Error in IPC open, closing";
return -1;
}
bool IPCDevice2::start()
{
LOGC(DDEV, INFO) << "starting IPC...";
if (started) {
LOGC(DDEV, ERR) << "Device already started";
return true;
}
int max_bufs_to_flush = 120;
flush_recv(max_bufs_to_flush);
started = true;
return true;
}
bool IPCDevice2::stop()
{
if (!started)
return true;
LOGC(DDEV, NOTICE) << "All channels stopped, terminating...";
/* reset internal buffer timestamps */
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i]->reset();
started = false;
return true;
}
double IPCDevice2::maxRxGain()
{
return 70;
}
double IPCDevice2::minRxGain()
{
return 0;
}
int IPCDevice2::getNominalTxPower(size_t chan)
{
return 10;
}
double IPCDevice2::setPowerAttenuation(int atten, size_t chan)
{
return atten;
}
double IPCDevice2::getPowerAttenuation(size_t chan)
{
return 0;
}
double IPCDevice2::setRxGain(double dB, size_t chan)
{
if (dB > maxRxGain())
dB = maxRxGain();
if (dB < minRxGain())
dB = minRxGain();
LOGCHAN(chan, DDEV, NOTICE) << "Setting RX gain to " << dB << " dB";
return dB;
}
bool IPCDevice2::flush_recv(size_t num_pkts)
{
ts_initial = 10000;
LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
return true;
}
bool IPCDevice2::setRxAntenna(const std::string &ant, size_t chan)
{
return true;
}
std::string IPCDevice2::getRxAntenna(size_t chan)
{
return "";
}
bool IPCDevice2::setTxAntenna(const std::string &ant, size_t chan)
{
return true;
}
std::string IPCDevice2::getTxAntenna(size_t chan)
{
return "";
}
bool IPCDevice2::requiresRadioAlign()
{
return false;
}
GSM::Time IPCDevice2::minLatency()
{
/* UNUSED */
return GSM::Time(0, 0);
}
/** Returns the starting write Timestamp*/
TIMESTAMP IPCDevice2::initialWriteTimestamp(void)
{
return ts_initial;
}
/** Returns the starting read Timestamp*/
TIMESTAMP IPCDevice2::initialReadTimestamp(void)
{
return ts_initial;
}
static timespec readtime, writetime;
static void wait_for_sample_time(timespec* last, unsigned int len) {
timespec ts, diff;
clock_gettime(CLOCK_MONOTONIC, &ts);
timespecsub(&ts, last, &diff);
auto elapsed_us = (diff.tv_sec * 1000000) + (diff.tv_nsec / 1000);
auto max_wait_us = ONE_SAMPLE_DURATION_US * len;
if(elapsed_us < max_wait_us)
usleep(max_wait_us-elapsed_us);
*last = ts;
}
// NOTE: Assumes sequential reads
int IPCDevice2::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun)
{
int rc, num_smpls; //, expect_smpls;
ssize_t avail_smpls;
unsigned int i = 0;
*overrun = false;
*underrun = false;
timestamp += 0;
/* Check that timestamp is valid */
rc = rx_buffers[0]->avail_smpls(timestamp);
if (rc < 0) {
LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
return 0;
}
/* Receive samples from HW until we have enough */
while ((avail_smpls = rx_buffers[i]->avail_smpls(timestamp)) < len) {
uint64_t recv_timestamp = timestamp;
m.read_ul(len - avail_smpls, &recv_timestamp, reinterpret_cast<sample_t *>(bufs[0]));
num_smpls = len - avail_smpls;
wait_for_sample_time(&readtime, num_smpls);
if (num_smpls == -ETIMEDOUT)
continue;
LOGCHAN(i, DDEV, DEBUG)
"Received timestamp = " << (TIMESTAMP)recv_timestamp << " (" << num_smpls << ")";
rc = rx_buffers[i]->write(bufs[i], num_smpls, (TIMESTAMP)recv_timestamp);
if (rc < 0) {
LOGCHAN(i, DDEV, ERROR)
<< rx_buffers[i]->str_code(rc) << " num smpls: " << num_smpls << " chan: " << i;
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
if (rc != smpl_buf::ERROR_OVERFLOW)
return 0;
}
}
/* We have enough samples */
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
if ((rc < 0) || (rc != len)) {
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". " << rx_buffers[i]->str_status(timestamp)
<< ", (len=" << len << ")";
return 0;
}
return len;
}
int IPCDevice2::writeSamples(std::vector<short *> &bufs, int len, bool *underrun, unsigned long long timestamp)
{
*underrun = false;
LOGCHAN(0, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << timestamp;
// rc = ipc_shm_enqueue(shm_io_tx_streams[i], timestamp, len, (uint16_t *)bufs[i]);
m.write_dl(len, timestamp, reinterpret_cast<sample_t *>(bufs[0]));
wait_for_sample_time(&writetime, len);
return 0;
}
bool IPCDevice2::updateAlignment(TIMESTAMP timestamp)
{
return true;
}
bool IPCDevice2::setTxFreq(double wFreq, size_t chan)
{
return true;
}
bool IPCDevice2::setRxFreq(double wFreq, size_t chan)
{
return true;
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
{
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
return NULL;
}
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
return NULL;
}
return new IPCDevice2(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -1,186 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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.
*/
#ifndef _IPC_DEVICE_H_
#define _IPC_DEVICE_H_
#include <climits>
#include <string>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include "ipcif.h"
class smpl_buf;
class IPCDevice2 : public RadioDevice {
trxmsif m;
protected:
std::vector<smpl_buf *> rx_buffers;
double actualSampleRate;
bool started;
TIMESTAMP ts_initial;
std::vector<double> tx_gains, rx_gains;
bool flush_recv(size_t num_pkts);
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
bool send_chan_wait_rsp(uint32_t chan, struct msgb *msg_to_send, uint32_t expected_rsp_msg_id);
bool send_all_chan_wait_rsp(uint32_t msgid_to_send, uint32_t msgid_to_expect);
public:
/** Object constructor */
IPCDevice2(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
virtual ~IPCDevice2() override;
/** Instantiate the IPC */
virtual int open(const std::string &args, int ref, bool swap_channels) override;
/** Start the IPC */
virtual bool start() override;
/** Stop the IPC */
virtual bool stop() override;
/* FIXME: any != USRP1 will do for now... */
enum TxWindowType getWindowType() override
{
return TX_WINDOW_FIXED;
}
/**
Read samples from the IPC.
@param buf preallocated buf to contain read result
@param len number of samples desired
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
@return The number of samples actually read
*/
virtual int readSamples(std::vector<short *> &buf, int len, bool *overrun, TIMESTAMP timestamp = 0xffffffff,
bool *underrun = NULL) override;
/**
Write samples to the IPC.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@return The number of samples actually written
*/
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff) override;
/** Update the alignment between the read and write timestamps */
virtual bool updateAlignment(TIMESTAMP timestamp) override;
/** Set the transmitter frequency */
virtual bool setTxFreq(double wFreq, size_t chan = 0) override;
/** Set the receiver frequency */
virtual bool setRxFreq(double wFreq, size_t chan = 0) override;
/** Returns the starting write Timestamp*/
virtual TIMESTAMP initialWriteTimestamp(void) override;
/** Returns the starting read Timestamp*/
virtual TIMESTAMP initialReadTimestamp(void) override;
/** returns the full-scale transmit amplitude **/
virtual double fullScaleInputValue() override
{
return (double)SHRT_MAX * 1;
}
/** returns the full-scale receive amplitude **/
virtual double fullScaleOutputValue() override
{
return (double)SHRT_MAX * 1;
}
/** sets the receive chan gain, returns the gain setting **/
virtual double setRxGain(double dB, size_t chan = 0) override;
/** get the current receive gain */
virtual double getRxGain(size_t chan = 0) override
{
return rx_gains[chan];
}
/** return maximum Rx Gain **/
virtual double maxRxGain(void) override;
/** return minimum Rx Gain **/
virtual double minRxGain(void) override;
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
double rssiOffset(size_t chan) override
{
return 0.0f;
};
double setPowerAttenuation(int atten, size_t chan) override;
double getPowerAttenuation(size_t chan = 0) override;
virtual int getNominalTxPower(size_t chan = 0) override;
/** sets the RX path to use, returns true if successful and false otherwise */
virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) override;
/* return the used RX path */
virtual std::string getRxAntenna(size_t chan = 0) override;
/** sets the RX path to use, returns true if successful and false otherwise */
virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) override;
/* return the used RX path */
virtual std::string getTxAntenna(size_t chan = 0) override;
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual bool requiresRadioAlign() override;
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency() override;
/** Return internal status values */
virtual inline double getTxFreq(size_t chan = 0) override
{
return 0;
}
virtual inline double getRxFreq(size_t chan = 0) override
{
return 0;
}
virtual inline double getSampleRate() override
{
return actualSampleRate;
}
};
#endif // _IPC_DEVICE_H_

View File

@@ -1,14 +0,0 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
AM_LDFLAGS = -lpthread -lrt
noinst_HEADERS = IPCDevice.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = IPCDevice.cpp
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC

View File

@@ -1,161 +0,0 @@
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* 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/>.
*
*/
#pragma once
#include <atomic>
#include <complex>
#include <cassert>
#include "shmif.h"
const int max_ul_rdlen = 1024 * 10;
const int max_dl_rdlen = 1024 * 10;
using sample_t = std::complex<int16_t>;
struct shm_if {
std::atomic<bool> ms_connected;
struct {
shm::shmmutex m;
shm::shmcond c;
std::atomic<uint64_t> ts;
std::atomic<size_t> len_req; // <-
std::atomic<size_t> len_written; // ->
sample_t buffer[max_ul_rdlen];
} ul;
struct {
shm::shmmutex writemutex;
shm::shmcond rdy2write;
shm::shmmutex readmutex;
shm::shmcond rdy2read;
std::atomic<uint64_t> ts;
std::atomic<size_t> len_req;
std::atomic<size_t> len_written;
sample_t buffer[max_dl_rdlen];
} dl;
};
// unique up to signed_type/2 diff
template <typename A> auto unsigned_diff(A a, A b) -> typename std::make_signed<A>::type
{
using stype = typename std::make_signed<A>::type;
return (a > b) ? static_cast<stype>(a - b) : -static_cast<stype>(b - a);
};
class trxmsif {
shm::shm<shm_if> m;
shm_if *ptr;
int dl_readoffset;
int samp2byte(int v)
{
return v * sizeof(sample_t);
}
public:
trxmsif() : m("trx-ms-if"), dl_readoffset(0)
{
}
bool create()
{
m.create();
ptr = m.p();
return m.isgood();
}
bool connect()
{
m.open();
ptr = m.p();
ptr->ms_connected = true;
return m.isgood();
}
bool good()
{
return m.isgood();
}
void write_dl(size_t howmany, uint64_t write_ts, sample_t *inbuf)
{
auto &dl = ptr->dl;
auto buf = &dl.buffer[0];
// if (ptr->ms_connected != true)
// return;
assert(sizeof(dl.buffer) >= samp2byte(howmany));
{
shm::signal_guard g(dl.writemutex, dl.rdy2write, dl.rdy2read);
memcpy(buf, inbuf, samp2byte(howmany));
dl.ts = write_ts;
dl.len_written = howmany;
}
}
void read_dl(size_t howmany, uint64_t* read_ts, sample_t *outbuf)
{
auto &dl = ptr->dl;
auto buf = &dl.buffer[0];
size_t len_avail = dl.len_written;
uint64_t ts = dl.ts;
auto left_to_read = len_avail - dl_readoffset;
// no data, wait for new buffer, maybe some data left afterwards
if (!left_to_read) {
shm::signal_guard g(dl.readmutex, dl.rdy2read, dl.rdy2write);
*read_ts = dl.ts;
len_avail = dl.len_written;
dl_readoffset += howmany;
assert(len_avail >= howmany);
memcpy(outbuf, buf, samp2byte(howmany));
return;
}
*read_ts = dl.ts + dl_readoffset;
left_to_read = len_avail - dl_readoffset;
// data left from prev read
if (left_to_read >= howmany) {
memcpy(outbuf, buf, samp2byte(howmany));
dl_readoffset += howmany;
return;
} else {
memcpy(outbuf, buf, samp2byte(left_to_read));
dl_readoffset = 0;
auto still_left_to_read = howmany - left_to_read;
{
shm::signal_guard g(dl.readmutex, dl.rdy2read, dl.rdy2write);
len_avail = dl.len_written;
dl_readoffset += still_left_to_read;
assert(len_avail >= still_left_to_read);
memcpy(outbuf + left_to_read, buf, samp2byte(still_left_to_read));
}
}
}
void read_ul(size_t howmany, uint64_t* read_ts, sample_t *outbuf)
{
// if (ptr->ms_connected != true) {
memset(outbuf, 0, samp2byte(howmany));
return;
// }
}
};

View File

@@ -1,219 +0,0 @@
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* 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/>.
*
*/
#pragma once
#include <cstring>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <cerrno>
namespace shm
{
class shmmutex {
pthread_mutex_t mutex;
public:
shmmutex()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
~shmmutex()
{
pthread_mutex_destroy(&mutex);
}
void lock()
{
pthread_mutex_lock(&mutex);
}
bool try_lock()
{
return pthread_mutex_trylock(&mutex);
}
void unlock()
{
pthread_mutex_unlock(&mutex);
}
pthread_mutex_t *p()
{
return &mutex;
}
};
class shmcond {
pthread_cond_t cond;
public:
shmcond()
{
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&cond, &attr);
pthread_condattr_destroy(&attr);
}
~shmcond()
{
pthread_cond_destroy(&cond);
}
void wait(shmmutex *lock)
{
pthread_cond_wait(&cond, lock->p());
}
void signal()
{
pthread_cond_signal(&cond);
}
void signal_all()
{
pthread_cond_broadcast(&cond);
}
};
template <typename IFT> class shm {
char shmname[512];
size_t IFT_sz = sizeof(IFT);
IFT *shmptr;
bool good;
int ipc_shm_setup(const char *shm_name)
{
int fd;
int rc;
void *ptr;
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
rc = -errno;
return rc;
}
if (ftruncate(fd, IFT_sz) < 0) {
rc = -errno;
shm_unlink(shm_name);
::close(fd);
}
if ((ptr = mmap(NULL, IFT_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
rc = -errno;
shm_unlink(shm_name);
::close(fd);
}
shmptr = new (ptr) IFT(); //static_cast<IFT *>(ptr);
::close(fd);
return 0;
}
int ipc_shm_connect(const char *shm_name)
{
int fd;
int rc;
void *ptr;
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) < 0) {
rc = -errno;
return rc;
}
struct stat shm_stat;
if (fstat(fd, &shm_stat) < 0) {
rc = -errno;
shm_unlink(shm_name);
::close(fd);
}
if ((ptr = mmap(NULL, shm_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
rc = -errno;
shm_unlink(shm_name);
::close(fd);
}
shmptr = static_cast<IFT *>(ptr);
::close(fd);
return 0;
}
public:
using IFT_t = IFT;
explicit shm(const char *name) : good(false)
{
strncpy((char *)shmname, name, 512);
}
void create()
{
if (ipc_shm_setup(shmname) == 0)
good = true;
}
void open()
{
if (ipc_shm_connect(shmname) == 0)
good = true;
}
bool isgood() const
{
return good;
}
void close()
{
if (isgood())
shm_unlink(shmname);
}
IFT *p()
{
return shmptr;
}
};
class signal_guard {
shmmutex &m;
shmcond &s;
public:
explicit signal_guard(shmmutex &m, shmcond &wait_for, shmcond &to_signal) : m(m), s(to_signal)
{
m.lock();
wait_for.wait(&m);
}
~signal_guard()
{
s.signal();
m.unlock();
}
};
} // namespace shm

View File

@@ -1 +0,0 @@
#include "../uhd/UHDDevice.cpp"

View File

@@ -1,255 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Eric Wild <ewild@sysmocom.de>
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
*/
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include "shm.h"
#include "ipc_shm.h"
#include "ipc-driver-test.h"
}
#include "../uhd/UHDDevice.h"
#include "uhdwrap.h"
#include "Logger.h"
#include "Threads.h"
#include "Utils.h"
int uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
{
int rv = uhd_device::open(args, ref, swap_channels);
samps_per_buff_rx = rx_stream->get_max_num_samps();
samps_per_buff_tx = tx_stream->get_max_num_samps();
channel_count = usrp_dev->get_rx_num_channels();
wrap_rx_buffs = std::vector<std::vector<short> >(channel_count, std::vector<short>(2 * samps_per_buff_rx));
for (size_t i = 0; i < wrap_rx_buffs.size(); i++)
wrap_rx_buf_ptrs.push_back(&wrap_rx_buffs[i].front());
wrap_tx_buffs = std::vector<std::vector<short> >(channel_count, std::vector<short>(2 * 5000));
for (size_t i = 0; i < wrap_tx_buffs.size(); i++)
wrap_tx_buf_ptrs.push_back(&wrap_tx_buffs[i].front());
return rv;
}
uhd_wrap::~uhd_wrap()
{
// drvtest::gshutdown = 1;
//t->join();
}
size_t uhd_wrap::bufsizerx()
{
return samps_per_buff_rx;
}
size_t uhd_wrap::bufsizetx()
{
return samps_per_buff_tx;
}
int uhd_wrap::chancount()
{
return channel_count;
}
int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
{
uhd::rx_metadata_t md;
size_t num_rx_samps = rx_stream->recv(wrap_rx_buf_ptrs, samps_per_buff_rx, md, 0.1, true);
*timestamp = md.time_spec.to_ticks(rx_rate);
return num_rx_samps; //uhd_device::readSamples(bufs, len, overrun, timestamp, underrun);
}
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
{
unsigned int rx_sps, tx_sps;
/* FIXME: dev arg string* */
/* FIXME: rx frontend bw? */
/* FIXME: tx frontend bw? */
ReferenceType cref;
switch (open_req->clockref) {
case FEATURE_MASK_CLOCKREF_EXTERNAL:
cref = ReferenceType::REF_EXTERNAL;
break;
case FEATURE_MASK_CLOCKREF_INTERNAL:
default:
cref = ReferenceType::REF_INTERNAL;
break;
}
std::vector<std::string> tx_paths;
std::vector<std::string> rx_paths;
for (unsigned int i = 0; i < open_req->num_chans; i++) {
tx_paths.push_back(open_req->chan_info[i].tx_path);
rx_paths.push_back(open_req->chan_info[i].rx_path);
}
/* FIXME: this is actually the sps value, not the sample rate!
* sample rate is looked up according to the sps rate by uhd backend */
rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
uhd_wrap *uhd_wrap_dev =
new uhd_wrap(tx_sps, rx_sps, RadioDevice::NORMAL, open_req->num_chans, 0.0, tx_paths, rx_paths);
uhd_wrap_dev->open("", cref, false);
return uhd_wrap_dev;
}
extern "C" int32_t uhdwrap_get_bufsizerx(void *dev)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->bufsizerx();
}
extern "C" int32_t uhdwrap_get_timingoffset(void *dev)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->getTimingOffset();
}
extern "C" int32_t uhdwrap_read(void *dev, uint32_t num_chans)
{
TIMESTAMP t;
uhd_wrap *d = (uhd_wrap *)dev;
if (num_chans != d->wrap_rx_buf_ptrs.size()) {
perror("omg chans?!");
}
int32_t read = d->wrap_read(&t);
/* multi channel rx on b210 will return 0 due to alignment adventures, do not put 0 samples into a ipc buffer... */
if (read <= 0)
return read;
for (uint32_t i = 0; i < num_chans; i++) {
ipc_shm_enqueue(ios_rx_from_device[i], t, read, (uint16_t *)&d->wrap_rx_buffs[i].front());
}
return read;
}
extern "C" int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun)
{
uhd_wrap *d = (uhd_wrap *)dev;
uint64_t timestamp;
int32_t len = -1;
for (uint32_t i = 0; i < num_chans; i++) {
len = ipc_shm_read(ios_tx_to_device[i], (uint16_t *)&d->wrap_tx_buffs[i].front(), 5000, &timestamp, 1);
if (len < 0)
return 0;
}
return d->writeSamples(d->wrap_tx_buf_ptrs, len, underrun, timestamp);
}
extern "C" double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx)
{
uhd_wrap *d = (uhd_wrap *)dev;
if (for_tx)
return d->setTxFreq(f, chan);
else
return d->setRxFreq(f, chan);
}
extern "C" double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx)
{
uhd_wrap *d = (uhd_wrap *)dev;
// if (for_tx)
// return d->setTxGain(f, chan);
// else
return d->setRxGain(f, chan);
}
extern "C" double uhdwrap_set_txatt(void *dev, double a, size_t chan)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->setPowerAttenuation(a, chan);
}
extern "C" int32_t uhdwrap_start(void *dev, int chan)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->start();
}
extern "C" int32_t uhdwrap_stop(void *dev, int chan)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->stop();
}
extern "C" void uhdwrap_fill_info_cnf(struct ipc_sk_if *ipc_prim)
{
struct ipc_sk_if_info_chan *chan_info;
uhd::device_addr_t args("");
uhd::device_addrs_t devs_found = uhd::device::find(args);
if (devs_found.size() < 1) {
std::cout << "\n No device found!";
exit(0);
}
uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(devs_found[0]);
auto rxchans = usrp->get_rx_num_channels();
auto txchans = usrp->get_tx_num_channels();
auto rx_range = usrp->get_rx_gain_range();
auto tx_range = usrp->get_tx_gain_range();
//auto nboards = usrp->get_num_mboards();
auto refs = usrp->get_clock_sources(0);
auto devname = usrp->get_mboard_name(0);
ipc_prim->u.info_cnf.feature_mask = 0;
if (std::find(refs.begin(), refs.end(), "internal") != refs.end())
ipc_prim->u.info_cnf.feature_mask |= FEATURE_MASK_CLOCKREF_INTERNAL;
if (std::find(refs.begin(), refs.end(), "external") != refs.end())
ipc_prim->u.info_cnf.feature_mask |= FEATURE_MASK_CLOCKREF_EXTERNAL;
// at least one duplex channel
auto num_chans = rxchans == txchans ? txchans : 1;
ipc_prim->u.info_cnf.iq_scaling_val_rx = 0.3;
ipc_prim->u.info_cnf.iq_scaling_val_tx = 1;
ipc_prim->u.info_cnf.max_num_chans = num_chans;
OSMO_STRLCPY_ARRAY(ipc_prim->u.info_cnf.dev_desc, devname.c_str());
chan_info = ipc_prim->u.info_cnf.chan_info;
for (unsigned int i = 0; i < ipc_prim->u.info_cnf.max_num_chans; i++) {
auto rxant = usrp->get_rx_antennas(i);
auto txant = usrp->get_tx_antennas(i);
for (unsigned int j = 0; j < txant.size(); j++) {
OSMO_STRLCPY_ARRAY(chan_info->tx_path[j], txant[j].c_str());
}
for (unsigned int j = 0; j < rxant.size(); j++) {
OSMO_STRLCPY_ARRAY(chan_info->rx_path[j], rxant[j].c_str());
}
chan_info->min_rx_gain = rx_range.start();
chan_info->max_rx_gain = rx_range.stop();
chan_info->min_tx_gain = tx_range.start();
chan_info->max_tx_gain = tx_range.stop();
chan_info->nominal_tx_power = 7.5; // FIXME: would require uhd dev + freq info
chan_info++;
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Eric Wild <ewild@sysmocom.de>
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef IPC_UHDWRAP_H
#define IPC_UHDWRAP_H
#ifdef __cplusplus
#include "../uhd/UHDDevice.h"
class uhd_wrap : public uhd_device {
public:
// std::thread *t;
size_t samps_per_buff_rx;
size_t samps_per_buff_tx;
int channel_count;
std::vector<std::vector<short> > wrap_rx_buffs;
std::vector<std::vector<short> > wrap_tx_buffs;
std::vector<short *> wrap_rx_buf_ptrs;
std::vector<short *> wrap_tx_buf_ptrs;
template <typename... Args> uhd_wrap(Args... args) : uhd_device(args...)
{
// t = new std::thread(magicthread);
// give the thread some time to start and set up
// std::this_thread::sleep_for(std::chrono::seconds(1));
}
virtual ~uhd_wrap();
// void ipc_sock_close() override {};
int wrap_read(TIMESTAMP *timestamp);
virtual int open(const std::string &args, int ref, bool swap_channels) override;
// bool start() override;
// bool stop() override;
// virtual TIMESTAMP initialWriteTimestamp() override;
// virtual TIMESTAMP initialReadTimestamp() override;
int getTimingOffset()
{
return ts_offset;
}
size_t bufsizerx();
size_t bufsizetx();
int chancount();
};
#else
void *uhdwrap_open(struct ipc_sk_if_open_req *open_req);
int32_t uhdwrap_get_bufsizerx(void *dev);
int32_t uhdwrap_get_timingoffset(void *dev);
int32_t uhdwrap_read(void *dev, uint32_t num_chans);
int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun);
double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx);
double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx);
int32_t uhdwrap_start(void *dev, int chan);
int32_t uhdwrap_stop(void *dev, int chan);
void uhdwrap_fill_info_cnf(struct ipc_sk_if *ipc_prim);
double uhdwrap_set_txatt(void *dev, double a, size_t chan);
#endif
#endif // IPC_B210_H

View File

@@ -557,7 +557,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
#ifdef USE_UHD_3_11 #ifdef USE_UHD_3_11
uhd::log::add_logger("OsmoTRX", &uhd_log_handler); uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
uhd::log::set_log_level(uhd::log::debug); uhd::log::set_log_level(uhd::log::debug);
uhd::log::set_console_level(uhd::log::off); uhd::log::set_console_level(uhd::log::debug);
uhd::log::set_logger_level("OsmoTRX", uhd::log::debug); uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
#else #else
uhd::msg::register_handler(&uhd_msg_handler); uhd::msg::register_handler(&uhd_msg_handler);
@@ -870,7 +870,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
if (rc < 0) { if (rc < 0) {
LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc); LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp); LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
return 0; return len;
} }
// Receive samples from the usrp until we have enough // Receive samples from the usrp until we have enough
@@ -975,13 +975,14 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
bool uhd_device::updateAlignment(TIMESTAMP timestamp) bool uhd_device::updateAlignment(TIMESTAMP timestamp)
{ {
aligned = false;
return true; return true;
} }
uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
{ {
double rf_spread, rf_freq; double rf_spread, rf_freq;
std::vector<double> freqs; std::vector<tune_result> freqs;
uhd::tune_request_t treq(freq); uhd::tune_request_t treq(freq);
if (dev_type == UMTRX) { if (dev_type == UMTRX) {
@@ -1013,17 +1014,17 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
freqs = rx_freqs; freqs = rx_freqs;
/* Tune directly if other channel isn't tuned */ /* Tune directly if other channel isn't tuned */
if (freqs[!chan] < 10.0) if (freqs[!chan].freq < 10.0)
return treq; return treq;
/* Find center frequency between channels */ /* Find center frequency between channels */
rf_spread = fabs(freqs[!chan] - freq); rf_spread = fabs(freqs[!chan].freq - freq);
if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) { if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n"; LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n";
return treq; return treq;
} }
rf_freq = (freqs[!chan] + freq) / 2.0f; rf_freq = (freqs[!chan].freq + freq) / 2.0f;
treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL; treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
treq.target_freq = freq; treq.target_freq = freq;
@@ -1041,11 +1042,13 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
if (tx) { if (tx) {
tres = usrp_dev->set_tx_freq(treq, chan); tres = usrp_dev->set_tx_freq(treq, chan);
tx_freqs[chan] = usrp_dev->get_tx_freq(chan); tx_freqs[chan].uhd = tres;
tx_freqs[chan].freq = usrp_dev->get_tx_freq(chan);
str_dir = "Tx"; str_dir = "Tx";
} else { } else {
tres = usrp_dev->set_rx_freq(treq, chan); tres = usrp_dev->set_rx_freq(treq, chan);
rx_freqs[chan] = usrp_dev->get_rx_freq(chan); rx_freqs[chan].uhd = tres;
rx_freqs[chan].freq = usrp_dev->get_rx_freq(chan);
str_dir = "Rx"; str_dir = "Rx";
} }
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl; LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
@@ -1059,13 +1062,15 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
*/ */
if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) { if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
if (tx) { if (tx) {
treq = select_freq(tx_freqs[!chan], !chan, true); treq = select_freq(tx_freqs[!chan].freq, !chan, true);
tres = usrp_dev->set_tx_freq(treq, !chan); tres = usrp_dev->set_tx_freq(treq, !chan);
tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan); tx_freqs[chan].uhd = tres;
tx_freqs[!chan].freq = usrp_dev->get_tx_freq(!chan);
} else { } else {
treq = select_freq(rx_freqs[!chan], !chan, false); treq = select_freq(rx_freqs[!chan].freq, !chan, false);
tres = usrp_dev->set_rx_freq(treq, !chan); tres = usrp_dev->set_rx_freq(treq, !chan);
rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan); tx_freqs[chan].uhd = tres;
rx_freqs[!chan].freq = usrp_dev->get_rx_freq(!chan);
} }
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl; LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
@@ -1105,6 +1110,20 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan)
return true; return true;
} }
bool uhd_device::setRxOffset(double wOffset, size_t chan)
{
uhd::tune_result_t tres;
uhd::tune_request_t treq(rx_freqs[chan].freq - wOffset);
treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
treq.rf_freq = rx_freqs[chan].uhd.actual_rf_freq;
tres = usrp_dev->set_rx_freq(treq, chan);
rx_freqs[chan].freq = usrp_dev->get_rx_freq(chan);
return true;
}
bool uhd_device::setRxFreq(double wFreq, size_t chan) bool uhd_device::setRxFreq(double wFreq, size_t chan)
{ {
uint16_t req_arfcn; uint16_t req_arfcn;
@@ -1116,19 +1135,19 @@ bool uhd_device::setRxFreq(double wFreq, size_t chan)
} }
ScopedLock lock(tune_lock); ScopedLock lock(tune_lock);
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1); // req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
if (req_arfcn == 0xffff) { // if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz"; // LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
return false; // return false;
} // }
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) { // if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq // LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )"; // << " Hz (ARFCN " << req_arfcn << " )";
return false; // return false;
} // }
if (!set_band(req_band)) // if (!set_band(req_band))
return false; // return false;
return set_freq(wFreq, chan, false); return set_freq(wFreq, chan, false);
} }
@@ -1140,7 +1159,7 @@ double uhd_device::getTxFreq(size_t chan)
return 0.0; return 0.0;
} }
return tx_freqs[chan]; return tx_freqs[chan].freq;
} }
double uhd_device::getRxFreq(size_t chan) double uhd_device::getRxFreq(size_t chan)
@@ -1150,7 +1169,7 @@ double uhd_device::getRxFreq(size_t chan)
return 0.0; return 0.0;
} }
return rx_freqs[chan]; return rx_freqs[chan].freq;
} }
bool uhd_device::setRxAntenna(const std::string &ant, size_t chan) bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)

View File

@@ -80,6 +80,12 @@ struct dev_band_desc {
*/ */
class uhd_device : public RadioDevice { class uhd_device : public RadioDevice {
public: public:
struct tune_result {
uhd::tune_result_t uhd;
double freq;
};
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type, uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chan_num, double offset, size_t chan_num, double offset,
const std::vector<std::string>& tx_paths, const std::vector<std::string>& tx_paths,
@@ -102,6 +108,7 @@ public:
bool setTxFreq(double wFreq, size_t chan); bool setTxFreq(double wFreq, size_t chan);
bool setRxFreq(double wFreq, size_t chan); bool setRxFreq(double wFreq, size_t chan);
bool setRxOffset(double wOffset, size_t chan);
TIMESTAMP initialWriteTimestamp(); TIMESTAMP initialWriteTimestamp();
TIMESTAMP initialReadTimestamp(); TIMESTAMP initialReadTimestamp();
@@ -159,7 +166,7 @@ protected:
double rx_gain_min, rx_gain_max; double rx_gain_min, rx_gain_max;
std::vector<double> tx_gains, rx_gains; std::vector<double> tx_gains, rx_gains;
std::vector<double> tx_freqs, rx_freqs; std::vector<tune_result> tx_freqs, rx_freqs;
bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */ bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */
enum gsm_band band; enum gsm_band band;
struct dev_band_desc band_desc; struct dev_band_desc band_desc;

148
Transceiver52M/fbsb.cpp Normal file
View File

@@ -0,0 +1,148 @@
#include <atomic>
#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
//#include "Sockets.h"
#include <sys/types.h>
#include <sys/socket.h>
static const auto tssize = 156.25;
static const auto framee_to_read = 12;
static const auto ts_to_read = framee_to_read * 8;
static const int fbsb_chunk_len = ts_to_read * 2 * tssize; // contiguous fcch+sch guaranteed
static const auto corr_offset = 0;
struct fbsb_par {
enum class fbsb_state {
IDLE,
INIT,
ACQ,
WAIT,
ACQ_COMPL,
DONE
};
int pos;
int time_idx;
std::atomic<fbsb_state> s;
GSM::Time rcvClock[ts_to_read];
complex fbsb_buf[fbsb_chunk_len+corr_offset];
complex conjbuf[fbsb_chunk_len+corr_offset];
complex avgbuf[fbsb_chunk_len+corr_offset];
void addclk(GSM::Time c) { rcvClock[time_idx] = c; time_idx++; return;}
public:
fbsb_par() : s(fbsb_state::IDLE) {
reset();
};
bool done() {return !(time_idx < ts_to_read);}
void take(void* addr, int num_cplx_sps, GSM::Time c) {
memcpy(fbsb_buf+pos+corr_offset,addr, num_cplx_sps * sizeof(complex));
pos += num_cplx_sps;
assert(pos < fbsb_chunk_len);
rcvClock[time_idx] = c;
time_idx++;
}
void reset() {pos = 0;
time_idx = 0;
// s = fbsb_state::IDLE;
memset(fbsb_buf, 0, sizeof(fbsb_buf));
memset(conjbuf, 0, sizeof(conjbuf));
memset(avgbuf, 0, sizeof(avgbuf));
}
int sz () {return fbsb_chunk_len;}
int off () {return corr_offset;}
// from osmotrx sigproc
complex fastPeakDetect(complex* rxBurst, float* index, int N)
{
float val, max = 0.0f;
complex amp;
int _index = -1;
for (size_t i = 0; i < N; i++) {
val = rxBurst[i].abs();
if (val > max) {
max = val;
_index = i;
amp = rxBurst[i];
}
}
if (index)
*index = (float)_index;
return amp;
}
void conj_with_lag(complex* in, complex* out, int lag, int offset, int N) {
int total_offset = offset + lag;
for (int s = 0; s < N; s++)
out[s] = in[s + total_offset] * in[s + total_offset - lag].conj();
}
auto ac_sum_with_lag(complex* in, int lag, int offset, int N) {
complex v(0,0);
auto total_offset = offset + lag;
for (auto s = 0; s < N; s++)
v += in[s + total_offset] * in[s + total_offset - lag].conj();
return atan2(v.imag(), v.real());
}
bool running_avg_opt(complex* in, complex* out, int avg_window, int val_to_find, int* idx, int N) {
bool found = false;
complex avg0 = 0;
complex scale(avg_window, avg_window);
for (auto i = 0; i < avg_window; i++)
avg0 += in[i];
out[0] = avg0 / scale;
//skip first
for (auto i = 1; i < N - avg_window; i++) {
avg0 += in[i-1] - in[i+avg_window];
auto tmp = avg0 / scale;
out[i] = tmp;
if (!found && tmp.abs() > val_to_find) {
found = !found;
*idx = i;
return true;
}
}
return false;
}
int fcch(complex* amp, int* found_idx, bool dump) {
conj_with_lag(fbsb_buf, conjbuf, 3, off(), sz());
running_avg_opt(conjbuf, avgbuf, 48, 1.5e6, found_idx, sz()-off());
float pos;
auto r = fastPeakDetect(avgbuf, &pos, sz()-off());
*found_idx = *found_idx+off();
std::cerr << "fcch found at " << pos << " amp: " << r.abs() << std::endl;
*amp = r;
if(dump) {
{
auto f = fopen("inbuf.cfile", "wb");
fwrite(fbsb_buf, sz(), 1, f);
fclose(f);
}
{
auto f = fopen("conjbuf.cfile", "wb");
fwrite(conjbuf, sz(), 1, f);
fclose(f);
}
{
auto f = fopen("schfcch.cfile", "wb");
fwrite(avgbuf, sz(), 1, f);
fclose(f);
}
exit(0);
}
return pos;
}
};

0
Transceiver52M/fbsb.h Normal file
View File

View File

@@ -86,11 +86,6 @@ detect_burst(const gr_complex* input,
float output[BURST_SIZE]; float output[BURST_SIZE];
int start_state = 3; int start_state = 3;
// if(burst_start < 0 ||burst_start > 10)
// fprintf(stderr, "bo %d\n", burst_start);
// burst_start = burst_start >= 0 ? burst_start : 0;
autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR); autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
for (int ii = 0; ii < d_chan_imp_length; ii++) for (int ii = 0; ii < d_chan_imp_length; ii++)
rhh[ii] = conj(rhh_temp[ii * d_OSR]); rhh[ii] = conj(rhh_temp[ii * d_OSR]);
@@ -226,7 +221,6 @@ int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int se
/* Compute window energies */ /* Compute window energies */
auto window_energy_start_offset = strongest_corr_nr - 6 * d_OSR; auto window_energy_start_offset = strongest_corr_nr - 6 * d_OSR;
window_energy_start_offset = window_energy_start_offset < 0 ? 0 : window_energy_start_offset; //can end up out of range..
auto window_energy_end_offset = strongest_corr_nr + 6 * d_OSR + d_chan_imp_length * d_OSR; auto window_energy_end_offset = strongest_corr_nr + 6 * d_OSR + d_chan_imp_length * d_OSR;
auto iter = power_buffer.begin() + window_energy_start_offset; auto iter = power_buffer.begin() + window_energy_start_offset;
auto iter_end = power_buffer.begin() + window_energy_end_offset; auto iter_end = power_buffer.begin() + window_energy_end_offset;
@@ -329,8 +323,7 @@ int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_r
const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING); const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
const int search_center = SYNC_POS + TRAIN_BEGINNING; const int search_center = SYNC_POS + TRAIN_BEGINNING;
const int search_start_pos = 0; const int search_start_pos = 0;
// FIXME: proper end offset const int search_stop_pos = len - tseqlen;
const int search_stop_pos = len - (N_SYNC_BITS*8);
auto tseq = &d_sch_training_seq[TRAIN_BEGINNING]; auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen, return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,

View File

@@ -189,8 +189,8 @@ template <typename T> struct blade_hw {
struct bladerf_stream *tx_stream; struct bladerf_stream *tx_stream;
// using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>; // using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
using tx_buf_q_type = spsc_cond<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>; using tx_buf_q_type = spsc_cond<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
const unsigned int rxFullScale, txFullScale; unsigned int rxFullScale, txFullScale;
const int rxtxdelay; int rxtxdelay;
float rxgain, txgain; float rxgain, txgain;
@@ -249,7 +249,7 @@ template <typename T> struct blade_hw {
blade_check(bladerf_open, &dev, ""); blade_check(bladerf_open, &dev, "");
if (!dev) { if (!dev) {
std::cerr << "open failed, device missing?" << std::endl; std::cerr << "open failed, device missing?" << std::endl;
exit(0); return -1;
} }
if (bladerf_device_speed(dev) != bladerf_dev_speed::BLADERF_DEVICE_SPEED_SUPER) { if (bladerf_device_speed(dev) != bladerf_dev_speed::BLADERF_DEVICE_SPEED_SUPER) {
std::cerr << "open failed, only superspeed (usb3) supported!" << std::endl; std::cerr << "open failed, only superspeed (usb3) supported!" << std::endl;

View File

@@ -1,259 +0,0 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* 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 <complex>
#include <cstring>
#include <functional>
#include <iostream>
#include <thread>
#include <Timeval.h>
#include <vector>
#include <ipcif.h>
// typedef unsigned long long TIMESTAMP;
using blade_sample_type = std::complex<int16_t>;
const int SAMPLE_SCALE_FACTOR = 1;
struct uhd_buf_wrap {
uint64_t ts;
uint32_t num_samps;
blade_sample_type *buf;
auto actual_samples_per_buffer()
{
return num_samps;
}
long get_first_ts()
{
return ts; //md->time_spec.to_ticks(rxticks);
}
int readall(blade_sample_type *outaddr)
{
memcpy(outaddr, buf, num_samps * sizeof(blade_sample_type));
return num_samps;
}
int read_n(blade_sample_type *outaddr, int start, int num)
{
// assert(start >= 0);
auto to_read = std::min((int)num_samps - start, num);
// assert(to_read >= 0);
memcpy(outaddr, buf + start, to_read * sizeof(blade_sample_type));
return to_read;
}
};
using dev_buf_t = uhd_buf_wrap;
using bh_fn_t = std::function<int(dev_buf_t *)>;
template <typename T> struct ipc_hw {
// uhd::usrp::multi_usrp::sptr dev;
// uhd::rx_streamer::sptr rx_stream;
// uhd::tx_streamer::sptr tx_stream;
blade_sample_type *one_pkt_buf;
std::vector<blade_sample_type *> pkt_ptrs;
size_t rx_spp;
double rxticks;
const unsigned int rxFullScale, txFullScale;
const int rxtxdelay;
float rxgain, txgain;
trxmsif m;
virtual ~ipc_hw()
{
delete[] one_pkt_buf;
}
ipc_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67)
{
}
bool tuneTx(double freq, size_t chan = 0)
{
msleep(25);
// dev->set_tx_freq(freq, chan);
msleep(25);
return true;
};
bool tuneRx(double freq, size_t chan = 0)
{
msleep(25);
// dev->set_rx_freq(freq, chan);
msleep(25);
return true;
};
bool tuneRxOffset(double offset, size_t chan = 0)
{
return true;
};
double setRxGain(double dB, size_t chan = 0)
{
rxgain = dB;
msleep(25);
// dev->set_rx_gain(dB, chan);
msleep(25);
return dB;
};
double setTxGain(double dB, size_t chan = 0)
{
txgain = dB;
msleep(25);
// dev->set_tx_gain(dB, chan);
msleep(25);
return dB;
};
int setPowerAttenuation(int atten, size_t chan = 0)
{
return atten;
};
int init_device(bh_fn_t rxh, bh_fn_t txh)
{
// std::thread([] {
// osmo_ctx_init("bernd");
// osmo_select_init();
// main_ipc();
// while (true)
// osmo_select_main(0);
// }).detach();
return m.connect() ? 0: -1;
}
void *rx_cb(bh_fn_t burst_handler)
{
void *ret;
static int to_skip = 0;
blade_sample_type pbuf[508 * 2];
uint64_t t;
int len = 508 * 2;
m.read_dl(508 * 2, &t, pbuf);
// auto len = ipc_shm_read(ios_tx_to_device[0], (uint16_t *)&pbuf, 508 * 2, &t, 1);
// if(len < 0) {
// std::cerr << "fuck, rx fail!" << std::endl;
// exit(0);
// }
// uhd::rx_metadata_t md;
// auto num_rx_samps = rx_stream->recv(pkt_ptrs.front(), rx_spp, md, 3.0, true);
// if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
// std::cerr << boost::format("Timeout while streaming") << std::endl;
// exit(0);
// }
// if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
// std::cerr << boost::format("Got an overflow indication. Please consider the following:\n"
// " Your write medium must sustain a rate of %fMB/s.\n"
// " Dropped samples will not be written to the file.\n"
// " Please modify this example for your purposes.\n"
// " This message will not appear again.\n") %
// 1.f;
// exit(0);
// ;
// }
// if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
// std::cerr << str(boost::format("Receiver error: %s") % md.strerror());
// exit(0);
// }
dev_buf_t rcd = { t, static_cast<uint32_t>(len), pbuf };
if (to_skip < 120) // prevents weird overflows on startup
to_skip++;
else {
burst_handler(&rcd);
}
return ret;
}
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [this, burst_handler] {
pthread_setname_np(pthread_self(), "rxrun");
// wait_for_shm_open();
// uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
// stream_cmd.stream_now = true;
// stream_cmd.time_spec = uhd::time_spec_t();
// rx_stream->issue_stream_cmd(stream_cmd);
while (1) {
rx_cb(burst_handler);
}
};
return fn;
}
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [] {
// wait_for_shm_open();
// dummy
};
return fn;
}
void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
{
// uhd::tx_metadata_t m = {};
// m.end_of_burst = true;
// m.start_of_burst = true;
// m.has_time_spec = true;
// m.time_spec = m.time_spec.from_ticks(ts + rxtxdelay, rxticks); // uhd specific b210 delay!
// std::vector<void *> ptrs(1, buffer);
// tx_stream->send(ptrs, len, m);
// uhd::async_metadata_t async_md;
// bool tx_ack = false;
// while (!tx_ack && tx_stream->recv_async_msg(async_md)) {
// tx_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK);
// }
// std::cout << (tx_ack ? "yay" : "nay") << " " << async_md.time_spec.to_ticks(rxticks) << std::endl;
}
void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
{
pthread_setname_np(pthread_self(), name);
// cpu_set_t cpuset;
// CPU_ZERO(&cpuset);
// CPU_SET(cpunum, &cpuset);
// auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
// if (rv < 0) {
// std::cerr << name << " affinity: errreur! " << std::strerror(errno);
// return exit(0);
// }
// sched_param sch_params;
// sch_params.sched_priority = prio;
// rv = pthread_setschedparam(pthread_self(), schedtype, &sch_params);
// if (rv < 0) {
// std::cerr << name << " sched: errreur! " << std::strerror(errno);
// return exit(0);
// }
}
};

View File

@@ -1,473 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <debug.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <inttypes.h>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <shm.h>
#include <ipc_shm.h>
#include <ipc_chan.h>
#include <ipc_sock.h>
#define DEFAULT_SHM_NAME "/osmo-trx-ipc-driver-shm2"
#define IPC_SOCK_PATH_PREFIX "/tmp"
static void *tall_ctx;
struct ipc_sock_state *global_ipc_sock_state;
/* 8 channels are plenty */
struct ipc_sock_state *global_ctrl_socks[8];
struct ipc_shm_io *ios_tx_to_device[8];
struct ipc_shm_io *ios_rx_from_device[8];
void *shm;
// void *global_dev;
static pthread_mutex_t wait_open_lock;
static pthread_cond_t wait_open_cond;
static struct ipc_shm_region *decoded_region;
static struct {
int msocknum;
char *ud_prefix_dir;
} cmdline_cfg = { 1, IPC_SOCK_PATH_PREFIX };
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
.color = NULL,
.description = "Main generic category",
.loglevel = LOGL_DEBUG,.enabled = 1,
},
[DDEV] = {
.name = "DDEV",
.description = "Device/Driver specific code",
.color = NULL,
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
const struct log_info log_infox = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
volatile int ipc_exit_requested = 0;
static int ipc_shm_setup(const char *shm_name, size_t shm_len)
{
int fd;
int rc;
LOGP(DMAIN, LOGL_NOTICE, "Opening shm path %s\n", shm_name);
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
LOGP(DMAIN, LOGL_ERROR, "shm_open %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_shm_open;
}
LOGP(DMAIN, LOGL_NOTICE, "Truncating %d to size %zu\n", fd, shm_len);
if (ftruncate(fd, shm_len) < 0) {
LOGP(DMAIN, LOGL_ERROR, "ftruncate %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
LOGP(DMAIN, LOGL_NOTICE, "mmaping shared memory fd %d\n", fd);
if ((shm = mmap(NULL, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
LOGP(DMAIN, LOGL_ERROR, "mmap %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
LOGP(DMAIN, LOGL_NOTICE, "mmap'ed shared memory at addr %p\n", shm);
/* After a call to mmap(2) the file descriptor may be closed without affecting the memory mapping. */
close(fd);
return 0;
err_mmap:
shm_unlink(shm_name);
close(fd);
err_shm_open:
return rc;
}
struct msgb *ipc_msgb_alloc(uint8_t msg_type)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = msgb_alloc(sizeof(struct ipc_sk_if) + 1000, "ipc_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct ipc_sk_if) + 1000);
ipc_prim = (struct ipc_sk_if *)msg->data;
ipc_prim->msg_type = msg_type;
return msg;
}
static int ipc_tx_info_cnf()
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = ipc_msgb_alloc(IPC_IF_MSG_INFO_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *)msg->data;
ipc_prim->u.info_cnf.feature_mask = FEATURE_MASK_CLOCKREF_EXTERNAL;
ipc_prim->u.info_cnf.iq_scaling_val_rx = 1;
ipc_prim->u.info_cnf.iq_scaling_val_tx = 1;
ipc_prim->u.info_cnf.max_num_chans = 1;
OSMO_STRLCPY_ARRAY(ipc_prim->u.info_cnf.dev_desc, "bernd");
struct ipc_sk_if_info_chan *chan_info = ipc_prim->u.info_cnf.chan_info;
OSMO_STRLCPY_ARRAY(chan_info->tx_path[0], "TX/RX");
OSMO_STRLCPY_ARRAY(chan_info->rx_path[0], "RX2");
chan_info->min_rx_gain = 0;
chan_info->max_rx_gain = 60;
chan_info->min_tx_gain = 0;
chan_info->max_tx_gain = 60;
chan_info->nominal_tx_power = 10;
return ipc_sock_send(msg);
}
static int ipc_tx_open_cnf(int rc, uint32_t num_chans, int32_t timingoffset)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
struct ipc_sk_if_open_cnf_chan *chan_info;
unsigned int i;
msg = ipc_msgb_alloc(IPC_IF_MSG_OPEN_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *)msg->data;
ipc_prim->u.open_cnf.return_code = rc;
ipc_prim->u.open_cnf.path_delay = timingoffset; // 6.18462e-5 * 1625e3 / 6;
OSMO_STRLCPY_ARRAY(ipc_prim->u.open_cnf.shm_name, DEFAULT_SHM_NAME);
chan_info = ipc_prim->u.open_cnf.chan_info;
for (i = 0; i < num_chans; i++) {
snprintf(chan_info->chan_ipc_sk_path, sizeof(chan_info->chan_ipc_sk_path), "%s/ipc_sock%d_%d",
cmdline_cfg.ud_prefix_dir, cmdline_cfg.msocknum, i);
/* FIXME: dynamc chan limit, currently 8 */
if (i < 8)
ipc_sock_init(chan_info->chan_ipc_sk_path, &global_ctrl_socks[i], ipc_chan_sock_accept, i);
chan_info++;
}
return ipc_sock_send(msg);
}
int ipc_rx_greeting_req(struct ipc_sk_if_greeting *greeting_req)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = ipc_msgb_alloc(IPC_IF_MSG_GREETING_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *)msg->data;
ipc_prim->u.greeting_cnf.req_version =
greeting_req->req_version == IPC_SOCK_API_VERSION ? IPC_SOCK_API_VERSION : 0;
ipc_sock_send(msg);
return 0;
}
int ipc_rx_info_req(struct ipc_sk_if_info_req *info_req)
{
ipc_tx_info_cnf();
return 0;
}
int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req)
{
/* calculate size needed */
unsigned int len;
unsigned int i;
// global_dev = uhdwrap_open(open_req);
/* b210 packet size is 2040, but our tx size is 2500, so just do *2 */
int shmbuflen = 5000 * 2;
len = ipc_shm_encode_region(NULL, open_req->num_chans, 4, shmbuflen);
/* Here we verify num_chans, rx_path, tx_path, clockref, etc. */
int rc = ipc_shm_setup(DEFAULT_SHM_NAME, len);
len = ipc_shm_encode_region((struct ipc_shm_raw_region *)shm, open_req->num_chans, 4, shmbuflen);
// LOGP(DMAIN, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *)shm, 80));
/* set up our own copy of the decoded area, we have to do it here,
* since the uhd wrapper does not allow starting single channels
* additionally go for the producer init for both, so only we are responsible for the init, instead
* of splitting it with the client and causing potential races if one side uses it too early */
decoded_region = ipc_shm_decode_region(0, (struct ipc_shm_raw_region *)shm);
for (i = 0; i < open_req->num_chans; i++) {
// ios_tx_to_device[i] = ipc_shm_init_consumer(decoded_region->channels[i]->dl_stream);
ios_tx_to_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->dl_stream);
ios_rx_from_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->ul_stream);
}
ipc_tx_open_cnf(-rc, open_req->num_chans, 0);
return 0;
}
volatile bool ul_running = false;
volatile bool dl_running = false;
void *uplink_thread(void *x_void_ptr)
{
uint32_t chann = decoded_region->num_chans;
ul_running = true;
pthread_setname_np(pthread_self(), "uplink_rx");
while (!ipc_exit_requested) {
// int32_t read = uhdwrap_read(global_dev, chann);
// ipc_shm_enqueue(ios_rx_from_device[i], tstamp, num_rx_samps, (uint16_t *)&d->wrap_rx_buffs[i].front());
if (read < 0)
return 0;
}
return 0;
}
void *downlink_thread(void *x_void_ptr)
{
int chann = decoded_region->num_chans;
dl_running = true;
pthread_setname_np(pthread_self(), "downlink_tx");
while (!ipc_exit_requested) {
bool underrun;
// uhdwrap_write(global_dev, chann, &underrun);
// len = ipc_shm_read(ios_tx_to_device[i], (uint16_t *)&d->wrap_tx_buffs[i].front(), 5000, &timestamp, 1);
// return d->writeSamples(d->wrap_tx_buf_ptrs, len, underrun, timestamp);
}
return 0;
}
int ipc_rx_chan_start_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
int rc = 0;
// rc = uhdwrap_start(global_dev, chan_nr);
pthread_cond_broadcast(&wait_open_cond);
// /* no per-chan start/stop */
// if (!dl_running || !ul_running) {
// /* chan != first chan start will "fail", which is fine, usrp can't start/stop chans independently */
// if (rc) {
// LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads.. req for chan:%d\n", chan_nr);
// pthread_t rx, tx;
// pthread_create(&rx, NULL, uplink_thread, 0);
// pthread_create(&tx, NULL, downlink_thread, 0);
// }
// } else
// LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads request ignored.. req for chan:%d\n", chan_nr);
msg = ipc_msgb_alloc(IPC_IF_MSG_START_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.start_cnf.return_code = rc ? 0 : -1;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_rx_chan_stop_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
int rc = true;
/* no per-chan start/stop */
// rc = uhdwrap_stop(global_dev, chan_nr);
msg = ipc_msgb_alloc(IPC_IF_MSG_STOP_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.stop_cnf.return_code = rc ? 0 : -1;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_rx_chan_setgain_req(struct ipc_sk_chan_if_gain *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
double rv = req->gain;
// rv = uhdwrap_set_gain(global_dev, req->gain, chan_nr, req->is_tx);
msg = ipc_msgb_alloc(IPC_IF_MSG_SETGAIN_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.set_gain_cnf.is_tx = req->is_tx;
ipc_prim->u.set_gain_cnf.gain = rv;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_rx_chan_setfreq_req(struct ipc_sk_chan_if_freq_req *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
bool rv = true;
// rv = uhdwrap_set_freq(global_dev, req->freq, chan_nr, req->is_tx);
msg = ipc_msgb_alloc(IPC_IF_MSG_SETFREQ_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.set_freq_cnf.return_code = rv ? 0 : 1;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_rx_chan_settxatten_req(struct ipc_sk_chan_if_tx_attenuation *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
double rv = req->attenuation;
// rv = uhdwrap_set_txatt(global_dev, req->attenuation, chan_nr);
msg = ipc_msgb_alloc(IPC_IF_MSG_SETTXATTN_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.txatten_cnf.attenuation = rv;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var,
int (*sock_callback_fn)(struct osmo_fd *fd, unsigned int what), int n)
{
struct ipc_sock_state *state;
struct osmo_fd *bfd;
int rc;
state = talloc_zero(NULL, struct ipc_sock_state);
if (!state)
return -ENOMEM;
*global_state_var = state;
INIT_LLIST_HEAD(&state->upqueue);
state->conn_bfd.fd = -1;
bfd = &state->listen_bfd;
bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND);
if (bfd->fd < 0) {
LOGP(DMAIN, LOGL_ERROR, "Could not create %s unix socket: %s\n", path, strerror(errno));
talloc_free(state);
return -1;
}
osmo_fd_setup(bfd, bfd->fd, OSMO_FD_READ, sock_callback_fn, state, n);
rc = osmo_fd_register(bfd);
if (rc < 0) {
LOGP(DMAIN, LOGL_ERROR, "Could not register listen fd: %d\n", rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
LOGP(DMAIN, LOGL_INFO, "Started listening on IPC socket: %s\n", path);
return 0;
}
int main_ipc()
{
char *ipc_msock_path = "/tmp/ipc_sock1";
tall_ctx = talloc_named_const(NULL, 0, "trx-ipc-ms");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_infox);
log_enable_multithread();
LOGP(DMAIN, LOGL_INFO, "Starting %s\n", "bernd");
ipc_sock_init(ipc_msock_path, &global_ipc_sock_state, ipc_sock_accept, 0);
// while (!ipc_exit_requested)
// osmo_select_main(0);
// if (global_dev) {
// unsigned int i;
// for (i = 0; i < decoded_region->num_chans; i++)
// uhdwrap_stop(global_dev, i);
// }
// ipc_sock_close(global_ipc_sock_state);
int rv;
pthread_condattr_t t2;
rv = pthread_condattr_setpshared(&t2, PTHREAD_PROCESS_SHARED);
rv = pthread_cond_init(&wait_open_cond, &t2);
return 0;
}
int wait_for_shm_open()
{
struct timespec tv;
int rv;
clock_gettime(CLOCK_REALTIME, &tv);
tv.tv_sec += 15;
rv = pthread_mutex_timedlock(&wait_open_lock, &tv);
if (rv != 0)
return -rv;
rv = pthread_cond_timedwait(&wait_open_cond, &wait_open_lock, &tv);
return rv;
}

View File

@@ -20,8 +20,10 @@
*/ */
#include <radioInterface.h> #include <radioInterface.h>
#include "l1if.h"
#include "ms_rx_upper.h" #include "ms_rx_upper.h"
#include "syncthing.h" #include "syncthing.h"
#include "ms_state.h"
void upper_trx::driveControl() void upper_trx::driveControl()
{ {
@@ -89,8 +91,6 @@ void upper_trx::commandhandler(char *buffer, char *response)
} else if (strcmp(command, "POWEROFF") == 0) { } else if (strcmp(command, "POWEROFF") == 0) {
set_ta(0); set_ta(0);
// turn off transmitter/demod // turn off transmitter/demod
// set_upper_ready(false);
sprintf(response, "RSP POWEROFF 0"); sprintf(response, "RSP POWEROFF 0");
} else if (strcmp(command, "POWERON") == 0) { } else if (strcmp(command, "POWERON") == 0) {
// turn on transmitter/demod // turn on transmitter/demod
@@ -101,8 +101,7 @@ void upper_trx::commandhandler(char *buffer, char *response)
if (!mOn) { if (!mOn) {
// Prepare for thread start // Prepare for thread start
mPower = -20; mPower = -20;
// start_ms(); start_ms();
set_upper_ready(true);
writeClockInterface(); writeClockInterface();
mOn = true; mOn = true;
@@ -197,6 +196,8 @@ void upper_trx::commandhandler(char *buffer, char *response)
sprintf(response, "RSP SETSLOT 1 %d %d", timeslot, corrCode); sprintf(response, "RSP SETSLOT 1 %d %d", timeslot, corrCode);
return; return;
} }
mStates.chanType[timeslot] = (ChannelCombination)corrCode;
mStates.setModulus(timeslot);
sprintf(response, "RSP SETSLOT 0 %d %d", timeslot, corrCode); sprintf(response, "RSP SETSLOT 0 %d %d", timeslot, corrCode);
} else if (!strcmp(command, "SETRXMASK")) { } else if (!strcmp(command, "SETRXMASK")) {
int slot; int slot;
@@ -210,6 +211,7 @@ void upper_trx::commandhandler(char *buffer, char *response)
} }
} else if (!strcmp(command, "SYNC")) { } else if (!strcmp(command, "SYNC")) {
// msleep(10); // msleep(10);
mStates.mode = trx_mode::TRX_MODE_MS_TRACK;
sprintf(response, "RSP SYNC 0"); sprintf(response, "RSP SYNC 0");
mMaxExpectedDelay = 48; mMaxExpectedDelay = 48;
// setRxGain(30); // setRxGain(30);

View File

@@ -81,7 +81,7 @@ static void check_rcv_fn(GSM::Time t, bool first, unsigned int &lastfn, unsigned
} }
__attribute__((xray_always_instrument)) __attribute__((noinline)) static void __attribute__((xray_always_instrument)) __attribute__((noinline)) static void
handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale) handle_it(one_burst &e, signalVector &burst, unsigned int tsc)
{ {
memset(burst.begin(), 0, burst.size() * sizeof(std::complex<float>)); memset(burst.begin(), 0, burst.size() * sizeof(std::complex<float>));
auto is_sch = gsm_sch_check_fn(e.gsmts.FN()) && e.gsmts.TN() == 0; auto is_sch = gsm_sch_check_fn(e.gsmts.FN()) && e.gsmts.TN() == 0;
@@ -115,7 +115,7 @@ handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
#endif #endif
{ {
convert_and_scale<float, float>(burst.begin(), burst.begin(), ONE_TS_BURST_LEN * 2, convert_and_scale<float, float>(burst.begin(), burst.begin(), ONE_TS_BURST_LEN * 2,
1.f / float(scale)); 1.f / 32767.f);
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
auto ss = reinterpret_cast<std::complex<float> *>(burst.begin()); auto ss = reinterpret_cast<std::complex<float> *>(burst.begin());
@@ -138,7 +138,7 @@ handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
return; return;
} }
#if 1 #if 1
convert_and_scale<float, int16_t>(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(scale)); convert_and_scale<float, int16_t>(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2, 1.f / 2047.f);
// std::cerr << "@" << tsc << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << " " // std::cerr << "@" << tsc << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << " "
// << std::endl; // << std::endl;
@@ -166,7 +166,7 @@ handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
#endif #endif
} }
__attribute__((xray_always_instrument)) __attribute__((noinline)) void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale) __attribute__((xray_always_instrument)) __attribute__((noinline)) void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc)
{ {
static bool first = true; static bool first = true;
unsigned int lastfn = 0; unsigned int lastfn = 0;
@@ -201,7 +201,7 @@ __attribute__((xray_always_instrument)) __attribute__((noinline)) void rcv_burst
check_rcv_fn(e.gsmts, first, lastfn, fnbm); check_rcv_fn(e.gsmts, first, lastfn, fnbm);
handle_it(e, burst, *tsc, scale); handle_it(e, burst, *tsc);
// rv = detectSCHBurst(*burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp); // rv = detectSCHBurst(*burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp);
// if (rv > 0) // if (rv > 0)

View File

@@ -22,4 +22,4 @@
#include "syncthing.h" #include "syncthing.h"
void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale); void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc);

View File

@@ -135,76 +135,56 @@ void ms_trx::maybe_update_gain(one_burst &brst)
gain_check = (gain_check + 1) % avgburst_num; gain_check = (gain_check + 1) % avgburst_num;
} }
static unsigned char sch_demod_bits[148]; bool ms_trx::handle_sch_or_nb(bool get_first_sch)
bool ms_trx::handle_sch_or_nb()
{ {
one_burst brst; one_burst brst;
auto current_gsm_time = timekeeper.gsmtime(); auto current_gsm_time = timekeeper.gsmtime();
brst.gsmts = current_gsm_time;
memcpy(brst.burst, burst_copy_buffer, sizeof(blade_sample_type) * ONE_TS_BURST_LEN);
auto pushok = rxqueue.spsc_push(&brst);
#ifdef PRINT_Q_OVERFLOW
if (!pushok)
std::cout << "F" << std::endl;
#endif
if (do_auto_gain)
maybe_update_gain(brst);
// only continue for SCH, don't touch FCCH
auto is_sch = gsm_sch_check_fn(current_gsm_time.FN()) && current_gsm_time.TN() == 0; auto is_sch = gsm_sch_check_fn(current_gsm_time.FN()) && current_gsm_time.TN() == 0;
auto is_fcch = gsm_fcch_check_fn(current_gsm_time.FN()) && current_gsm_time.TN() == 0; auto is_fcch = gsm_fcch_check_fn(current_gsm_time.FN()) && current_gsm_time.TN() == 0;
#pragma unused(is_fcch) #pragma unused(is_fcch)
//either pass burst to upper layer for demod, OR pass demodded SCH to upper layer so we don't waste time processing it twice
brst.gsmts = current_gsm_time;
if (!is_sch) { if (!is_sch) {
memcpy(brst.burst, burst_copy_buffer, sizeof(blade_sample_type) * ONE_TS_BURST_LEN); // sched_yield();
} else { return false;
handle_sch(false);
memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
} }
// auto pushok = rxqueue.spsc_push(&brst); auto rv = handle_sch(false);
#ifndef SYNCTHINGONLY // sched_yield();
if (upper_is_ready) { // this is blocking, so only submit if there is a reader - only if upper exists! return rv;
#endif
while (!rxqueue.spsc_push(&brst))
;
#ifndef SYNCTHINGONLY
}
#endif
// #ifdef PRINT_Q_OVERFLOW
// if (!pushok)
// std::cout << "F" << std::endl;
// #endif
if (do_auto_gain)
maybe_update_gain(brst);
return false;
} }
static float sch_acq_buffer[SCH_LEN_SPS * 2]; float bernd[SCH_LEN_SPS * 2];
bool ms_trx::handle_sch(bool is_first_sch_acq) bool ms_trx::handle_sch(bool is_first_sch_acq)
{ {
struct estim_burst_params ebp;
auto current_gsm_time = timekeeper.gsmtime(); auto current_gsm_time = timekeeper.gsmtime();
const auto buf_len = is_first_sch_acq ? SCH_LEN_SPS : ONE_TS_BURST_LEN; const auto buf_len = is_first_sch_acq ? SCH_LEN_SPS : ONE_TS_BURST_LEN;
const auto which_in_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer; const auto which_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2];
const auto ss = reinterpret_cast<std::complex<float> *>(which_out_buffer);
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
unsigned char outbin[148];
float max_corr = 0; float max_corr = 0;
int start; const auto ss = reinterpret_cast<std::complex<float> *>(&bernd[0]);
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer)); convert_and_scale<float, int16_t>(bernd, which_buffer, buf_len * 2, 1.f / 2047.f);
if (is_first_sch_acq) {
convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2, auto start = is_first_sch_acq ? get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr) :
1.f / float(rxFullScale)); get_sch_chan_imp_resp(ss, channel_imp_resp);
start = get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr); detect_burst(&ss[start], &channel_imp_resp[0], 0, outbin);
detect_burst(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
} else {
convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2,
1.f / float(rxFullScale));
start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
start = start < 39 ? start : 39;
start = start > -39 ? start : -39;
detect_burst(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
}
SoftVector bitss(148); SoftVector bitss(148);
for (int i = 0; i < 148; i++) { for (int i = 0; i < 148; i++) {
bitss[i] = (!sch_demod_bits[i]) < 1 ? -1 : 1; bitss[i] = (!outbin[i]) < 1 ? -1 : 1;
} }
auto sch_decode_success = decode_sch(bitss.begin(), is_first_sch_acq); auto sch_decode_success = decode_sch(bitss.begin(), is_first_sch_acq);
@@ -213,7 +193,7 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
const auto ts_offset_symb = 0; const auto ts_offset_symb = 0;
if (is_first_sch_acq) { if (is_first_sch_acq) {
// update ts to first sample in sch buffer, to allow delay calc for current ts // update ts to first sample in sch buffer, to allow delay calc for current ts
first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1; first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4);
} else if (abs(start) > 1) { } else if (abs(start) > 1) {
// continuous sch tracking, only update if off too much // continuous sch tracking, only update if off too much
temp_ts_corr_offset += -start; temp_ts_corr_offset += -start;

View File

@@ -21,31 +21,21 @@
#include "sigProcLib.h" #include "sigProcLib.h"
#include "syncthing.h" #include "syncthing.h"
#include "l1if.h"
#include <signalVector.h> #include <signalVector.h>
#include <radioVector.h> #include <radioVector.h>
#include <radioInterface.h> #include <radioInterface.h>
#include "grgsm_vitac/grgsm_vitac.h" #include "grgsm_vitac/grgsm_vitac.h"
#include "ms_state.h"
#include "ms_rx_upper.h" #include "ms_rx_upper.h"
extern "C" { extern "C" {
#include <osmocom/core/select.h>
#include "sch.h" #include "sch.h"
#include "convolve.h" #include "convolve.h"
#include "convert.h" #include "convert.h"
#include "proto_trxd.h" #include "proto_trxd.h"
void __lsan_do_recoverable_leak_check();
} }
namespace trxcon
{
extern "C" {
#include <trxcon/trx_if.h>
}
trx_instance *trxcon_instance; // local handle
static tx_queue_t txq;
} // namespace trxcon
#ifdef LOG #ifdef LOG
#undef LOG #undef LOG
#define LOG(...) upper_trx::dummy_log() #define LOG(...) upper_trx::dummy_log()
@@ -53,6 +43,15 @@ static tx_queue_t txq;
void upper_trx::start_threads() void upper_trx::start_threads()
{ {
thr_rx = std::thread([this] {
set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
while (1) {
driveReceiveFIFO();
pthread_testcancel();
}
});
msleep(1);
thr_control = std::thread([this] { thr_control = std::thread([this] {
set_name_aff_sched("upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR)); set_name_aff_sched("upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR));
while (1) { while (1) {
@@ -69,26 +68,6 @@ void upper_trx::start_threads()
pthread_testcancel(); pthread_testcancel();
} }
}); });
// atomic ensures data is not written to q until loop reads
start_ms();
set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_RR) - 5);
while (1) {
// set_upper_ready(true);
driveReceiveFIFO();
pthread_testcancel();
osmo_select_main(1);
}
// std::thread([this] {
// set_name_aff_sched("leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10);
// while (1) {
// std::this_thread::sleep_for(std::chrono::seconds{ 5 });
// __lsan_do_recoverable_leak_check();
// }
// }).detach();
} }
void upper_trx::start_ms() void upper_trx::start_ms()
@@ -96,65 +75,102 @@ void upper_trx::start_ms()
ms_trx::start(); ms_trx::start();
} }
/* Detect SCH synchronization sequence within a burst */
bool upper_trx::detectSCH(ms_TransceiverState *state, signalVector &burst, struct estim_burst_params *ebp)
{
int shift;
sch_detect_type full;
float mag, threshold = 4.0;
full = (state->mode == trx_mode::TRX_MODE_MS_TRACK) ? sch_detect_type::SCH_DETECT_NARROW :
sch_detect_type::SCH_DETECT_FULL;
if (!detectSCHBurst(burst, threshold, rx_sps, full, ebp))
return false;
std::clog << "SCH : Timing offset " << ebp->toa << " symbols" << std::endl;
mag = fabsf(ebp->toa);
if (mag < 1.0f)
return true;
shift = (int)(mag / 2.0f);
if (!shift)
shift++;
shift = ebp->toa > 0 ? shift : -shift;
std::clog << "SCH : shift -> " << shift << " symbols" << std::endl;
// mRadioInterface->applyOffset(shift);
return false;
}
SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) __attribute__((optnone)) SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) __attribute__((optnone))
{ {
float pow, avg = 1.0; float pow, avg = 1.0;
static SoftVector bits(148); signalVector *burst;
static complex workbuf[40 + 625 + 40]; SoftVector *bits = new SoftVector(148);
static signalVector sv(workbuf, 40, 625);
GSM::Time burst_time; GSM::Time burst_time;
auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[40]);
memset((void *)&workbuf[0], 0, sizeof(workbuf));
// assert(sv.begin() == &workbuf[40]);
one_burst e; one_burst e;
unsigned char outbin[148]; unsigned char outbin[148];
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
std::stringstream dbgout; std::stringstream dbgout;
while (!rxqueue.spsc_pop(&e)) { while (!rxqueue.spsc_pop(&e)) {
rxqueue.spsc_prep_pop(); rxqueue.spsc_prep_pop();
} }
auto sv = signalVector(625, 40);
burst = &sv;
auto ss = reinterpret_cast<std::complex<float> *>(burst->begin());
convert_and_scale<float, int16_t>(burst->begin(), e.burst, ONE_TS_BURST_LEN * 2, 1.f / 2047.f);
/* Set time and determine correlation type */
burst_time = e.gsmts; burst_time = e.gsmts;
wTime = burst_time; wTime = burst_time;
auto is_sch = (burst_time.TN() == 0 && gsm_sch_check_fn(burst_time.FN())); CorrType type = mStates.expectedCorrType(burst_time, mRxSlotMask);
auto is_fcch = (burst_time.TN() == 0 && gsm_fcch_check_fn(burst_time.FN()));
if (is_fcch) { switch (mStates.mode) {
// return trash case trx_mode::TRX_MODE_MS_TRACK:
// fprintf(stderr, "c %d\n",burst_time.FN()); if (gsm_sch_check_fn(burst_time.FN()) && burst_time.TN() == 0)
return &bits; type = SCH;
else if (burst_time.TN() == 0 && !gsm_fcch_check_fn(burst_time.FN())) // all ts0, but not fcch or sch..
type = TSC;
else if (type == OFF)
goto release;
break;
case trx_mode::TRX_MODE_OFF:
default:
goto release;
} }
if (is_sch) { pow = energyDetect(*burst, 20 * rx_sps);
for (int i = 0; i < 148; i++)
(bits)[i] = (!e.sch_bits[i]) < 1 ? -1 : 1;
RSSI = 10;
timingOffset = 0;
// fprintf(stderr, "s %d\n", burst_time.FN());
return &bits;
}
auto ts = trxcon::trxcon_instance->ts_list[burst_time.TN()];
if (ts == NULL || ts->mf_layout == NULL)
return 0;
convert_and_scale<float, int16_t>(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
pow = energyDetect(sv, 20 * rx_sps);
if (pow < -1) { if (pow < -1) {
LOG(ALERT) << "Received empty burst"; LOG(ALERT) << "Received empty burst";
return NULL; goto release;
} }
avg = sqrt(pow); avg = sqrt(pow);
{
if (type == SCH) {
int d_c0_burst_start = get_sch_chan_imp_resp(ss, &chan_imp_resp[0]);
detect_burst(ss, &chan_imp_resp[0], d_c0_burst_start, outbin);
for (int i = 0; i < 148; i++)
(*bits)[i] = (!outbin[i]) < 1 ? -1 : 1;
// auto rv = decode_sch(bits->begin(), false);
// dbgout << "U SCH@"
// << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << d_c0_burst_start
// << " DECODE:" << (rv ? "yes" : "---") << std::endl;
// std::cerr << dbgout.str();
} else {
float ncmax, dcmax; float ncmax, dcmax;
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
std::complex<float> chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR]; std::complex<float> chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC); auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC);
auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY); auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
@@ -163,68 +179,101 @@ SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingO
// std::cerr << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start // std::cerr << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start
// << " o db: " << dummy_burst_start << std::endl; // << " o db: " << dummy_burst_start << std::endl;
normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39; if (is_nb)
normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39; detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin);
else
// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN()); detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
// if (is_nb)
detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin);
// else
// detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
for (int i = 0; i < 148; i++) for (int i = 0; i < 148; i++)
(bits)[i] = (outbin[i]) < 1 ? -1 : 1; (*bits)[i] = (outbin[i]) < 1 ? -1 : 1;
} }
RSSI = (int)floor(20.0 * log10(rxFullScale / avg)); RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
timingOffset = (int)round(0); timingOffset = (int)round(0);
return &bits; return bits;
release:
delete bits;
return NULL;
} }
void upper_trx::driveReceiveFIFO() void upper_trx::driveReceiveFIFO()
{ {
SoftVector *rxBurst = NULL;
int RSSI; int RSSI;
int TOA; // in 1/256 of a symbol int TOA; // in 1/256 of a symbol
GSM::Time burstTime; GSM::Time burstTime;
rxBurst = pullRadioVector(burstTime, RSSI, TOA);
if (!mOn) if (!mOn)
return; return;
SoftVector *rxBurst = pullRadioVector(burstTime, RSSI, TOA); // _only_ return useless fcch trash to tickle trxcons tx path
// auto is_fcch = [&burstTime]{ return burstTime.TN() == 0 && gsm_fcch_check_fn(burstTime.FN());};
// if(!rxBurst && !is_fcch())
// return;
auto response = (trxd_from_trx *)calloc(1, sizeof(trxd_from_trx));
response->ts = burstTime.TN();
response->fn = htonl(burstTime.FN());
response->rssi = RSSI;
response->toa = htons(TOA);
if (rxBurst) { if (rxBurst) {
trxd_from_trx response;
response.ts = burstTime.TN();
response.fn = htonl(burstTime.FN());
response.rssi = RSSI;
response.toa = htons(TOA);
SoftVector::const_iterator burstItr = rxBurst->begin(); SoftVector::const_iterator burstItr = rxBurst->begin();
if (burstTime.TN() == 0 && gsm_sch_check_fn(burstTime.FN())) { if (gsm_sch_check_fn(burstTime.FN())) {
clamp_array(rxBurst->begin(), 148, 1.5f); clamp_array(rxBurst->begin(), 148, 1.5f);
for (unsigned int i = 0; i < gSlotLen; i++) { for (unsigned int i = 0; i < gSlotLen; i++) {
auto val = *burstItr++; auto val = *burstItr++;
auto vval = isnan(val) ? 0 : val; auto vval = isnan(val) ? 0 : val;
((int8_t *)response.symbols)[i] = round((vval - 0.5) * 64.0); ((int8_t *)response->symbols)[i] = round((vval - 0.5) * 64.0);
} }
} else { } else {
// invert and fix to +-127 sbits // invert and fix to +-127 sbits
for (int i = 0; i < 148; i++) for (int i = 0; i < 148; i++)
((int8_t *)response.symbols)[i] = *burstItr++ > 0.0f ? -127 : 127; ((int8_t *)response->symbols)[i] = *burstItr++ > 0.0f ? -127 : 127;
} }
trxcon::trx_data_rx_handler(trxcon::trxcon_instance, (uint8_t *)&response);
delete rxBurst;
} }
#ifdef IPCIF
push_d(response);
#else
int rv = sendto(mDataSockets, response, sizeof(trxd_from_trx), 0, (struct sockaddr *)&datadest,
sizeof(struct sockaddr_in));
if (rv < 0) {
std::cerr << "fuck, send?" << std::endl;
exit(0);
}
free(response);
#endif
} }
void upper_trx::driveTx() void upper_trx::driveTx()
{ {
trxd_to_trx e; #ifdef IPCIF
while (!trxcon::txq.spsc_pop(&e)) { auto burst = pop_d();
trxcon::txq.spsc_prep_pop(); if (!burst) {
// std::cerr << "wtf no tx burst?" << std::endl;
// exit(0);
continue;
}
#else
trxd_to_trx buffer;
socklen_t addr_len = sizeof(datasrc);
int rdln = recvfrom(mDataSockets, (void *)&buffer, sizeof(trxd_to_trx), 0, &datasrc, &addr_len);
if (rdln < 0 && errno == EAGAIN) {
std::cerr << "fuck, rcv?" << std::endl;
exit(0);
} }
trxd_to_trx *burst = &e; trxd_to_trx *burst = &buffer;
#endif
auto proper_fn = ntohl(burst->fn); auto proper_fn = ntohl(burst->fn);
// std::cerr << "got burst!" << proper_fn << ":" << burst->ts // std::cerr << "got burst!" << proper_fn << ":" << burst->ts
// << " current: " << timekeeper.gsmtime().FN() // << " current: " << timekeeper.gsmtime().FN()
@@ -259,9 +308,30 @@ void upper_trx::driveTx()
// memory read --binary --outfile /tmp/mem.bin &burst_buf[0] --count 2500 --force // memory read --binary --outfile /tmp/mem.bin &burst_buf[0] --count 2500 --force
submit_burst(burst_buf, txburst->size(), currTime); submit_burst(burst_buf, txburst->size(), currTime);
delete txburst;
#ifdef IPCIF
free(burst);
#endif
} }
// __attribute__((xray_always_instrument)) static void *rx_stream_callback(struct bladerf *dev,
// struct bladerf_stream *stream,
// struct bladerf_metadata *meta, void *samples,
// size_t num_samples, void *user_data)
// {
// struct ms_trx *trx = (struct ms_trx *)user_data;
// return trx->rx_cb(dev, stream, meta, samples, num_samples, user_data);
// }
// __attribute__((xray_always_instrument)) static void *tx_stream_callback(struct bladerf *dev,
// struct bladerf_stream *stream,
// struct bladerf_metadata *meta, void *samples,
// size_t num_samples, void *user_data)
// {
// struct ms_trx *trx = (struct ms_trx *)user_data;
// return BLADERF_STREAM_NO_DATA;
// }
int trxc_main(int argc, char *argv[]) int trxc_main(int argc, char *argv[])
{ {
pthread_setname_np(pthread_self(), "main_trxc"); pthread_setname_np(pthread_self(), "main_trxc");
@@ -281,21 +351,14 @@ int trxc_main(int argc, char *argv[])
return status; return status;
} }
extern "C" { extern "C" volatile bool gshutdown = false;
void init_external_transceiver(struct trx_instance *trx, int argc, char **argv) extern "C" void init_external_transceiver(int argc, char **argv)
{ {
trxcon::trxcon_instance = (trxcon::trx_instance *)trx;
std::cout << "init?" << std::endl; std::cout << "init?" << std::endl;
trxc_main(argc, argv); trxc_main(argc, argv);
} }
void close_external_transceiver(int argc, char **argv) extern "C" void stop_trx()
{ {
std::cout << "Shutting down transceiver..." << std::endl; std::cout << "Shutting down transceiver..." << std::endl;
} }
void tx_external_transceiver(uint8_t *burst)
{
trxcon::txq.spsc_push((trxd_to_trx *)burst);
}
}

View File

@@ -28,12 +28,13 @@
#include "GSMCommon.h" #include "GSMCommon.h"
#include "radioClock.h" #include "radioClock.h"
#include "syncthing.h" #include "syncthing.h"
#include "l1if.h" #include "ms_state.h"
using tx_queue_t = spsc_cond<8 * 1, trxd_to_trx, true, false>;
class upper_trx : public ms_trx { class upper_trx : public ms_trx {
int rx_sps, tx_sps; int rx_sps, tx_sps;
ms_TransceiverState mStates;
bool mOn; ///< flag to indicate that transceiver is powered on bool mOn; ///< flag to indicate that transceiver is powered on
double mTxFreq; ///< the transmit frequency double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency double mRxFreq; ///< the receive frequency
@@ -41,6 +42,9 @@ class upper_trx : public ms_trx {
unsigned mMaxExpectedDelay; ///< maximum TOA offset in GSM symbols unsigned mMaxExpectedDelay; ///< maximum TOA offset in GSM symbols
unsigned long long mRxSlotMask[8]; ///< MS - enabled multiframe slot mask unsigned long long mRxSlotMask[8]; ///< MS - enabled multiframe slot mask
int mDataSockets;
sockaddr_in datadest;
sockaddr datasrc;
int mCtrlSockets; int mCtrlSockets;
sockaddr_in ctrldest; sockaddr_in ctrldest;
sockaddr ctrlsrc; sockaddr ctrlsrc;
@@ -94,6 +98,8 @@ class upper_trx : public ms_trx {
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset); SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
bool detectSCH(ms_TransceiverState *state, signalVector &burst, struct estim_burst_params *ebp);
std::thread thr_control, thr_rx, thr_tx; std::thread thr_control, thr_rx, thr_tx;
public: public:
@@ -104,8 +110,12 @@ class upper_trx : public ms_trx {
{ {
auto c_srcport = 6700 + 2 * 0 + 1; auto c_srcport = 6700 + 2 * 0 + 1;
auto c_dstport = 6700 + 2 * 0 + 101; auto c_dstport = 6700 + 2 * 0 + 101;
auto d_srcport = 6700 + 2 * 0 + 2;
auto d_dstport = 6700 + 2 * 0 + 102;
openudp(&mCtrlSockets, c_srcport, "127.0.0.1"); openudp(&mCtrlSockets, c_srcport, "127.0.0.1");
openudp(&mDataSockets, d_srcport, "127.0.0.1");
resolveAddress(&ctrldest, "127.0.0.1", c_dstport); resolveAddress(&ctrldest, "127.0.0.1", c_dstport);
resolveAddress(&datadest, "127.0.0.1", d_dstport);
}; };
}; };

View File

@@ -0,0 +1,175 @@
#pragma once
#include <radioVector.h>
#include <signalVector.h>
enum class trx_mode {
TRX_MODE_OFF,
TRX_MODE_BTS,
TRX_MODE_MS_ACQUIRE,
TRX_MODE_MS_TRACK,
};
enum class ChannelCombination {
FILL, ///< Channel is transmitted, but unused
I, ///< TCH/FS
II, ///< TCH/HS, idle every other slot
III, ///< TCH/HS
IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
VI, ///< CCCH+BCCH, uplink RACH
VII, ///< SDCCH/8 + SACCH/8
VIII, ///< TCH/F + FACCH/F + SACCH/M
IX, ///< TCH/F + SACCH/M
X, ///< TCH/FD + SACCH/MD
XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
XII, ///< PCCCH+PDTCH+PACCH+PTCCH
XIII, ///< PDTCH+PACCH+PTCCH
NONE_INACTIVE, ///< Channel is inactive, default
LOOPBACK ///< similar go VII, used in loopback testing
};
struct ms_TransceiverState {
ms_TransceiverState() : mFreqOffsets(10), mode(trx_mode::TRX_MODE_OFF)
{
for (int i = 0; i < 8; i++) {
chanType[i] = ChannelCombination::NONE_INACTIVE;
fillerModulus[i] = 26;
for (int n = 0; n < 102; n++)
fillerTable[n][i] = nullptr;
}
}
~ms_TransceiverState()
{
for (int i = 0; i < 8; i++) {
for (int n = 0; n < 102; n++)
delete fillerTable[n][i];
}
}
void setModulus(size_t timeslot)
{
switch (chanType[timeslot]) {
case ChannelCombination::NONE_INACTIVE:
case ChannelCombination::I:
case ChannelCombination::II:
case ChannelCombination::III:
case ChannelCombination::FILL:
fillerModulus[timeslot] = 26;
break;
case ChannelCombination::IV:
case ChannelCombination::VI:
case ChannelCombination::V:
fillerModulus[timeslot] = 51;
break;
//case V:
case ChannelCombination::VII:
fillerModulus[timeslot] = 102;
break;
case ChannelCombination::XIII:
fillerModulus[timeslot] = 52;
break;
default:
break;
}
}
CorrType expectedCorrType(GSM::Time currTime, unsigned long long *mRxSlotMask)
{
unsigned burstTN = currTime.TN();
unsigned burstFN = currTime.FN();
if (mode == trx_mode::TRX_MODE_MS_TRACK) {
/* 102 modulus case currently unhandled */
if (fillerModulus[burstTN] > 52)
return OFF;
int modFN = burstFN % fillerModulus[burstTN];
unsigned long long reg = (unsigned long long)1 << modFN;
if (reg & mRxSlotMask[burstTN])
return TSC;
else
return OFF;
}
switch (chanType[burstTN]) {
case ChannelCombination::NONE_INACTIVE:
return OFF;
break;
case ChannelCombination::FILL:
return IDLE;
break;
case ChannelCombination::I:
return TSC;
/*if (burstFN % 26 == 25)
return IDLE;
else
return TSC;*/
break;
case ChannelCombination::II:
return TSC;
break;
case ChannelCombination::III:
return TSC;
break;
case ChannelCombination::IV:
case ChannelCombination::VI:
return RACH;
break;
case ChannelCombination::V: {
int mod51 = burstFN % 51;
if ((mod51 <= 36) && (mod51 >= 14))
return RACH;
else if ((mod51 == 4) || (mod51 == 5))
return RACH;
else if ((mod51 == 45) || (mod51 == 46))
return RACH;
else
return TSC;
break;
}
case ChannelCombination::VII:
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
return IDLE;
else
return TSC;
break;
case ChannelCombination::XIII: {
int mod52 = burstFN % 52;
if ((mod52 == 12) || (mod52 == 38))
return RACH;
else if ((mod52 == 25) || (mod52 == 51))
return IDLE;
else
return TSC;
break;
}
case ChannelCombination::LOOPBACK:
if ((burstFN % 51 <= 50) && (burstFN % 51 >= 48))
return IDLE;
else
return TSC;
break;
default:
return OFF;
break;
}
}
/* Initialize a multiframe slot in the filler table */
void init(size_t slot, signalVector *burst, bool fill);
ChannelCombination chanType[8];
/* The filler table */
signalVector *fillerTable[102][8];
int fillerModulus[8];
/* Received noise energy levels */
avgVector mFreqOffsets;
/* Transceiver mode */
trx_mode mode;
};

View File

@@ -119,7 +119,7 @@ void tx_test(ms_trx *t, ts_hitter_q_t *q, unsigned int *tsc)
t->submit_burst_ts(buf2, burst->size() + pad, send_ts - pad); t->submit_burst_ts(buf2, burst->size() + pad, send_ts - pad);
// signalVector test(burst->size() + pad); // signalVector test(burst->size() + pad);
// convert_and_scale<float, int16_t>(test.begin(), buf2, burst->size() * 2 + pad, 1.f / float(scale)); // convert_and_scale<float, int16_t>(test.begin(), buf2, burst->size() * 2 + pad, 1.f / 2047.f);
// estim_burst_params ebp; // estim_burst_params ebp;
// auto det = detectAnyBurst(test, 0, 4, 4, CorrType::RACH, 40, &ebp); // auto det = detectAnyBurst(test, 0, 4, 4, CorrType::RACH, 40, &ebp);
// if (det > 0) // if (det > 0)
@@ -214,7 +214,7 @@ int main(int argc, char *argv[])
if (status == 0) { if (status == 0) {
// FIXME: hacks! needs exit flag for detached threads! // FIXME: hacks! needs exit flag for detached threads!
std::thread(rcv_bursts_test, &trx->rxqueue, &trx->mTSC, trx->rxFullScale).detach(); std::thread(rcv_bursts_test, &trx->rxqueue, &trx->mTSC).detach();
if (tx_flag) if (tx_flag)
std::thread(tx_test, trx, &trx->ts_hitter_q, &trx->mTSC).detach(); std::thread(tx_test, trx, &trx->ts_hitter_q, &trx->mTSC).detach();
trx->start(); trx->start();
@@ -276,11 +276,6 @@ void ms_trx::start()
tx_task = std::thread(fn2); tx_task = std::thread(fn2);
} }
void ms_trx::set_upper_ready(bool is_ready)
{
upper_is_ready = is_ready;
}
void ms_trx::stop_threads() void ms_trx::stop_threads()
{ {
std::cerr << "killing threads...\r\n" << std::endl; std::cerr << "killing threads...\r\n" << std::endl;

View File

@@ -35,9 +35,6 @@
#elif defined(BUILDUHD) #elif defined(BUILDUHD)
#include "uhd_specific.h" #include "uhd_specific.h"
#define BASET uhd_hw<ms_trx> #define BASET uhd_hw<ms_trx>
#elif defined(BUILDIPC)
#include "ipc_specific.h"
#define BASET ipc_hw<ms_trx>
#else #else
#error wat? no device.. #error wat? no device..
#endif #endif
@@ -46,7 +43,7 @@
#include "itrq.h" #include "itrq.h"
const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/; const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue const unsigned int NUM_RXQ_FRAMES = 12 * 1; // rx thread <-> upper rx queue
const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/); const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
template <typename T> void clamp_array(T *start2, unsigned int len, T max) template <typename T> void clamp_array(T *start2, unsigned int len, T max)
@@ -69,17 +66,11 @@ template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *d
} }
struct one_burst { struct one_burst {
one_burst()
{
}
GSM::Time gsmts; GSM::Time gsmts;
union { blade_sample_type burst[ONE_TS_BURST_LEN];
blade_sample_type burst[ONE_TS_BURST_LEN];
unsigned char sch_bits[148];
};
}; };
using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>; using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, false>;
enum class SCH_STATE { SEARCHING, FOUND }; enum class SCH_STATE { SEARCHING, FOUND };
@@ -195,10 +186,8 @@ struct ms_trx : public BASET {
time_keeper timekeeper; time_keeper timekeeper;
void start(); void start();
std::atomic<bool> upper_is_ready;
void set_upper_ready(bool is_ready);
bool handle_sch_or_nb(); bool handle_sch_or_nb(bool first = false);
bool handle_sch(bool first = false); bool handle_sch(bool first = false);
bool decode_sch(float *bits, bool update_global_clock); bool decode_sch(float *bits, bool update_global_clock);
SCH_STATE search_for_sch(dev_buf_t *rcd); SCH_STATE search_for_sch(dev_buf_t *rcd);

View File

@@ -74,8 +74,8 @@ template <typename T> struct uhd_hw {
std::vector<blade_sample_type *> pkt_ptrs; std::vector<blade_sample_type *> pkt_ptrs;
size_t rx_spp; size_t rx_spp;
double rxticks; double rxticks;
const unsigned int rxFullScale, txFullScale; unsigned int rxFullScale, txFullScale;
const int rxtxdelay; int rxtxdelay;
float rxgain, txgain; float rxgain, txgain;
virtual ~uhd_hw() virtual ~uhd_hw()

View File

@@ -0,0 +1,718 @@
/*
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* 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 "Transceiver.h"
#include "radioDevice.h"
#include "Utils.h"
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <sched.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <sys/signalfd.h>
#include <GSMCommon.h>
#include <Logger.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/stats.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/misc.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/ctrl/ports.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/cpu_sched_vty.h>
#include "convolve.h"
#include "convert.h"
#include "trx_vty.h"
#include "debug.h"
#include "osmo_signal.h"
#include "trx_rate_ctr.h"
}
#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
static char* config_file = (char*)DEFAULT_CONFIG_FILE;
struct osmo_fd signal_ofd;
volatile bool gshutdown = false;
static void *tall_trx_ctx;
static struct trx_ctx *g_trx_ctx;
static struct ctrl_handle *g_ctrlh;
static RadioDevice *usrp;
static RadioInterface *radio;
/* Create radio interface
* The interface consists of sample rate changes, frequency shifts,
* channel multiplexing, and other conversions. The transceiver core
* accepts input vectors sampled at multiples of the GSM symbol rate.
* The radio interface connects the main transceiver with the device
* object, which may be operating some other rate.
*/
RadioInterface *makeRadioInterface(struct trx_ctx *trx,
RadioDevice *usrp, int type)
{
RadioInterface *radio = NULL;
size_t div = 1;
int offset = 3;
if (trx->cfg.ms) {
if (type != RadioDevice::NORMAL) {
LOG(ALERT) << "Unsupported configuration";
return NULL;
}
offset *= -1;
}
switch (type) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans, offset, offset);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps);
break;
case RadioDevice::MULTI_ARFCN:
radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
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;
}
/* Callback function to be called every time we receive a signal from TRANSC */
static int transc_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
switch (signal) {
case S_MAIN_STOP_REQUIRED:
gshutdown = true;
break;
default:
break;
}
return 0;
}
/* Create transceiver core
* The multi-threaded modem core operates at multiples of the GSM rate of
* 270.8333 ksps and consists of GSM specific modulation, demodulation,
* and decoding schemes. Also included are the socket interfaces for
* connecting to the upper layer stack.
*/
int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
{
VectorFIFO *fifo;
transceiver = new Transceiver2(&trx->cfg, GSM::Time(3,0), radio);
if (!transceiver->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 && transceiver->receiveFIFO(fifo, i))
continue;
LOG(ALERT) << "Could not attach FIFO to channel " << i;
return -1;
}
return 0;
}
static void sig_handler(int signo)
{
if (gshutdown)
/* We are in the middle of shutdown process, avoid any kind of extra
action like printing */
return;
fprintf(stderr, "signal %d received\n", signo);
switch (signo) {
case SIGINT:
case SIGTERM:
fprintf(stderr, "shutting down\n");
gshutdown = true;
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report and
* then run default SIGABRT handler, who will generate coredump
* and abort the process. abort() should do this for us after we
* return, but program wouldn't exit if an external SIGABRT is
* received.
*/
talloc_report(tall_trx_ctx, stderr);
talloc_report_full(tall_trx_ctx, stderr);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
break;
case SIGUSR1:
talloc_report(tall_trx_ctx, stderr);
talloc_report_full(tall_trx_ctx, stderr);
break;
case SIGUSR2:
talloc_report_full(tall_trx_ctx, stderr);
break;
case SIGHUP:
log_targets_reopen();
default:
break;
}
}
static int signalfd_callback(struct osmo_fd *ofd, unsigned int what)
{
struct signalfd_siginfo fdsi;
ssize_t s;
s = read(ofd->fd, &fdsi, sizeof(struct signalfd_siginfo));
if (s < 0) {
LOG(FATAL) << "Failed to read from signalfd ("<< ofd->fd << "): " << errno;
gshutdown = true;
return 0;
}
sig_handler(fdsi.ssi_signo);
return 0;
}
static void setup_signal_handlers()
{
sigset_t set;
int sfd;
signal(SIGABRT, &sig_handler);
osmo_init_ignore_signals();
/* Other threads created by this thread (main) will inherit a copy of the
signal mask. */
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGUSR2);
sigaddset(&set, SIGHUP);
if (pthread_sigmask(SIG_BLOCK, &set, NULL)) {
fprintf(stderr, "pthread_sigmask() failed.\n");
exit(EXIT_FAILURE);
}
if ((sfd = signalfd(-1, &set, 0)) == -1) {
fprintf(stderr, "signalfd() failed (%d).\n", errno);
exit(EXIT_FAILURE);
}
osmo_fd_setup(&signal_ofd, sfd, OSMO_FD_READ, signalfd_callback, NULL, 0);
if (osmo_fd_register(&signal_ofd) < 0) {
fprintf(stderr, "osmo_fd_register() failed.\n");
exit(EXIT_FAILURE);
}
}
static void print_help()
{
printf( "Some useful options:\n"
" -h, --help This text\n"
" -C, --config Filename The config file to use\n"
" -V, --version Print the version of OsmoTRX\n"
"\nVTY reference generation:\n"
" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
" --vty-ref-xml Generate the VTY reference XML output and exit.\n"
);
}
static void print_deprecated(char opt)
{
LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
<< " Please use VTY cfg option instead."
<< " All cmd line options are already being overridden by VTY options if set.";
}
static void handle_long_options(const char *prog_name, const int long_option)
{
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
switch (long_option) {
case 1:
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
if (vty_ref_mode < 0) {
fprintf(stderr, "%s: Unknown VTY reference generation "
"mode '%s'\n", prog_name, optarg);
exit(2);
}
break;
case 2:
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
}
}
static void handle_options(int argc, char **argv, struct trx_ctx* trx)
{
int option;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
bool rx_paths_set = false, tx_paths_set = false;
static int long_option = 0;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config", 1, 0, 'C'},
{"version", 0, 0, 'V'},
{"vty-ref-mode", 1, &long_option, 1},
{"vty-ref-xml", 0, &long_option, 2},
{NULL, 0, 0, 0}
};
while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options,
NULL)) != -1) {
switch (option) {
case 'h':
print_help();
exit(0);
break;
case 0:
handle_long_options(argv[0], long_option);
break;
case 'a':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
break;
case 'l':
print_deprecated(option);
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'i':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg);
break;
case 'j':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg);
break;
case 'p':
print_deprecated(option);
trx->cfg.base_port = atoi(optarg);
break;
case 'c':
print_deprecated(option);
trx->cfg.num_chans = atoi(optarg);
break;
case 'm':
print_deprecated(option);
trx->cfg.multi_arfcn = true;
break;
case 'x':
print_deprecated(option);
trx->cfg.clock_ref = REF_EXTERNAL;
break;
case 'g':
print_deprecated(option);
trx->cfg.clock_ref = REF_GPS;
break;
case 'f':
print_deprecated(option);
trx->cfg.filler = FILLER_DUMMY;
break;
case 'o':
print_deprecated(option);
trx->cfg.offset = atof(optarg);
break;
case 's':
print_deprecated(option);
trx->cfg.tx_sps = atoi(optarg);
break;
case 'b':
print_deprecated(option);
trx->cfg.rx_sps = atoi(optarg);
break;
case 'r':
print_deprecated(option);
trx->cfg.rtsc = atoi(optarg);
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
trx->cfg.filler = FILLER_NORM_RAND;
break;
case 'A':
print_deprecated(option);
trx->cfg.rach_delay = atoi(optarg);
trx->cfg.filler = FILLER_ACCESS_RAND;
break;
case 'R':
print_deprecated(option);
trx->cfg.rssi_offset = atof(optarg);
trx->cfg.force_rssi_offset = true;
break;
case 'S':
print_deprecated(option);
trx->cfg.swap_channels = true;
break;
case 'e':
print_deprecated(option);
trx->cfg.egprs = true;
break;
case 't':
print_deprecated(option);
trx->cfg.sched_rr = atoi(optarg);
break;
case 'y':
print_deprecated(option);
tx_paths = comma_delimited_to_vector(optarg);
tx_paths_set = true;
break;
case 'z':
print_deprecated(option);
rx_paths = comma_delimited_to_vector(optarg);
rx_paths_set = true;
break;
case 'C':
config_file = optarg;
break;
case 'V':
print_version(1);
exit(0);
break;
default:
goto bad_config;
}
}
trx->cfg.ms = true;
if (argc > optind) {
LOG(ERROR) << "Unsupported positional arguments on command line";
goto bad_config;
}
/* Cmd line option specific validation & setup */
if (trx->cfg.num_chans > TRX_CHAN_MAX) {
LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX;
goto bad_config;
}
if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) ||
(rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) {
LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match";
goto bad_config;
}
for (i = 0; i < trx->cfg.num_chans; i++) {
trx->cfg.chans[i].trx = trx;
trx->cfg.chans[i].idx = i;
if (tx_paths_set)
osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str());
if (rx_paths_set)
osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str());
}
return;
bad_config:
print_help();
exit(0);
}
int trx_validate_config(struct trx_ctx *trx)
{
if (trx->cfg.multi_arfcn && trx->cfg.num_chans > TRX_MCHAN_MAX) {
LOG(ERROR) << "Unsupported number of channels";
return -1;
}
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
if ((trx->cfg.egprs || trx->cfg.multi_arfcn) &&
(trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) {
LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
return -1;
}
return 0;
}
static int set_sched_rr(unsigned int prio)
{
struct sched_param param;
int rc;
memset(&param, 0, sizeof(param));
param.sched_priority = prio;
LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority
<< ". This setting is DEPRECATED, please use 'policy rr " << param.sched_priority
<< "' under the 'sched' VTY node instead.";
rc = sched_setscheduler(getpid(), SCHED_RR, &param);
if (rc != 0) {
LOG(ERROR) << "Config: Setting SCHED_RR failed";
return -1;
}
return 0;
}
static void print_simd_info(void)
{
#ifdef HAVE_SSE3
LOGP(DMAIN, LOGL_INFO, "SSE3 support compiled in");
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (__builtin_cpu_supports("sse3"))
LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
else
LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
#else
LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
#endif
#endif
#ifdef HAVE_SSE4_1
LOGP(DMAIN, LOGL_INFO, "SSE4.1 support compiled in");
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (__builtin_cpu_supports("sse4.1"))
LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
else
LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
#else
LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
#endif
#endif
#ifndef HAVE_ATOMIC_OPS
#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
LOG(NOTICE) << "Built without atomic operation support. Using Mutex, it may affect performance!";
#endif
}
static void print_config(struct trx_ctx *trx)
{
unsigned int i;
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl;
ost << " Device args............. " << charp2str(trx->cfg.dev_args) << std::endl;
ost << " TRX Base Port........... " << trx->cfg.base_port << std::endl;
ost << " TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl;
ost << " GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl;
ost << " Channels................ " << trx->cfg.num_chans << std::endl;
ost << " Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl;
ost << " Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl;
ost << " EDGE support............ " << trx->cfg.egprs << std::endl;
ost << " Extended RACH support... " << trx->cfg.ext_rach << std::endl;
ost << " Reference............... " << trx->cfg.clock_ref << std::endl;
ost << " Filler Burst Type....... " << get_value_string(filler_names, trx->cfg.filler) << std::endl;
ost << " Filler Burst TSC........ " << trx->cfg.rtsc << std::endl;
ost << " Filler Burst RACH Delay. " << trx->cfg.rach_delay << std::endl;
ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
ost << " LO freq. offset......... " << trx->cfg.offset << std::endl;
if (trx->cfg.freq_offset_khz != 0)
ost << " Tune freq. offset....... " << trx->cfg.freq_offset_khz << std::endl;
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << (trx->cfg.force_rssi_offset ? "" : " (relative)") << std::endl;
ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
ost << " Tx Antennas.............";
for (i = 0; i < trx->cfg.num_chans; i++) {
std::string p = charp2str(trx->cfg.chans[i].tx_path);
ost << " '" << ((p != "") ? p : "<default>") << "'";
}
ost << std::endl;
ost << " Rx Antennas.............";
for (i = 0; i < trx->cfg.num_chans; i++) {
std::string p = charp2str(trx->cfg.chans[i].rx_path);
ost << " '" << ((p != "") ? p : "<default>") << "'";
}
ost << std::endl;
LOG(INFO) << ost << std::endl;
}
static void trx_stop()
{
LOG(NOTICE) << "Shutting down transceiver..." << std::endl;
delete transceiver;
delete radio;
delete usrp;
}
static int trx_start(struct trx_ctx *trx)
{
int type, chans;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
/* Create the low level device object */
if (trx->cfg.multi_arfcn)
iface = RadioDevice::MULTI_ARFCN;
/* 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));
}
usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
trx->cfg.num_chans, trx->cfg.offset,
tx_paths, rx_paths);
type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;
}
/* Setup the appropriate device interface */
radio = makeRadioInterface(trx, usrp, type);
if (!radio)
goto shutdown;
/* Create the transceiver core */
if (makeTransceiver(trx, radio) < 0)
goto shutdown;
chans = transceiver->numChans();
LOG(NOTICE) << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl;
return 0;
shutdown:
trx_stop();
return -1;
}
int main(int argc, char *argv[])
{
int rc;
tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
msgb_talloc_ctx_init(tall_trx_ctx, 0);
g_vty_info.tall_ctx = tall_trx_ctx;
setup_signal_handlers();
g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
convolve_init();
convert_init();
osmo_init_logging2(tall_trx_ctx, &log_info);
log_enable_multithread();
osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info);
logging_vty_add_cmds();
ctrl_vty_init(tall_trx_ctx);
osmo_cpu_sched_vty_init(tall_trx_ctx);
trx_vty_init(g_trx_ctx);
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();
handle_options(argc, argv, g_trx_ctx);
rate_ctr_init(tall_trx_ctx);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
exit(2);
}
rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
if (rc < 0)
exit(1);
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1);
}
/* Backward compatibility: Hack to have 1 channel allocated by default.
* Can be Dropped once we * get rid of "-c" cmdline param */
if (g_trx_ctx->cfg.num_chans == 0) {
g_trx_ctx->cfg.num_chans = 1;
g_trx_ctx->cfg.chans[0].trx = g_trx_ctx;
g_trx_ctx->cfg.chans[0].idx = 0;
LOG(ERROR) << "No explicit channel config found. Make sure you" \
" configure channels in VTY config. Using 1 channel as default," \
" but expect your config to break in the future.";
}
print_simd_info();
print_config(g_trx_ctx);
if (trx_validate_config(g_trx_ctx) < 0) {
LOG(ERROR) << "Config failure - exiting";
return EXIT_FAILURE;
}
if (g_trx_ctx->cfg.sched_rr) {
if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0)
return EXIT_FAILURE;
}
osmo_signal_register_handler(SS_MAIN, transc_sig_cb, NULL);
trx_rate_ctr_init(tall_trx_ctx, g_trx_ctx);
srandom(time(NULL));
if(trx_start(g_trx_ctx) < 0)
return EXIT_FAILURE;
while (!gshutdown)
osmo_select_main(0);
trx_stop();
osmo_fd_unregister(&signal_ofd);
osmo_fd_close(&signal_ofd);
osmo_signal_unregister_handler(SS_MAIN, transc_sig_cb, NULL);
return 0;
}

View File

@@ -1,8 +1,6 @@
/* /*
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc> * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
* *
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
@@ -12,27 +10,23 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. * Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
#include "Transceiver.h" #include "Transceiver2.h"
#include "radioDevice.h" #include "radioDevice.h"
#include "Utils.h"
#include <time.h> #include <time.h>
#include <signal.h> #include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <getopt.h>
#include <sched.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <sys/signalfd.h>
#include <GSMCommon.h> #include <GSMCommon.h>
#include <Logger.h> #include <Logger.h>
@@ -61,21 +55,101 @@ extern "C" {
#include "trx_rate_ctr.h" #include "trx_rate_ctr.h"
} }
#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
#define charp2str(a) ((a) ? std::string(a) : std::string("")) //#include <mempool.cpp>
//atomicstackpool<NUM_ALLOCS,ALLOC_SZ> ALLOCPRIO a0;
static char* config_file = (char*)DEFAULT_CONFIG_FILE; /* Samples-per-symbol for downlink path
* 4 - Uses precision modulator (more computation, less distortion)
* 1 - Uses minimized modulator (less computation, more distortion)
*
* Other values are invalid. Receive path (uplink) is always
* downsampled to 1 sps. Default to 4 sps for all cases except for
* ARM and non-SIMD enabled architectures.
*/
#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
#define DEFAULT_SPS 1
#else
#define DEFAULT_SPS 4
#endif
struct osmo_fd signal_ofd; /* Default configuration parameters
volatile bool gshutdown = false; * Note that these values are only used if the particular key does not
* exist in the configuration database. IP port and address values will
* typically be overwritten by the OpenBTS.db values. Other values will
* not be in the database by default.
*/
#define DEFAULT_TRX_PORT 5700
#define DEFAULT_TRX_IP "127.0.0.1"
#define DEFAULT_EXTREF false
#define DEFAULT_DIVERSITY false
#define DEFAULT_CHANS 1
static void *tall_trx_ctx; struct trx_config {
static struct trx_ctx *g_trx_ctx; std::string log_level;
static struct ctrl_handle *g_ctrlh; std::string addr;
std::string dev_args;
unsigned port;
unsigned sps;
unsigned chans;
bool extref;
bool filler;
bool diversity;
bool ms;
double offset;
};
static RadioDevice *usrp;
static RadioInterface *radio;
extern "C" volatile bool gshutdown = false;
/* Setup configuration values
* Don't query the existence of the Log.Level because it's a
* mandatory value. That is, if it doesn't exist, the configuration
* table will crash or will have already crashed. Everything else we
* can survive without and use default values if the database entries
* are empty.
*/
bool trx_setup_config(struct trx_config *config)
{
std::string refstr, fillstr, divstr, msstr;
config->log_level = "foo";
config->port = DEFAULT_TRX_PORT;
config->addr = DEFAULT_TRX_IP;
//config->extref = DEFAULT_EXTREF;
config->diversity = DEFAULT_DIVERSITY;
config->sps = 4;//DEFAULT_SPS;
config->chans = DEFAULT_CHANS;
/* Diversity only supported on 2 channels */
if (config->diversity)
config->chans = 2;
refstr = config->extref ? "Enabled" : "Disabled";
fillstr = config->filler ? "Enabled" : "Disabled";
divstr = config->diversity ? "Enabled" : "Disabled";
msstr = config->ms ? "Enabled" : "Disabled";
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Log Level............... " << config->log_level << std::endl;
ost << " Device args............. " << config->dev_args << std::endl;
ost << " TRX Base Port........... " << config->port << std::endl;
ost << " TRX Address............. " << config->addr << std::endl;
ost << " Channels................ " << config->chans << std::endl;
ost << " Samples-per-Symbol...... " << config->sps << std::endl;
ost << " External Reference...... " << refstr << std::endl;
ost << " C0 Filler Table......... " << fillstr << std::endl;
ost << " Diversity............... " << divstr << std::endl;
ost << " MS Mode................. " << msstr << std::endl;
ost << " Tuning offset........... " << config->offset << std::endl;
std::cout << ost.str() << std::endl;
return true;
}
/* Create radio interface /* Create radio interface
* The interface consists of sample rate changes, frequency shifts, * The interface consists of sample rate changes, frequency shifts,
@@ -84,24 +158,26 @@ static RadioInterface *radio;
* The radio interface connects the main transceiver with the device * The radio interface connects the main transceiver with the device
* object, which may be operating some other rate. * object, which may be operating some other rate.
*/ */
RadioInterface *makeRadioInterface(struct trx_ctx *trx, RadioInterface *makeRadioInterface(struct trx_config *config,
RadioDevice *usrp, int type) RadioDevice *usrp, int type)
{ {
RadioInterface *radio = NULL; RadioInterface *radio = NULL;
size_t div = 1;
int offset = 3;
if (config->ms) {
if (type != RadioDevice::NORMAL) {
LOG(ALERT) << "Unsupported configuration";
return NULL;
}
offset *= -1;
}
switch (type) { switch (type) {
case RadioDevice::NORMAL: case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, trx->cfg.tx_sps, radio = new RadioInterface(usrp, config->sps, config->sps,
trx->cfg.rx_sps, trx->cfg.num_chans); config->chans, div, offset);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps);
break;
case RadioDevice::MULTI_ARFCN:
radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
break; break;
default: default:
LOG(ALERT) << "Unsupported radio interface configuration"; LOG(ALERT) << "Unsupported radio interface configuration";
@@ -116,590 +192,257 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
return radio; return radio;
} }
/* Callback function to be called every time we receive a signal from TRANSC */
static int transc_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
switch (signal) {
case S_MAIN_STOP_REQUIRED:
gshutdown = true;
break;
default:
break;
}
return 0;
}
/* Create transceiver core /* Create transceiver core
* The multi-threaded modem core operates at multiples of the GSM rate of * The multi-threaded modem core operates at multiples of the GSM rate of
* 270.8333 ksps and consists of GSM specific modulation, demodulation, * 270.8333 ksps and consists of GSM specific modulation, demodulation,
* and decoding schemes. Also included are the socket interfaces for * and decoding schemes. Also included are the socket interfaces for
* connecting to the upper layer stack. * connecting to the upper layer stack.
*/ */
int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio) Transceiver2 *makeTransceiver(struct trx_config *config, RadioInterface *radio)
{ {
Transceiver2 *trx;
VectorFIFO *fifo; VectorFIFO *fifo;
transceiver = new Transceiver(&trx->cfg, GSM::Time(3,0), radio); trx = new Transceiver2(config->port, config->addr.c_str(), config->sps,
if (!transceiver->init()) { config->chans, GSM::Time(3,0), radio);
if (!trx->init(config->filler)) {
LOG(ALERT) << "Failed to initialize transceiver"; LOG(ALERT) << "Failed to initialize transceiver";
return -1; delete trx;
return NULL;
} }
for (size_t i = 0; i < trx->cfg.num_chans; i++) { for (size_t i = 0; i < config->chans; i++) {
fifo = radio->receiveFIFO(i); fifo = radio->receiveFIFO(i);
if (fifo && transceiver->receiveFIFO(fifo, i)) if (fifo && trx->receiveFIFO(fifo, i))
continue; continue;
LOG(ALERT) << "Could not attach FIFO to channel " << i; LOG(ALERT) << "Could not attach FIFO to channel " << i;
return -1; delete trx;
return NULL;
} }
return 0;
return trx;
} }
static void sig_handler(int signo) static void sig_handler(int signo)
{ {
fprintf(stdout, "Received shutdown signal");
if (gshutdown) gshutdown = true;
/* We are in the middle of shutdown process, avoid any kind of extra
action like printing */
return;
fprintf(stderr, "signal %d received\n", signo);
switch (signo) {
case SIGINT:
case SIGTERM:
fprintf(stderr, "shutting down\n");
gshutdown = true;
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report and
* then run default SIGABRT handler, who will generate coredump
* and abort the process. abort() should do this for us after we
* return, but program wouldn't exit if an external SIGABRT is
* received.
*/
talloc_report(tall_trx_ctx, stderr);
talloc_report_full(tall_trx_ctx, stderr);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
break;
case SIGUSR1:
talloc_report(tall_trx_ctx, stderr);
talloc_report_full(tall_trx_ctx, stderr);
break;
case SIGUSR2:
talloc_report_full(tall_trx_ctx, stderr);
break;
case SIGHUP:
log_targets_reopen();
default:
break;
}
}
static int signalfd_callback(struct osmo_fd *ofd, unsigned int what)
{
struct signalfd_siginfo fdsi;
ssize_t s;
s = read(ofd->fd, &fdsi, sizeof(struct signalfd_siginfo));
if (s < 0) {
LOG(FATAL) << "Failed to read from signalfd ("<< ofd->fd << "): " << errno;
gshutdown = true;
return 0;
}
sig_handler(fdsi.ssi_signo);
return 0;
} }
static void setup_signal_handlers() static void setup_signal_handlers()
{ {
sigset_t set; if (signal(SIGINT, sig_handler) == SIG_ERR) {
int sfd; fprintf(stderr, "Failed to install SIGINT signal handler\n");
signal(SIGABRT, &sig_handler);
osmo_init_ignore_signals();
/* Other threads created by this thread (main) will inherit a copy of the
signal mask. */
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGUSR2);
sigaddset(&set, SIGHUP);
if (pthread_sigmask(SIG_BLOCK, &set, NULL)) {
fprintf(stderr, "pthread_sigmask() failed.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (signal(SIGTERM, sig_handler) == SIG_ERR) {
if ((sfd = signalfd(-1, &set, 0)) == -1) { fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
fprintf(stderr, "signalfd() failed (%d).\n", errno); exit( EXIT_FAILURE);
exit(EXIT_FAILURE);
}
osmo_fd_setup(&signal_ofd, sfd, OSMO_FD_READ, signalfd_callback, NULL, 0);
if (osmo_fd_register(&signal_ofd) < 0) {
fprintf(stderr, "osmo_fd_register() failed.\n");
exit(EXIT_FAILURE);
} }
} }
static void print_help() static void print_help()
{ {
printf( "Some useful options:\n" fprintf(stdout, "Options:\n"
" -h, --help This text\n" " -h This text\n"
" -C, --config Filename The config file to use\n" " -a UHD device args\n"
" -V, --version Print the version of OsmoTRX\n" " -l Logging level (%s)\n"
"\nVTY reference generation:\n" " -i IP address of GSM core\n"
" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n" " -p Base port number\n"
" --vty-ref-xml Generate the VTY reference XML output and exit.\n" " -d Enable dual channel diversity receiver\n"
); " -x Enable external 10 MHz reference\n"
" -s Samples-per-symbol (1 or 4)\n"
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -m Enable MS mode\n"
" -o Set baseband frequency offset (default=auto)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
} }
static void print_deprecated(char opt) static void handle_options(int argc, char **argv, struct trx_config *config)
{
LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
<< " Please use VTY cfg option instead."
<< " All cmd line options are already being overridden by VTY options if set.";
}
static void handle_long_options(const char *prog_name, const int long_option)
{
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
switch (long_option) {
case 1:
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
if (vty_ref_mode < 0) {
fprintf(stderr, "%s: Unknown VTY reference generation "
"mode '%s'\n", prog_name, optarg);
exit(2);
}
break;
case 2:
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
}
}
static void handle_options(int argc, char **argv, struct trx_ctx* trx)
{ {
int option; int option;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
bool rx_paths_set = false, tx_paths_set = false;
static int long_option = 0;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config", 1, 0, 'C'},
{"version", 0, 0, 'V'},
{"vty-ref-mode", 1, &long_option, 1},
{"vty-ref-xml", 0, &long_option, 2},
{NULL, 0, 0, 0}
};
while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options, optind=1;
NULL)) != -1) {
config->port = 0;
config->sps = 0;
config->chans = 0;
config->extref = true;
config->filler = false;
config->diversity = false;
config->ms = true;
config->offset = 0.0;
while ((option = getopt(argc, argv, "xo")) != -1) {
switch (option) { switch (option) {
case 'h':
print_help();
exit(0);
break;
case 0:
handle_long_options(argv[0], long_option);
break;
case 'a':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
break;
case 'l':
print_deprecated(option);
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'i':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg);
break;
case 'j':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg);
break;
case 'p':
print_deprecated(option);
trx->cfg.base_port = atoi(optarg);
break;
case 'c':
print_deprecated(option);
trx->cfg.num_chans = atoi(optarg);
break;
case 'm':
print_deprecated(option);
trx->cfg.multi_arfcn = true;
break;
case 'x': case 'x':
print_deprecated(option); config->extref = true;
trx->cfg.clock_ref = REF_EXTERNAL;
break;
case 'g':
print_deprecated(option);
trx->cfg.clock_ref = REF_GPS;
break;
case 'f':
print_deprecated(option);
trx->cfg.filler = FILLER_DUMMY;
break; break;
case 'o': case 'o':
print_deprecated(option); config->offset = atof(optarg);
trx->cfg.offset = atof(optarg);
break;
case 's':
print_deprecated(option);
trx->cfg.tx_sps = atoi(optarg);
break;
case 'b':
print_deprecated(option);
trx->cfg.rx_sps = atoi(optarg);
break;
case 'r':
print_deprecated(option);
trx->cfg.rtsc = atoi(optarg);
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
trx->cfg.filler = FILLER_NORM_RAND;
break;
case 'A':
print_deprecated(option);
trx->cfg.rach_delay = atoi(optarg);
trx->cfg.filler = FILLER_ACCESS_RAND;
break;
case 'R':
print_deprecated(option);
trx->cfg.rssi_offset = atof(optarg);
trx->cfg.force_rssi_offset = true;
break;
case 'S':
print_deprecated(option);
trx->cfg.swap_channels = true;
break;
case 'e':
print_deprecated(option);
trx->cfg.egprs = true;
break;
case 't':
print_deprecated(option);
trx->cfg.sched_rr = atoi(optarg);
break;
case 'y':
print_deprecated(option);
tx_paths = comma_delimited_to_vector(optarg);
tx_paths_set = true;
break;
case 'z':
print_deprecated(option);
rx_paths = comma_delimited_to_vector(optarg);
rx_paths_set = true;
break;
case 'C':
config_file = optarg;
break;
case 'V':
print_version(1);
exit(0);
break; break;
default: default:
goto bad_config; print_help();
exit(0);
} }
} }
if (argc > optind) {
LOG(ERROR) << "Unsupported positional arguments on command line";
goto bad_config;
}
/* Cmd line option specific validation & setup */
if (trx->cfg.num_chans > TRX_CHAN_MAX) {
LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX;
goto bad_config;
}
if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) ||
(rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) {
LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match";
goto bad_config;
}
for (i = 0; i < trx->cfg.num_chans; i++) {
trx->cfg.chans[i].trx = trx;
trx->cfg.chans[i].idx = i;
if (tx_paths_set)
osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str());
if (rx_paths_set)
osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str());
}
return;
bad_config:
print_help();
exit(0);
} }
int trx_validate_config(struct trx_ctx *trx) const char* commands[] {
{ "CMD POWEROFF",
if (trx->cfg.multi_arfcn && trx->cfg.num_chans > TRX_MCHAN_MAX) { //"CMD RXTUNE 1782000",
LOG(ERROR) << "Unsupported number of channels"; "CMD RXTUNE 931400",
return -1; //"CMD TXTUNE 1877000",
} "CMD TXTUNE 931900",
"CMD SETTSC 7",
"CMD POWERON",
"CMD SETRXGAIN 30",
"CMD SETPOWER 0",
//"CMD SETSLOT 6 7",
//"CMD SETSLOT 7 13",
//"CMD NOHANDOVER 1 0",
"CMD SYNC",
"CMD GETBSIC",
};
/* Force 4 SPS for EDGE or multi-ARFCN configurations */ const char* commands2[] {
if ((trx->cfg.egprs || trx->cfg.multi_arfcn) && "CMD GETBSIC",
(trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) { };
LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
return -1;
}
return 0; RadioDevice *usrp;
} RadioInterface *radio = NULL;
Transceiver2 *trx = NULL;
static int set_sched_rr(unsigned int prio) void* tall_trx_ctx;
{ struct trx_ctx* g_trx_ctx;
struct sched_param param;
int rc;
memset(&param, 0, sizeof(param));
param.sched_priority = prio;
LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority
<< ". This setting is DEPRECATED, please use 'policy rr " << param.sched_priority
<< "' under the 'sched' VTY node instead.";
rc = sched_setscheduler(getpid(), SCHED_RR, &param);
if (rc != 0) {
LOG(ERROR) << "Config: Setting SCHED_RR failed";
return -1;
}
return 0;
}
static void print_simd_info(void) int trx_main(int argc, char *argv[])
{
#ifdef HAVE_SSE3
LOGP(DMAIN, LOGL_INFO, "SSE3 support compiled in");
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (__builtin_cpu_supports("sse3"))
LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
else
LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
#else
LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
#endif
#endif
#ifdef HAVE_SSE4_1
LOGP(DMAIN, LOGL_INFO, "SSE4.1 support compiled in");
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (__builtin_cpu_supports("sse4.1"))
LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
else
LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
#else
LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
#endif
#endif
#ifndef HAVE_ATOMIC_OPS
#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
LOG(NOTICE) << "Built without atomic operation support. Using Mutex, it may affect performance!";
#endif
}
static void print_config(struct trx_ctx *trx)
{
unsigned int i;
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl;
ost << " Device args............. " << charp2str(trx->cfg.dev_args) << std::endl;
ost << " TRX Base Port........... " << trx->cfg.base_port << std::endl;
ost << " TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl;
ost << " GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl;
ost << " Channels................ " << trx->cfg.num_chans << std::endl;
ost << " Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl;
ost << " Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl;
ost << " EDGE support............ " << trx->cfg.egprs << std::endl;
ost << " Extended RACH support... " << trx->cfg.ext_rach << std::endl;
ost << " Reference............... " << trx->cfg.clock_ref << std::endl;
ost << " Filler Burst Type....... " << get_value_string(filler_names, trx->cfg.filler) << std::endl;
ost << " Filler Burst TSC........ " << trx->cfg.rtsc << std::endl;
ost << " Filler Burst RACH Delay. " << trx->cfg.rach_delay << std::endl;
ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
ost << " LO freq. offset......... " << trx->cfg.offset << std::endl;
if (trx->cfg.freq_offset_khz != 0)
ost << " Tune freq. offset....... " << trx->cfg.freq_offset_khz << std::endl;
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << (trx->cfg.force_rssi_offset ? "" : " (relative)") << std::endl;
ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
ost << " Tx Antennas.............";
for (i = 0; i < trx->cfg.num_chans; i++) {
std::string p = charp2str(trx->cfg.chans[i].tx_path);
ost << " '" << ((p != "") ? p : "<default>") << "'";
}
ost << std::endl;
ost << " Rx Antennas.............";
for (i = 0; i < trx->cfg.num_chans; i++) {
std::string p = charp2str(trx->cfg.chans[i].rx_path);
ost << " '" << ((p != "") ? p : "<default>") << "'";
}
ost << std::endl;
LOG(INFO) << ost << std::endl;
}
static void trx_stop()
{
LOG(NOTICE) << "Shutting down transceiver..." << std::endl;
delete transceiver;
delete radio;
delete usrp;
}
static int trx_start(struct trx_ctx *trx)
{ {
int type, chans; int type, chans;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
/* Create the low level device object */ struct trx_config config;
if (trx->cfg.multi_arfcn)
iface = RadioDevice::MULTI_ARFCN;
/* Generate vector of rx/tx_path: */ std::cerr << "starting.." << std::endl;
for (i = 0; i < trx->cfg.num_chans; i++) {
rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path)); convolve_init();
tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path)); convert_init();
tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
msgb_talloc_ctx_init(tall_trx_ctx, 0);
g_vty_info.tall_ctx = tall_trx_ctx;
//setup_signal_handlers();
g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
osmo_init_logging2(tall_trx_ctx, &log_info);
log_enable_multithread();
//osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info);
logging_vty_add_cmds();
//ctrl_vty_init(tall_trx_ctx);
osmo_cpu_sched_vty_init(tall_trx_ctx);
trx_vty_init(g_trx_ctx);
osmo_talloc_vty_add_cmds();
//osmo_stats_vty_add_cmds();
sched_param sch_params;
sch_params.sched_priority = 19;
pthread_setschedparam(pthread_self(), SCHED_RR, &sch_params);
handle_options(argc, argv, &config);
//setup_signal_handlers();
/* Check database sanity */
if (!trx_setup_config(&config)) {
std::cerr << "Config: Database failure - exiting" << std::endl;
return EXIT_FAILURE;
} }
usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface, //gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
trx->cfg.num_chans, trx->cfg.offset,
tx_paths, rx_paths); srandom(time(NULL));
type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
/* Create the low level device object */
usrp = RadioDevice::make(config.sps, config.sps, RadioDevice::NORMAL, config.chans,
config.offset);
type = usrp->open(config.dev_args, config.extref, false);
if (type < 0) { if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl; LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown; goto shutdown;
} }
/* Setup the appropriate device interface */ /* Setup the appropriate device interface */
radio = makeRadioInterface(trx, usrp, type); radio = makeRadioInterface(&config, usrp, type);
if (!radio) if (!radio)
goto shutdown; goto shutdown;
/* Create the transceiver core */ /* Create the transceiver core */
if (makeTransceiver(trx, radio) < 0) trx = makeTransceiver(&config, radio);
if (!trx)
goto shutdown; goto shutdown;
chans = transceiver->numChans(); trx->start();
LOG(NOTICE) << "-- Transceiver active with "
chans = trx->numChans();
std::cout << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl; << chans << " channel(s)" << std::endl;
return 0;
#if 0
for(auto i: commands) {
int MAX_PACKET_LENGTH = 100;
char response[MAX_PACKET_LENGTH];
char buffer[MAX_PACKET_LENGTH];
memset(response, 0, sizeof(response));
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "%s", i);
trx->commandhandler(buffer, response, 0);
std::cerr << i << "\tr: " << response << " ##" << std::endl;
}
sleep(30);
for(auto i: commands2) {
int MAX_PACKET_LENGTH = 100;
char response[MAX_PACKET_LENGTH];
char buffer[MAX_PACKET_LENGTH];
memset(response, 0, sizeof(response));
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "%s", i);
trx->commandhandler(buffer, response, 0);
std::cerr << i << "\tr: " << response << " ##" << std::endl;
}
#endif
// while (!gshutdown)
// sleep(1);
shutdown: shutdown:
trx_stop(); // std::cout << "Shutting down transceiver..." << std::endl;
return -1;
}
int main(int argc, char *argv[]) // delete trx;
{ // delete radio;
int rc; // delete usrp;
tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
msgb_talloc_ctx_init(tall_trx_ctx, 0);
g_vty_info.tall_ctx = tall_trx_ctx;
setup_signal_handlers();
g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
convolve_init();
convert_init();
osmo_init_logging2(tall_trx_ctx, &log_info);
log_enable_multithread();
osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info);
logging_vty_add_cmds();
ctrl_vty_init(tall_trx_ctx);
osmo_cpu_sched_vty_init(tall_trx_ctx);
trx_vty_init(g_trx_ctx);
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();
handle_options(argc, argv, g_trx_ctx);
rate_ctr_init(tall_trx_ctx);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
exit(2);
}
rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
if (rc < 0)
exit(1);
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1);
}
/* Backward compatibility: Hack to have 1 channel allocated by default.
* Can be Dropped once we * get rid of "-c" cmdline param */
if (g_trx_ctx->cfg.num_chans == 0) {
g_trx_ctx->cfg.num_chans = 1;
g_trx_ctx->cfg.chans[0].trx = g_trx_ctx;
g_trx_ctx->cfg.chans[0].idx = 0;
LOG(ERROR) << "No explicit channel config found. Make sure you" \
" configure channels in VTY config. Using 1 channel as default," \
" but expect your config to break in the future.";
}
print_simd_info();
print_config(g_trx_ctx);
if (trx_validate_config(g_trx_ctx) < 0) {
LOG(ERROR) << "Config failure - exiting";
return EXIT_FAILURE;
}
if (g_trx_ctx->cfg.sched_rr) {
if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0)
return EXIT_FAILURE;
}
osmo_signal_register_handler(SS_MAIN, transc_sig_cb, NULL);
trx_rate_ctr_init(tall_trx_ctx, g_trx_ctx);
srandom(time(NULL));
if(trx_start(g_trx_ctx) < 0)
return EXIT_FAILURE;
while (!gshutdown)
osmo_select_main(0);
trx_stop();
osmo_fd_unregister(&signal_ofd);
osmo_fd_close(&signal_ofd);
osmo_signal_unregister_handler(SS_MAIN, transc_sig_cb, NULL);
return 0; return 0;
} }
extern "C" void init_external_transceiver(int argc, char **argv) {
trx_main(argc, argv);
}
extern "C" void stop_trx() {
std::cout << "Shutting down transceiver..." << std::endl;
delete trx;
delete radio;
delete usrp;
}

View File

@@ -30,6 +30,41 @@ void RadioClock::set(const GSM::Time& wTime)
updateSignal.signal(); updateSignal.signal();
} }
GSM::Time RadioClock::adjust(GSM::Time &wBase, GSM::Time &wOffset)
{
int tn_diff, fn_diff = 0;
/* Modulo TN adustment */
tn_diff = wBase.TN() + wOffset.TN();
if (tn_diff < 0) {
tn_diff += 8;
fn_diff--;
} else if (tn_diff >= 8) {
tn_diff -= 8;
fn_diff++;
}
/* Modulo FN adjustment */
fn_diff += wBase.FN() + wOffset.FN();
if (fn_diff < 0)
fn_diff += GSM::gHyperframe;
else if ((unsigned) fn_diff >= GSM::gHyperframe)
fn_diff = fn_diff - GSM::gHyperframe;
return GSM::Time(fn_diff, tn_diff);
}
void RadioClock::adjust(GSM::Time& wOffset)
{
mLock.lock();
mClock = adjust(mClock, wOffset);
updateSignal.signal();
mLock.unlock();
}
void RadioClock::incTN() void RadioClock::incTN()
{ {
ScopedLock lock(mLock); ScopedLock lock(mLock);

View File

@@ -28,7 +28,10 @@
class RadioClock { class RadioClock {
public: public:
static GSM::Time adjust(GSM::Time &base, GSM::Time &offset);
void set(const GSM::Time& wTime); void set(const GSM::Time& wTime);
void adjust(GSM::Time &wOffset);
void incTN(); void incTN();
GSM::Time get(); GSM::Time get();
void wait(); void wait();

View File

@@ -42,7 +42,8 @@ RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
: mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mReceiveFIFO(mChans), mDevice(wDevice), : mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mReceiveFIFO(mChans), mDevice(wDevice),
sendBuffer(mChans), recvBuffer(mChans), convertRecvBuffer(mChans), sendBuffer(mChans), recvBuffer(mChans), convertRecvBuffer(mChans),
convertSendBuffer(mChans), powerScaling(mChans), underrun(false), overrun(false), convertSendBuffer(mChans), powerScaling(mChans), underrun(false), overrun(false),
writeTimestamp(0), readTimestamp(0), receiveOffset(wReceiveOffset), mOn(false) writeTimestamp(0), readTimestamp(0), receiveOffset(wReceiveOffset), shiftOffset(0), shiftUpdate(false),
mOn(false)
{ {
mClock.set(wStartTime); mClock.set(wStartTime);
} }
@@ -157,11 +158,21 @@ bool RadioInterface::tuneTx(double freq, size_t chan)
return mDevice->setTxFreq(freq, chan); return mDevice->setTxFreq(freq, chan);
} }
void RadioInterface::adjustClock(GSM::Time &offset)
{
mClock.adjust(offset);
}
bool RadioInterface::tuneRx(double freq, size_t chan) bool RadioInterface::tuneRx(double freq, size_t chan)
{ {
return mDevice->setRxFreq(freq, chan); return mDevice->setRxFreq(freq, chan);
} }
bool RadioInterface::tuneRxOffset(double offset, size_t chan)
{
return mDevice->setRxOffset(offset, chan);
}
/** synchronization thread loop */ /** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface) void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{ {
@@ -248,7 +259,10 @@ int RadioInterface::driveReceiveRadio()
return -1; return -1;
GSM::Time rcvClock = mClock.get(); GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset); if (receiveOffset < 0)
rcvClock.incTN(-receiveOffset);
else
rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN(); unsigned tN = rcvClock.TN();
int recvSz = recvBuffer[0]->getAvailSamples(); int recvSz = recvBuffer[0]->getAvailSamples();
const int symbolsPerSlot = gSlotLen + 8; const int symbolsPerSlot = gSlotLen + 8;
@@ -301,6 +315,12 @@ bool RadioInterface::isUnderrun()
return retVal; return retVal;
} }
void RadioInterface::applyOffset(int offset)
{
shiftOffset += offset;
shiftUpdate = true;
}
VectorFIFO* RadioInterface::receiveFIFO(size_t chan) VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
{ {
if (chan >= mReceiveFIFO.size()) if (chan >= mReceiveFIFO.size())
@@ -333,7 +353,7 @@ int RadioInterface::pullBuffer()
numRecv = mDevice->readSamples(convertRecvBuffer, numRecv = mDevice->readSamples(convertRecvBuffer,
segmentLen, segmentLen,
&overrun, &overrun,
readTimestamp, readTimestamp + mSPSRx * shiftOffset,
&local_underrun); &local_underrun);
if ((size_t) numRecv != segmentLen) { if ((size_t) numRecv != segmentLen) {
@@ -368,11 +388,16 @@ bool RadioInterface::pushBuffer()
segmentLen * 2); segmentLen * 2);
} }
if (shiftUpdate) {
mDevice->updateAlignment(0);
shiftUpdate = false;
}
/* Send the all samples in the send buffer */ /* Send the all samples in the send buffer */
numSent = mDevice->writeSamples(convertSendBuffer, numSent = mDevice->writeSamples(convertSendBuffer,
segmentLen, segmentLen,
&local_underrun, &local_underrun,
writeTimestamp); writeTimestamp + mSPSTx * shiftOffset);
osmo_trx_sync_or_and_fetch(&underrun, local_underrun); osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
writeTimestamp += numSent; writeTimestamp += numSent;

View File

@@ -55,7 +55,8 @@ protected:
RadioClock mClock; ///< the basestation clock! RadioClock mClock; ///< the basestation clock!
int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots
int shiftOffset;
bool shiftUpdate;
bool mOn; ///< indicates radio is on bool mOn; ///< indicates radio is on
private: private:
@@ -92,6 +93,7 @@ public:
/** check for underrun, resets underrun value */ /** check for underrun, resets underrun value */
bool isUnderrun(); bool isUnderrun();
void applyOffset(int offset);
/** return the receive FIFO */ /** return the receive FIFO */
VectorFIFO* receiveFIFO(size_t chan = 0); VectorFIFO* receiveFIFO(size_t chan = 0);
@@ -99,12 +101,18 @@ public:
/** return the basestation clock */ /** return the basestation clock */
RadioClock* getClock(void) { return &mClock;}; RadioClock* getClock(void) { return &mClock;};
/** apply an offset to the main clock */
void adjustClock(GSM::Time &offset);
/** set transmit frequency */ /** set transmit frequency */
virtual bool tuneTx(double freq, size_t chan = 0); virtual bool tuneTx(double freq, size_t chan = 0);
/** set receive frequency */ /** set receive frequency */
virtual bool tuneRx(double freq, size_t chan = 0); virtual bool tuneRx(double freq, size_t chan = 0);
/** set frequency correction */
virtual bool tuneRxOffset(double offset, size_t chan = 0);
/** set receive gain */ /** set receive gain */
virtual double setRxGain(double dB, size_t chan = 0); virtual double setRxGain(double dB, size_t chan = 0);

View File

@@ -250,7 +250,7 @@ int RadioInterfaceMulti::pullBuffer()
num = mDevice->readSamples(convertRecvBuffer, num = mDevice->readSamples(convertRecvBuffer,
outerRecvBuffer->size(), outerRecvBuffer->size(),
&overrun, &overrun,
readTimestamp, readTimestamp + shiftOffset,
&local_underrun); &local_underrun);
if (num != channelizer->inputLen()) { if (num != channelizer->inputLen()) {
LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen(); LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();

View File

@@ -167,7 +167,7 @@ int RadioInterfaceResamp::pullBuffer()
num_recv = mDevice->readSamples(convertRecvBuffer, num_recv = mDevice->readSamples(convertRecvBuffer,
resamp_outchunk, resamp_outchunk,
&overrun, &overrun,
readTimestamp, readTimestamp + shiftOffset,
&local_underrun); &local_underrun);
if (num_recv != (int) resamp_outchunk) { if (num_recv != (int) resamp_outchunk) {
LOG(ALERT) << "Receive error " << num_recv; LOG(ALERT) << "Receive error " << num_recv;

View File

@@ -96,8 +96,10 @@ float avgVector::avg() const
bool avgVector::insert(float val) bool avgVector::insert(float val)
{ {
if (!size()) if (size() < max) {
return false; push_back(val);
return true;
}
if (itr >= this->size()) if (itr >= this->size())
itr = 0; itr = 0;
@@ -107,6 +109,16 @@ bool avgVector::insert(float val)
return true; return true;
} }
bool avgVector::full() const
{
return size() >= max;
}
void avgVector::reset()
{
resize(0);
}
GSM::Time VectorQueue::nextTime() const GSM::Time VectorQueue::nextTime() const
{ {
GSM::Time retVal; GSM::Time retVal;

View File

@@ -52,10 +52,13 @@ class avgVector : std::vector<float> {
public: public:
avgVector(size_t size = 0); avgVector(size_t size = 0);
bool insert(float val); bool insert(float val);
bool full() const;
float avg() const; float avg() const;
void reset();
private: private:
size_t itr; size_t itr;
size_t max;
}; };
class VectorFIFO : public InterthreadQueue<radioVector> { }; class VectorFIFO : public InterthreadQueue<radioVector> { };

View File

@@ -87,19 +87,18 @@ static Resampler *dnsampler = NULL;
* perform 16-byte memory alignment required by many SSE instructions. * perform 16-byte memory alignment required by many SSE instructions.
*/ */
struct CorrelationSequence { struct CorrelationSequence {
CorrelationSequence() : sequence(NULL), buffer(NULL), toa(0.0), history(nullptr) CorrelationSequence() : sequence(NULL), buffer(NULL), toa(0.0)
{ {
} }
~CorrelationSequence() ~CorrelationSequence()
{ {
delete sequence; delete sequence;
delete[] history;
} }
signalVector *sequence; signalVector *sequence;
void *buffer; void *buffer;
complex *history; void *history;
float toa; float toa;
complex gain; complex gain;
}; };
@@ -1492,7 +1491,7 @@ bool generateSCHSequence(int sps)
/* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */ /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
data = (complex *) convolve_h_alloc(seq1->size()); data = (complex *) convolve_h_alloc(seq1->size());
_seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free); _seq1 = new signalVector(data, 0, seq1->size());
_seq1->setAligned(true); _seq1->setAligned(true);
memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex)); memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex));

View File

@@ -336,7 +336,6 @@ AC_CONFIG_FILES([\
Transceiver52M/device/usrp1/Makefile \ Transceiver52M/device/usrp1/Makefile \
Transceiver52M/device/lms/Makefile \ Transceiver52M/device/lms/Makefile \
Transceiver52M/device/ipc/Makefile \ Transceiver52M/device/ipc/Makefile \
Transceiver52M/device/ipc2/Makefile \
Transceiver52M/device/bladerf/Makefile \ Transceiver52M/device/bladerf/Makefile \
tests/Makefile \ tests/Makefile \
tests/CommonLibs/Makefile \ tests/CommonLibs/Makefile \

View File

@@ -602,9 +602,32 @@ rsp_error:
static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
{ {
struct trx_instance *trx = ofd->data; struct trx_instance *trx = ofd->data;
struct trx_meas_set meas;
uint8_t buf[TRXD_BUF_SIZE]; uint8_t buf[TRXD_BUF_SIZE];
sbit_t bits[148];
int8_t rssi, tn;
int16_t toa256;
uint32_t fn;
ssize_t read_len; ssize_t read_len;
#ifdef IPCIF
struct trxd_from_trx* rcvd = trxif_from_trx_d();
if (!rcvd) {
LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", rcvd);
return rcvd;
}
tn = rcvd->ts;
fn = rcvd->fn;
rssi = -(int8_t) rcvd->rssi;
toa256 = (int16_t) rcvd->toa;
/* Copy and convert bits {254..0} to sbits {-127..127} */
//osmo_ubit2sbit(bits, rcvd->symbols, 148);
memcpy(bits, rcvd->symbols, 148);
free(rcvd);
#else
read_len = read(ofd->fd, buf, sizeof(buf)); read_len = read(ofd->fd, buf, sizeof(buf));
if (read_len <= 0) { if (read_len <= 0) {
LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len); LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
@@ -618,18 +641,7 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
read_len); read_len);
return -EINVAL; return -EINVAL;
} }
#endif
return trx_data_rx_handler(trx, buf);
}
int trx_data_rx_handler(struct trx_instance *trx, uint8_t *buf)
{
struct trx_meas_set meas;
sbit_t bits[148];
int8_t rssi, tn;
int16_t toa256;
uint32_t fn;
tn = buf[0]; tn = buf[0];
fn = osmo_load32be(buf + 1); fn = osmo_load32be(buf + 1);
rssi = -(int8_t)buf[5]; rssi = -(int8_t)buf[5];
@@ -669,8 +681,6 @@ int trx_data_rx_handler(struct trx_instance *trx, uint8_t *buf)
return 0; return 0;
} }
extern void tx_external_transceiver(uint8_t *burst) __attribute__((weak));
int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
uint8_t pwr, const ubit_t *bits) uint8_t pwr, const ubit_t *bits)
{ {
@@ -709,10 +719,7 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
memcpy(buf + 6, bits, 148); memcpy(buf + 6, bits, 148);
/* Send data to transceiver */ /* Send data to transceiver */
if (tx_external_transceiver) send(trx->trx_ofd_data.fd, buf, 154, 0);
tx_external_transceiver(buf);
else
send(trx->trx_ofd_data.fd, buf, 154, 0);
#endif #endif
return 0; return 0;

View File

@@ -84,4 +84,3 @@ int trx_if_cmd_measure(struct trx_instance *trx,
int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
uint8_t pwr, const ubit_t *bits); uint8_t pwr, const ubit_t *bits);
int trx_data_rx_handler(struct trx_instance *trx, uint8_t *buf);

View File

@@ -271,8 +271,9 @@ static void signal_handler(int signum)
} }
} }
extern void init_external_transceiver(struct trx_instance *trx, int argc, char **argv) __attribute__((weak)); extern void init_external_transceiver(int argc, char **argv);
extern void close_external_transceiver(int argc, char **argv) __attribute__((weak)); extern void stop_trx();
extern volatile bool gshutdown;
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@@ -371,14 +372,14 @@ int main(int argc, char **argv)
/* Initialize pseudo-random generator */ /* Initialize pseudo-random generator */
srand(time(NULL)); srand(time(NULL));
if (init_external_transceiver) init_external_transceiver(argc, argv);
init_external_transceiver(app_data.trx, argc, argv);
else while (!app_data.quit)
while (!app_data.quit) osmo_select_main(0);
osmo_select_main(0);
gshutdown = true;
stop_trx();
if (close_external_transceiver)
close_external_transceiver(argc, argv);
exit: exit:
/* Close active connections */ /* Close active connections */