mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-02 21:23:16 +00:00
Compare commits
1 Commits
fairwaves/
...
ttsou/sigg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b79895c999 |
@@ -1,51 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
project(osmo-trx C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
# Set the version information here
|
||||
set(MAJOR_VERSION 0)
|
||||
set(API_COMPAT 0)
|
||||
set(MINOR_VERSION 1)
|
||||
set(MAINT_VERSION git)
|
||||
|
||||
set(LIBVER "${MAJOR_VERSION}.${API_COMPAT}.${MINOR_VERSION}")
|
||||
|
||||
include_directories(CommonLibs)
|
||||
include_directories(GSM)
|
||||
|
||||
add_definitions(-Wall -g)
|
||||
|
||||
#set(BUILD_SHARED_LIBS ON)
|
||||
|
||||
CONFIGURE_FILE(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake_config.in.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/config.h
|
||||
@ONLY)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
option(TRANS_FULL_VERSION "Compile with all Multichannel/Resampler support" OFF)
|
||||
#option(SQLITE_CONFIG "Use config values from SQLite3 database" OFF)
|
||||
set(SQLITE_CONFIG ON)
|
||||
|
||||
if(TRANS_FULL_VERSION)
|
||||
find_package(FFTW)
|
||||
endif(TRANS_FULL_VERSION)
|
||||
|
||||
find_package(XTRX)
|
||||
|
||||
if(SQLITE_CONFIG)
|
||||
find_library(sqlite3 sqlite3)
|
||||
else(SQLITE_CONFIG)
|
||||
add_definitions(-DNO_SQLITE_CONFIG)
|
||||
endif(SQLITE_CONFIG)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
|
||||
add_subdirectory(CommonLibs)
|
||||
add_subdirectory(GSM)
|
||||
add_subdirectory(Transceiver52M)
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#
|
||||
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
# Copyright 2011, 2012 Range Networks, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING 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/>.
|
||||
#
|
||||
|
||||
set(EXTRA_DIST example.config README.common)
|
||||
|
||||
set(libcommon_files
|
||||
BitVector.cpp
|
||||
LinkedLists.cpp
|
||||
Sockets.cpp
|
||||
Threads.cpp
|
||||
Timeval.cpp
|
||||
Logger.cpp)
|
||||
|
||||
if(SQLITE_CONFIG)
|
||||
set(libcommon_files
|
||||
${libcommon_files}
|
||||
Configuration.cpp
|
||||
sqlite3util.cpp)
|
||||
endif(SQLITE_CONFIG)
|
||||
|
||||
add_library(common ${libcommon_files})
|
||||
|
||||
add_executable(InterthreadTest InterthreadTest.cpp)
|
||||
target_link_libraries(InterthreadTest common pthread)
|
||||
|
||||
add_executable(SocketsTest SocketsTest.cpp)
|
||||
target_link_libraries(SocketsTest common pthread)
|
||||
|
||||
add_executable(TimevalTest TimevalTest.cpp)
|
||||
target_link_libraries(TimevalTest common)
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ float ConfigurationRecord::floatNumber() const
|
||||
|
||||
ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdName, ConfigurationKeyMap wSchema)
|
||||
{
|
||||
gLogEarly(LOG_INFO, "opening configuration table from path %s", filename);
|
||||
// Connect to the database.
|
||||
int rc = sqlite3_open(filename,&mDB);
|
||||
// (pat) When I used malloc here, sqlite3 sporadically crashes.
|
||||
|
||||
@@ -223,18 +223,18 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
||||
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort)
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort, wSrcIP);
|
||||
open(wSrcPort);
|
||||
}
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
|
||||
const char *wDestIP, unsigned short wDestPort)
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort,
|
||||
const char * wDestIP, unsigned short wDestPort )
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort, wSrcIP);
|
||||
open(wSrcPort);
|
||||
destination(wDestPort, wDestIP);
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
|
||||
}
|
||||
|
||||
|
||||
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
|
||||
void UDPSocket::open(unsigned short localPort)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
||||
@@ -265,7 +265,7 @@ void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = inet_addr(wlocalIP);
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_port = htons(localPort);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
|
||||
@@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket {
|
||||
public:
|
||||
|
||||
/** Open a USP socket with an OS-assigned port and no default destination. */
|
||||
UDPSocket(const char *localIP, unsigned short localPort);
|
||||
UDPSocket( unsigned short localPort=0);
|
||||
|
||||
/** Given a full specification, open the socket and set the dest address. */
|
||||
UDPSocket(const char *localIP, unsigned short localPort,
|
||||
const char *remoteIP, unsigned short remotePort);
|
||||
UDPSocket( unsigned short localPort,
|
||||
const char * remoteIP, unsigned short remotePort);
|
||||
|
||||
/** Set the destination port. */
|
||||
void destination( unsigned short wDestPort, const char * wDestIP );
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
unsigned short port() const;
|
||||
|
||||
/** Open and bind the UDP socket to a local port. */
|
||||
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
|
||||
void open(unsigned short localPort=0);
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
||||
|
||||
@@ -37,7 +37,7 @@ static const int gNumToSend = 10;
|
||||
|
||||
void *testReaderIP(void *)
|
||||
{
|
||||
UDPSocket readSocket("127.0.0.1", 5934, "localhost", 5061);
|
||||
UDPSocket readSocket(5934, "localhost", 5061);
|
||||
readSocket.nonblocking();
|
||||
int rc = 0;
|
||||
while (rc<gNumToSend) {
|
||||
@@ -61,8 +61,7 @@ void *testReaderUnix(void *)
|
||||
readSocket.nonblocking();
|
||||
int rc = 0;
|
||||
while (rc<gNumToSend) {
|
||||
char buf[MAX_UDP_LENGTH+1];
|
||||
buf[MAX_UDP_LENGTH] = '\0';
|
||||
char buf[MAX_UDP_LENGTH];
|
||||
int count = readSocket.read(buf, MAX_UDP_LENGTH);
|
||||
if (count>0) {
|
||||
COUT("read: " << buf);
|
||||
@@ -83,7 +82,7 @@ int main(int argc, char * argv[] )
|
||||
Thread readerThreadUnix;
|
||||
readerThreadUnix.start(testReaderUnix,NULL);
|
||||
|
||||
UDPSocket socket1("127.0.0.1", 5061, "127.0.0.1", 5934);
|
||||
UDPSocket socket1(5061, "127.0.0.1",5934);
|
||||
UDDSocket socket1U("testSource","testDestination");
|
||||
|
||||
COUT("socket1: " << socket1.port());
|
||||
|
||||
@@ -118,8 +118,8 @@ template <class T> class Vector {
|
||||
/** Build an empty Vector of a given size. */
|
||||
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
||||
|
||||
/** Build a Vector by moving another. */
|
||||
Vector(Vector<T>&& other)
|
||||
/** Build a Vector by shifting the data block. */
|
||||
Vector(Vector<T>& other)
|
||||
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
||||
{ other.mData=NULL; }
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#
|
||||
# Copyright 2008, 2009 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 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/>.
|
||||
#
|
||||
|
||||
set(libGSM_files GSMCommon.cpp)
|
||||
add_library(GSM ${libGSM_files})
|
||||
|
||||
34
INSTALLATION
34
INSTALLATION
@@ -1 +1,33 @@
|
||||
This is special branch for XTRX support. It should be configured with cmake, autoconf isn't supported yet.
|
||||
Installation Requirements
|
||||
|
||||
|
||||
|
||||
OpenBTS compiles to a simple Unix binary and does not require special
|
||||
installation.
|
||||
|
||||
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
|
||||
running configure.
|
||||
|
||||
To run OpenBTS, the following should be installed:
|
||||
|
||||
Asterisk (http://www.asterisk.org), running SIP on port 5060.
|
||||
|
||||
libosip2 (http://www.gnu.org/software/osip/)
|
||||
|
||||
libortp (http://freshmeat.net/projects/ortp/)
|
||||
|
||||
libusrp (http://gnuradio.org).
|
||||
This is part of the GNURadio installation.
|
||||
It is the only part used by OpenBTS.
|
||||
|
||||
|
||||
OpenBTS logs to syslogd as facility LOG_LOCAL7. Please set your /etc/syslog.conf
|
||||
accordingly.
|
||||
|
||||
|
||||
For information on specific executables, see tests/README.tests and
|
||||
apps/README.apps.
|
||||
|
||||
See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more
|
||||
information.
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ EXTRA_DIST = \
|
||||
COPYING \
|
||||
README
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
dox: FORCE
|
||||
doxygen doxconfig
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
if(NOT TRANS_FULL_VERSION)
|
||||
add_definitions(-DNO_RESAMPLER)
|
||||
add_definitions(-DNO_MULTIARFCN)
|
||||
endif()
|
||||
|
||||
|
||||
add_subdirectory(x86)
|
||||
include_directories(common)
|
||||
include_directories(".")
|
||||
|
||||
set(COMMON_FILES
|
||||
radioInterface.cpp
|
||||
radioVector.cpp
|
||||
radioClock.cpp
|
||||
radioBuffer.cpp
|
||||
sigProcLib.cpp
|
||||
signalVector.cpp
|
||||
Transceiver.cpp)
|
||||
|
||||
set(libtransceiver_files
|
||||
Resampler.cpp
|
||||
${COMMON_FILES})
|
||||
|
||||
if(TRANS_FULL_VERSION)
|
||||
set(libtransceiver_files
|
||||
${libtransceiver_files}
|
||||
radioInterfaceResamp.cpp
|
||||
radioInterfaceMulti.cpp
|
||||
ChannelizerBase.cpp
|
||||
Channelizer.cpp
|
||||
Synthesis.cpp
|
||||
common/fft.c
|
||||
radioInterfaceDiversity.cpp)
|
||||
endif(TRANS_FULL_VERSION)
|
||||
|
||||
set(noinst_HEADERS
|
||||
Complex.h
|
||||
radioInterface.h
|
||||
radioVector.h
|
||||
radioClock.h
|
||||
radioDevice.h
|
||||
radioBuffer.h
|
||||
sigProcLib.h
|
||||
signalVector.h
|
||||
Transceiver.h
|
||||
USRPDevice.h
|
||||
Resampler.h
|
||||
ChannelizerBase.h
|
||||
Channelizer.h
|
||||
Synthesis.h
|
||||
common/convolve.h
|
||||
common/convert.h
|
||||
common/scale.h
|
||||
common/mult.h
|
||||
common/fft.h)
|
||||
|
||||
add_library(transceiver ${libtransceiver_files})
|
||||
|
||||
|
||||
set(DEVICE XTRXDevice.cpp)
|
||||
set(DEVICE_LIBS ${XTRX_LIBRARIES})
|
||||
set(DEVICE_INC ${XTRX_INCLUDES})
|
||||
|
||||
include_directories(${DEVICE_INC})
|
||||
add_executable(osmo-trx osmo-trx.cpp ${DEVICE})
|
||||
target_link_libraries(osmo-trx transceiver arch GSM common ${sqlite3} ${FFTW_LIBRARIES} ${DEVICE_LIBS} pthread dl)
|
||||
|
||||
@@ -69,7 +69,7 @@ libtransceiver_la_SOURCES = \
|
||||
radioInterfaceResamp.cpp \
|
||||
radioInterfaceMulti.cpp
|
||||
|
||||
bin_PROGRAMS = osmo-trx
|
||||
bin_PROGRAMS = osmo-trx osmo-siggen
|
||||
|
||||
noinst_HEADERS = \
|
||||
Complex.h \
|
||||
@@ -99,10 +99,18 @@ osmo_trx_LDADD = \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE3_LIBS)
|
||||
|
||||
osmo_siggen_SOURCES = osmo-siggen.cpp
|
||||
osmo_siggen_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE3_LIBS)
|
||||
|
||||
if USRP1
|
||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||
osmo_trx_LDADD += $(USRP_LIBS)
|
||||
else
|
||||
libtransceiver_la_SOURCES += UHDDevice.cpp
|
||||
osmo_trx_LDADD += $(UHD_LIBS) $(FFTWF_LIBS)
|
||||
osmo_siggen_LDADD += $(UHD_LIBS) $(FFTWF_LIBS)
|
||||
endif
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Resampler.h"
|
||||
|
||||
@@ -36,8 +35,6 @@ extern "C" {
|
||||
|
||||
#define MAX_OUTPUT_LEN 4096
|
||||
|
||||
using namespace std;
|
||||
|
||||
static float sinc(float x)
|
||||
{
|
||||
if (x == 0.0)
|
||||
@@ -46,19 +43,32 @@ static float sinc(float x)
|
||||
return sin(M_PI * x) / (M_PI * x);
|
||||
}
|
||||
|
||||
void Resampler::initFilters(float bw)
|
||||
bool Resampler::initFilters(float bw)
|
||||
{
|
||||
float cutoff;
|
||||
size_t proto_len = p * filt_len;
|
||||
float *proto, val, cutoff;
|
||||
float sum = 0.0f, scale = 0.0f;
|
||||
float midpt = (float) (proto_len - 1.0) / 2.0;
|
||||
|
||||
/*
|
||||
* Allocate partition filters and the temporary prototype filter
|
||||
* according to numerator of the rational rate. Coefficients are
|
||||
* real only and must be 16-byte memory aligned for SSE usage.
|
||||
*/
|
||||
auto proto = vector<float>(p * filt_len);
|
||||
for (auto &part : partitions)
|
||||
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
|
||||
proto = new float[proto_len];
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
partitions = (float **) malloc(sizeof(float *) * p);
|
||||
if (!partitions) {
|
||||
delete[] proto;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < p; i++) {
|
||||
partitions[i] = (float *)
|
||||
memalign(16, filt_len * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the prototype filter with a Blackman-harris window.
|
||||
@@ -75,26 +85,47 @@ void Resampler::initFilters(float bw)
|
||||
else
|
||||
cutoff = (float) q;
|
||||
|
||||
float midpt = (proto.size() - 1) / 2.0;
|
||||
for (size_t i = 0; i < proto.size(); i++) {
|
||||
for (size_t i = 0; i < proto_len; i++) {
|
||||
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
||||
proto[i] *= a0 -
|
||||
a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
|
||||
a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
|
||||
a3 * cos(6 * M_PI * i / (proto.size() - 1));
|
||||
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
|
||||
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
|
||||
a3 * cos(6 * M_PI * i / (proto_len - 1));
|
||||
sum += proto[i];
|
||||
}
|
||||
scale = p / sum;
|
||||
|
||||
/* Populate filter partitions from the prototype filter */
|
||||
for (size_t i = 0; i < filt_len; i++) {
|
||||
for (size_t n = 0; n < p; n++)
|
||||
partitions[n][i] = complex<float>(proto[i * p + n] * scale);
|
||||
for (size_t n = 0; n < p; n++) {
|
||||
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
|
||||
partitions[n][2 * i + 1] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Store filter taps in reverse */
|
||||
for (auto &part : partitions)
|
||||
reverse(&part[0], &part[filt_len]);
|
||||
/* For convolution, we store the filter taps in reverse */
|
||||
for (size_t n = 0; n < p; n++) {
|
||||
for (size_t i = 0; i < filt_len / 2; i++) {
|
||||
val = partitions[n][2 * i];
|
||||
partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
|
||||
partitions[n][2 * (filt_len - 1 - i)] = val;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] proto;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resampler::releaseFilters()
|
||||
{
|
||||
if (partitions) {
|
||||
for (size_t i = 0; i < p; i++)
|
||||
free(partitions[i]);
|
||||
}
|
||||
|
||||
free(partitions);
|
||||
partitions = NULL;
|
||||
}
|
||||
|
||||
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||
@@ -128,6 +159,14 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resampler::computePath()
|
||||
{
|
||||
for (int i = 0; i < MAX_OUTPUT_LEN; i++) {
|
||||
in_index[i] = (q * i) / p;
|
||||
out_path[i] = (q * i) % p;
|
||||
}
|
||||
}
|
||||
|
||||
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
|
||||
{
|
||||
int n, path;
|
||||
@@ -141,8 +180,8 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
|
||||
path = out_path[i];
|
||||
|
||||
convolve_real(in, in_len,
|
||||
reinterpret_cast<float *>(partitions[path]),
|
||||
filt_len, &out[2 * i], out_len - i,
|
||||
partitions[path], filt_len,
|
||||
&out[2 * i], out_len - i,
|
||||
n, 1, 1, 0);
|
||||
}
|
||||
|
||||
@@ -151,18 +190,14 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
|
||||
|
||||
bool Resampler::init(float bw)
|
||||
{
|
||||
if (p == 0 || q == 0 || filt_len == 0) return false;
|
||||
|
||||
/* Filterbank filter internals */
|
||||
initFilters(bw);
|
||||
if (!initFilters(bw))
|
||||
return false;
|
||||
|
||||
/* Precompute filterbank paths */
|
||||
int i = 0;
|
||||
for (auto &index : in_index)
|
||||
index = (q * i++) / p;
|
||||
i = 0;
|
||||
for (auto &path : out_path)
|
||||
path = (q * i++) % p;
|
||||
in_index = new size_t[MAX_OUTPUT_LEN];
|
||||
out_path = new size_t[MAX_OUTPUT_LEN];
|
||||
computePath();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -173,7 +208,7 @@ size_t Resampler::len()
|
||||
}
|
||||
|
||||
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
|
||||
: in_index(NULL), out_path(NULL), partitions(NULL)
|
||||
{
|
||||
this->p = p;
|
||||
this->q = q;
|
||||
@@ -182,6 +217,8 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||
|
||||
Resampler::~Resampler()
|
||||
{
|
||||
for (auto &part : partitions)
|
||||
free(part);
|
||||
releaseFilters();
|
||||
|
||||
delete in_index;
|
||||
delete out_path;
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
#ifndef _RESAMPLER_H_
|
||||
#define _RESAMPLER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
|
||||
class Resampler {
|
||||
public:
|
||||
/* Constructor for rational sample rate conversion
|
||||
@@ -66,11 +63,14 @@ private:
|
||||
size_t p;
|
||||
size_t q;
|
||||
size_t filt_len;
|
||||
std::vector<size_t> in_index;
|
||||
std::vector<size_t> out_path;
|
||||
std::vector<std::complex<float> *> partitions;
|
||||
size_t *in_index;
|
||||
size_t *out_path;
|
||||
|
||||
void initFilters(float bw);
|
||||
float **partitions;
|
||||
|
||||
bool initFilters(float bw);
|
||||
void releaseFilters();
|
||||
void computePath();
|
||||
};
|
||||
|
||||
#endif /* _RESAMPLER_H_ */
|
||||
|
||||
@@ -112,17 +112,16 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un
|
||||
}
|
||||
|
||||
Transceiver::Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
const char *GSMcoreAddress,
|
||||
const char *wTRXAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
double wRssiOffset)
|
||||
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
|
||||
mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
|
||||
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
||||
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
||||
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||
rssiOffset(wRssiOffset),
|
||||
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
|
||||
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false),
|
||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
||||
mWriteBurstToDiskMask(0)
|
||||
{
|
||||
@@ -198,8 +197,8 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
|
||||
d_srcport = mBasePort + 2 * i + 2;
|
||||
d_dstport = mBasePort + 2 * i + 102;
|
||||
|
||||
mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
|
||||
mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
|
||||
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
|
||||
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
||||
}
|
||||
|
||||
/* Randomize the central clock */
|
||||
@@ -274,7 +273,7 @@ bool Transceiver::start()
|
||||
TxUpperLoopAdapter, (void*) chan);
|
||||
}
|
||||
|
||||
mForceClockInterface = true;
|
||||
writeClockInterface();
|
||||
mOn = true;
|
||||
return true;
|
||||
}
|
||||
@@ -298,10 +297,6 @@ void Transceiver::stop()
|
||||
LOG(NOTICE) << "Stopping the transceiver";
|
||||
mTxLowerLoopThread->cancel();
|
||||
mRxLowerLoopThread->cancel();
|
||||
mTxLowerLoopThread->join();
|
||||
mRxLowerLoopThread->join();
|
||||
delete mTxLowerLoopThread;
|
||||
delete mRxLowerLoopThread;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
mRxServiceLoopThreads[i]->cancel();
|
||||
@@ -320,6 +315,11 @@ void Transceiver::stop()
|
||||
mTxPriorityQueues[i].clear();
|
||||
}
|
||||
|
||||
mTxLowerLoopThread->join();
|
||||
mRxLowerLoopThread->join();
|
||||
delete mTxLowerLoopThread;
|
||||
delete mRxLowerLoopThread;
|
||||
|
||||
mOn = false;
|
||||
LOG(NOTICE) << "Transceiver stopped";
|
||||
}
|
||||
@@ -430,7 +430,7 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
|
||||
case V:
|
||||
state->fillerModulus[timeslot] = 51;
|
||||
break;
|
||||
//case V:
|
||||
//case V:
|
||||
case VII:
|
||||
state->fillerModulus[timeslot] = 102;
|
||||
break;
|
||||
@@ -545,7 +545,7 @@ void writeToFile(radioVector *radio_burst, size_t chan)
|
||||
|
||||
/*
|
||||
* Pull bursts from the FIFO and handle according to the slot
|
||||
* and burst correlation type. Equalzation is currently disabled.
|
||||
* and burst correlation type. Equalzation is currently disabled.
|
||||
*/
|
||||
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
||||
double &timingOffset, double &noise,
|
||||
@@ -656,7 +656,7 @@ void Transceiver::reset()
|
||||
mTxPriorityQueues[i].clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Transceiver::driveControl(size_t chan)
|
||||
{
|
||||
int MAX_PACKET_LENGTH = 100;
|
||||
@@ -678,6 +678,9 @@ void Transceiver::driveControl(size_t chan)
|
||||
|
||||
sscanf(buffer,"%3s %s",cmdcheck,command);
|
||||
|
||||
if (!chan)
|
||||
writeClockInterface();
|
||||
|
||||
if (strcmp(cmdcheck,"CMD")!=0) {
|
||||
LOG(WARNING) << "bogus message on control interface";
|
||||
return;
|
||||
@@ -802,7 +805,7 @@ void Transceiver::driveControl(size_t chan)
|
||||
LOG(WARNING) << "bogus message on control interface";
|
||||
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
||||
setModulus(timeslot, chan);
|
||||
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
||||
@@ -850,14 +853,14 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
|
||||
|
||||
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
||||
|
||||
|
||||
int RSSI = (int) buffer[5];
|
||||
BitVector newBurst(burstLen);
|
||||
BitVector::iterator itr = newBurst.begin();
|
||||
char *bufferItr = buffer+6;
|
||||
while (itr < newBurst.end())
|
||||
while (itr < newBurst.end())
|
||||
*itr++ = *bufferItr++;
|
||||
|
||||
|
||||
GSM::Time currTime = GSM::Time(frameNum,timeSlot);
|
||||
|
||||
addRadioVector(chan, newBurst, RSSI, currTime);
|
||||
@@ -871,9 +874,9 @@ void Transceiver::driveReceiveRadio()
|
||||
{
|
||||
if (!mRadioInterface->driveReceiveRadio()) {
|
||||
usleep(100000);
|
||||
} else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
|
||||
mForceClockInterface = false;
|
||||
writeClockInterface();
|
||||
} else {
|
||||
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
||||
writeClockInterface();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,7 +946,7 @@ void Transceiver::driveTxFIFO()
|
||||
{
|
||||
|
||||
/**
|
||||
Features a carefully controlled latency mechanism, to
|
||||
Features a carefully controlled latency mechanism, to
|
||||
assure that transmit packets arrive at the radio/USRP
|
||||
before they need to be transmitted.
|
||||
|
||||
@@ -954,7 +957,7 @@ void Transceiver::driveTxFIFO()
|
||||
|
||||
|
||||
RadioClock *radioClock = (mRadioInterface->getClock());
|
||||
|
||||
|
||||
if (mOn) {
|
||||
//radioClock->wait(); // wait until clock updates
|
||||
LOG(DEBUG) << "radio clock " << radioClock->get();
|
||||
|
||||
@@ -89,17 +89,15 @@ struct TransceiverState {
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
class Transceiver {
|
||||
public:
|
||||
/** Transceiver constructor
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX, as a string
|
||||
@param GSMcoreAddress IP address of the GSM core, as a string
|
||||
@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
|
||||
*/
|
||||
Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
const char *GSMcoreAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
@@ -154,8 +152,7 @@ public:
|
||||
|
||||
private:
|
||||
int mBasePort;
|
||||
std::string mLocalAddr;
|
||||
std::string mRemoteAddr;
|
||||
std::string mAddr;
|
||||
|
||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||
@@ -172,7 +169,7 @@ private:
|
||||
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
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
|
||||
@@ -211,7 +208,6 @@ private:
|
||||
|
||||
bool mEdge;
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
|
||||
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||
double mTxFreq; ///< the transmit frequency
|
||||
double mRxFreq; ///< the receive frequency
|
||||
@@ -279,3 +275,4 @@ void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** transmit queueing thread loop */
|
||||
void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ static const std::map<dev_key, dev_desc> dev_param_map {
|
||||
{ std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } },
|
||||
{ std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} },
|
||||
{ std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
|
||||
{ std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
|
||||
{ std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 16.5/GSMRATE, "STREAM/LimeSDR (4 SPS TX/RX)" } },
|
||||
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
|
||||
};
|
||||
|
||||
@@ -212,7 +212,7 @@ public:
|
||||
~uhd_device();
|
||||
|
||||
int open(const std::string &args, int ref, bool swap_channels);
|
||||
bool start();
|
||||
bool start(bool tx_only);
|
||||
bool stop();
|
||||
bool restart();
|
||||
void setPriority(float prio);
|
||||
@@ -224,6 +224,7 @@ public:
|
||||
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp, bool isControl);
|
||||
|
||||
void triggerGPIO(TIMESTAMP ts);
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
|
||||
bool setTxFreq(double wFreq, size_t chan);
|
||||
@@ -437,7 +438,7 @@ void uhd_device::set_rates()
|
||||
tx_rate = usrp_dev->get_tx_rate();
|
||||
rx_rate = usrp_dev->get_rx_rate();
|
||||
|
||||
ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
|
||||
ts_offset = (TIMESTAMP) desc.offset * rx_rate;
|
||||
LOG(INFO) << "Rates configured for " << desc.str;
|
||||
}
|
||||
|
||||
@@ -519,7 +520,6 @@ bool uhd_device::parse_dev_type()
|
||||
{ "B100", { B100, TX_WINDOW_USRP1 } },
|
||||
{ "B200", { B200, TX_WINDOW_USRP1 } },
|
||||
{ "B200mini", { B200, TX_WINDOW_USRP1 } },
|
||||
{ "B205mini", { B200, TX_WINDOW_USRP1 } },
|
||||
{ "B210", { B210, TX_WINDOW_USRP1 } },
|
||||
{ "E100", { E1XX, TX_WINDOW_FIXED } },
|
||||
{ "E110", { E1XX, TX_WINDOW_FIXED } },
|
||||
@@ -527,25 +527,25 @@ bool uhd_device::parse_dev_type()
|
||||
{ "E3XX", { E3XX, TX_WINDOW_FIXED } },
|
||||
{ "X300", { X3XX, TX_WINDOW_FIXED } },
|
||||
{ "X310", { X3XX, TX_WINDOW_FIXED } },
|
||||
{ "USRP2", { USRP2, TX_WINDOW_FIXED } },
|
||||
{ "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
|
||||
{ "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
|
||||
{ "STREAM", { LIMESDR, TX_WINDOW_USRP1 } },
|
||||
};
|
||||
|
||||
// Compare UHD motherboard and device strings */
|
||||
auto mapIter = devStringMap.begin();
|
||||
while (mapIter != devStringMap.end()) {
|
||||
if (devString.find(mapIter->first) != std::string::npos ||
|
||||
mboardString.find(mapIter->first) != std::string::npos) {
|
||||
dev_type = std::get<0>(mapIter->second);
|
||||
tx_window = std::get<1>(mapIter->second);
|
||||
return true;
|
||||
}
|
||||
mapIter++;
|
||||
std::string found;
|
||||
if (devStringMap.find(devString) != devStringMap.end())
|
||||
found = devString;
|
||||
else if (devStringMap.find(mboardString) != devStringMap.end())
|
||||
found = mboardString;
|
||||
|
||||
if (found.empty()) {
|
||||
LOG(ALERT) << "Unsupported device " << devString;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(ALERT) << "Unsupported device " << devString;
|
||||
return false;
|
||||
dev_type = devStringMap.at(found).first;
|
||||
tx_window = devStringMap.at(found).second;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -577,7 +577,7 @@ void uhd_device::set_channels(bool swap)
|
||||
chans = 1;
|
||||
}
|
||||
|
||||
if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
|
||||
if (chans > dev_param_map.at(dev_key(dev_type, 1, 1)).channels)
|
||||
throw std::invalid_argument("Device does not support number of requested channels");
|
||||
|
||||
std::string subdev_string;
|
||||
@@ -785,7 +785,7 @@ bool uhd_device::restart()
|
||||
return flush_recv(10);
|
||||
}
|
||||
|
||||
bool uhd_device::start()
|
||||
bool uhd_device::start(bool tx_only)
|
||||
{
|
||||
LOG(INFO) << "Starting USRP...";
|
||||
|
||||
@@ -803,12 +803,21 @@ bool uhd_device::start()
|
||||
async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
|
||||
|
||||
// Start streaming
|
||||
if (!restart())
|
||||
if (!tx_only && !restart())
|
||||
return false;
|
||||
|
||||
// Setup GPIO
|
||||
usrp_dev->set_gpio_attr("FP0", "CTRL", 0x00);
|
||||
usrp_dev->set_gpio_attr("FP0", "DDR", 0x01);
|
||||
|
||||
// Display usrp time
|
||||
double time_now = usrp_dev->get_time_now().get_real_secs();
|
||||
LOG(INFO) << "The current time is " << time_now << " seconds";
|
||||
auto now = usrp_dev->get_time_now();
|
||||
LOG(INFO) << "The current time is " << now.get_real_secs() << " seconds";
|
||||
|
||||
if (tx_only) {
|
||||
auto start = uhd::time_spec_t(now.get_real_secs() + 1.0);
|
||||
ts_initial = start.to_ticks(tx_rate);
|
||||
}
|
||||
|
||||
started = true;
|
||||
return true;
|
||||
@@ -973,6 +982,27 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
return len;
|
||||
}
|
||||
|
||||
#define GSM_FRAME_PERIOD (120e-3/26)
|
||||
#define GPIO_FRAME_ADVANCE 5
|
||||
#define GPIO_ON_PERIOD (GSM_FRAME_PERIOD / 2)
|
||||
|
||||
/*
|
||||
* Trigger GPIO a handful of frames ahead of the current sample timestamp.
|
||||
* This extends the number of GPIO triggers in the device side command
|
||||
* queue and prevents late packet and underrun errors on the RF sample path.
|
||||
*/
|
||||
void uhd_device::triggerGPIO(TIMESTAMP ticks)
|
||||
{
|
||||
auto ts = uhd::time_spec_t::from_ticks(ticks, tx_rate);
|
||||
auto adv = uhd::time_spec_t(GPIO_FRAME_ADVANCE * GSM_FRAME_PERIOD);
|
||||
auto per = uhd::time_spec_t(GPIO_ON_PERIOD);
|
||||
|
||||
usrp_dev->set_command_time(ts - adv);
|
||||
usrp_dev->set_gpio_attr("FP0", "OUT", 0x01);
|
||||
usrp_dev->set_command_time(ts - adv + per);
|
||||
usrp_dev->set_gpio_attr("FP0", "OUT", 0x00);
|
||||
}
|
||||
|
||||
int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
unsigned long long timestamp,bool isControl)
|
||||
{
|
||||
@@ -982,13 +1012,7 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||
metadata.end_of_burst = false;
|
||||
metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
|
||||
|
||||
*underrun = false;
|
||||
|
||||
// No control packets
|
||||
if (isControl) {
|
||||
LOG(ERR) << "Control packets not supported";
|
||||
return 0;
|
||||
}
|
||||
if (underrun) *underrun = false;
|
||||
|
||||
if (bufs.size() != chans) {
|
||||
LOG(ALERT) << "Invalid channel combination " << bufs.size();
|
||||
@@ -998,10 +1022,9 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||
// Drop a fixed number of packets (magic value)
|
||||
if (!aligned) {
|
||||
drop_cnt++;
|
||||
|
||||
if (drop_cnt == 1) {
|
||||
LOG(DEBUG) << "Aligning transmitter: stop burst";
|
||||
*underrun = true;
|
||||
if (underrun) *underrun = true;
|
||||
metadata.end_of_burst = true;
|
||||
} else if (drop_cnt < 30) {
|
||||
LOG(DEBUG) << "Aligning transmitter: packet advance";
|
||||
@@ -1015,7 +1038,7 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||
}
|
||||
|
||||
thread_enable_cancel(false);
|
||||
size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
||||
size_t num_smpls = tx_stream->send(bufs, len, metadata, 1.0);
|
||||
thread_enable_cancel(true);
|
||||
|
||||
if (num_smpls != (unsigned) len) {
|
||||
@@ -1070,7 +1093,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
||||
|
||||
/* Find center frequency between channels */
|
||||
rf_spread = fabs(freqs[!chan] - 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, 1, 1)).mcr) {
|
||||
LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
|
||||
return treq;
|
||||
}
|
||||
@@ -1186,7 +1209,7 @@ TIMESTAMP uhd_device::initialReadTimestamp()
|
||||
double uhd_device::fullScaleInputValue()
|
||||
{
|
||||
if (dev_type == LIMESDR)
|
||||
return (double) SHRT_MAX * LIMESDR_TX_AMPL;
|
||||
return (double) 2047 * LIMESDR_TX_AMPL;
|
||||
if (dev_type == UMTRX)
|
||||
return (double) SHRT_MAX * UMTRX_TX_AMPL;
|
||||
else
|
||||
@@ -1195,6 +1218,7 @@ double uhd_device::fullScaleInputValue()
|
||||
|
||||
double uhd_device::fullScaleOutputValue()
|
||||
{
|
||||
if (dev_type == LIMESDR) return (double) 2047;
|
||||
return (double) SHRT_MAX;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,397 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "Threads.h"
|
||||
#include "XTRXDevice.h"
|
||||
|
||||
#include <Logger.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double defaultRXBandwidth = 2e6;
|
||||
const double defaultTXBandwidth = 3e6;
|
||||
|
||||
static int time_tx_corr = 60; //20+20+20+20+20;
|
||||
|
||||
XTRXDevice::XTRXDevice(size_t txsps, size_t rxsps)
|
||||
{
|
||||
LOG(INFO) << "creating XTRX device...";
|
||||
|
||||
this->txsps = txsps;
|
||||
this->rxsps = rxsps;
|
||||
|
||||
rxGain = 0;
|
||||
|
||||
loopback = false;
|
||||
device = NULL;
|
||||
}
|
||||
|
||||
static int parse_config(const char* line, const char* argument, int default_value)
|
||||
{
|
||||
const char* arg_found = strstr(line, argument);
|
||||
if (!arg_found)
|
||||
return default_value;
|
||||
|
||||
const char* qe_pos = strchr(arg_found, '=');
|
||||
if (!qe_pos)
|
||||
return default_value;
|
||||
|
||||
int res = strtol(qe_pos + 1, NULL, 10);
|
||||
if (res == 0 && errno) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int XTRXDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||
{
|
||||
LOG(INFO) << "opening XTRX device '" << args << "'..";
|
||||
|
||||
int loglevel = parse_config(args.c_str(), "loglevel", 3);
|
||||
int lb_param = parse_config(args.c_str(), "loopback", 0);
|
||||
time_tx_corr = parse_config(args.c_str(), "tcorr", time_tx_corr);
|
||||
int fref = parse_config(args.c_str(), "refclk", 30720000);
|
||||
int rxdec = parse_config(args.c_str(), "rxdec", 0);
|
||||
|
||||
char xtrx_name[500];
|
||||
const char* lend = strchr(args.c_str(), ',');
|
||||
int len = (lend) ? (lend - args.c_str()) : sizeof(xtrx_name) - 1;
|
||||
strncpy(xtrx_name, args.c_str(), len);
|
||||
xtrx_name[len] = 0;
|
||||
|
||||
if (lb_param) {
|
||||
LOG(ALERT) << "XTRX LOOPBACK mode is set!";
|
||||
loopback = true;
|
||||
}
|
||||
|
||||
int res = xtrx_open(xtrx_name, loglevel, &device);
|
||||
if (res) {
|
||||
LOG(ALERT) << "XTRX creating failed, device " << xtrx_name << " code " << res;
|
||||
return -1;
|
||||
}
|
||||
double actualMasterClock = 0;
|
||||
|
||||
if (fref > 0) {
|
||||
xtrx_set_ref_clk(device, fref, XTRX_CLKSRC_INT);
|
||||
}
|
||||
|
||||
res = xtrx_set_samplerate(device,
|
||||
GSMRATE * (double) std::min(txsps, rxsps) * 32 * 4 * ((rxdec) ? 2 : 1),
|
||||
GSMRATE * (double) rxsps,
|
||||
GSMRATE * (double) txsps,
|
||||
(rxdec) ? XTRX_SAMPLERATE_FORCE_RX_DECIM : 0,
|
||||
&actualMasterClock,
|
||||
&actualRXSampleRate,
|
||||
&actualTXSampleRate);
|
||||
if (res) {
|
||||
LOG(ALERT) << "XTRX failed to set samplerate RX: " << GSMRATE * (double) rxsps
|
||||
<< " TX: " << GSMRATE * (double) txsps
|
||||
<< " res: " << res;
|
||||
return -1;
|
||||
} else {
|
||||
LOG(INFO) << "XTRX set samplerate Master: " << actualMasterClock
|
||||
<< " RX: " << actualRXSampleRate
|
||||
<< " TX: " << actualTXSampleRate;
|
||||
}
|
||||
|
||||
|
||||
int i;
|
||||
double bw;
|
||||
double actualbw;
|
||||
|
||||
actualbw = 0;
|
||||
bw = defaultRXBandwidth;
|
||||
for (i = 0, res = -1; res && (i < 4); i++, bw *= 1.5) {
|
||||
res = xtrx_tune_rx_bandwidth(device, XTRX_CH_AB, bw, &actualbw);
|
||||
}
|
||||
if (res) {
|
||||
LOG(ALERT) << "XTRX failed to set RX bandwidth: " << bw
|
||||
<< " res: " << res;
|
||||
return -1;
|
||||
} else {
|
||||
LOG(INFO) << "XTRX set RX bandwidth: " << actualbw;
|
||||
}
|
||||
|
||||
actualbw = 0;
|
||||
bw = defaultTXBandwidth;
|
||||
for (i = 0, res = -1; res && (i < 4); i++, bw *= 1.1) {
|
||||
res = xtrx_tune_tx_bandwidth(device, XTRX_CH_AB, bw, &actualbw);
|
||||
}
|
||||
if (res) {
|
||||
LOG(ALERT) << "XTRX failed to set TX bandwidth: " << bw
|
||||
<< " res: " << res;
|
||||
return -1;
|
||||
} else {
|
||||
LOG(INFO) << "XTRX set TX bandwidth: " << actualbw;
|
||||
}
|
||||
|
||||
samplesRead = 0;
|
||||
samplesWritten = 0;
|
||||
started = false;
|
||||
|
||||
return NORMAL;
|
||||
}
|
||||
|
||||
XTRXDevice::~XTRXDevice()
|
||||
{
|
||||
if (device) {
|
||||
xtrx_close(device);
|
||||
}
|
||||
}
|
||||
|
||||
bool XTRXDevice::start()
|
||||
{
|
||||
LOG(INFO) << "starting XTRX...";
|
||||
if (started) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dataStart = 0;
|
||||
dataEnd = 0;
|
||||
timeStart = 0;
|
||||
timeEnd = 0;
|
||||
timeRx = initialReadTimestamp();
|
||||
timestampOffset = 0;
|
||||
latestWriteTimestamp = 0;
|
||||
lastPktTimestamp = 0;
|
||||
hi32Timestamp = 0;
|
||||
isAligned = false;
|
||||
|
||||
//xtrx_stop(device, XTRX_TX);
|
||||
//xtrx_stop(device, XTRX_RX);
|
||||
|
||||
xtrx_set_antenna(device, XTRX_TX_L);
|
||||
xtrx_set_antenna(device, XTRX_RX_L);
|
||||
|
||||
xtrx_run_params_t params;
|
||||
params.dir = XTRX_TRX;
|
||||
params.nflags = (loopback) ? XTRX_RUN_DIGLOOPBACK : 0;
|
||||
|
||||
params.rx.chs = XTRX_CH_AB;
|
||||
params.rx.flags = XTRX_RSP_SISO_MODE;
|
||||
params.rx.hfmt = XTRX_IQ_INT16;
|
||||
params.rx.wfmt = XTRX_WF_16;
|
||||
params.rx.paketsize = 625 * rxsps;
|
||||
|
||||
params.tx.chs = XTRX_CH_AB;
|
||||
params.tx.flags = XTRX_RSP_SISO_MODE;
|
||||
params.tx.hfmt = XTRX_IQ_INT16;
|
||||
params.tx.wfmt = XTRX_WF_16;
|
||||
params.tx.paketsize = 625 * txsps;
|
||||
|
||||
if (loopback) {
|
||||
params.tx.flags |= XTRX_RSP_SWAP_AB | XTRX_RSP_SWAP_IQ;
|
||||
}
|
||||
|
||||
params.tx_repeat_buf = NULL;
|
||||
params.rx_stream_start = initialReadTimestamp();
|
||||
|
||||
int res = xtrx_run_ex(device, ¶ms);
|
||||
if (res) {
|
||||
LOG(ALERT) << "XTRX start failed res: " << res;
|
||||
} else {
|
||||
LOG(INFO) << "XTRX started";
|
||||
started = true;
|
||||
}
|
||||
return started;
|
||||
}
|
||||
|
||||
bool XTRXDevice::stop()
|
||||
{
|
||||
if (started) {
|
||||
int res = xtrx_stop(device, XTRX_TRX);
|
||||
if (res) {
|
||||
LOG(ALERT) << "XTRX stop failed res: " << res;
|
||||
} else {
|
||||
LOG(INFO) << "XTRX stopped";
|
||||
started = false;
|
||||
}
|
||||
}
|
||||
return !started;
|
||||
}
|
||||
|
||||
TIMESTAMP XTRXDevice::initialWriteTimestamp()
|
||||
{
|
||||
if (/*(iface == MULTI_ARFCN) || */(rxsps == txsps))
|
||||
return initialReadTimestamp();
|
||||
else
|
||||
return initialReadTimestamp() * txsps;
|
||||
}
|
||||
|
||||
double XTRXDevice::maxTxGain()
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
|
||||
double XTRXDevice::minTxGain()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
double XTRXDevice::maxRxGain()
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
|
||||
double XTRXDevice::minRxGain()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
double XTRXDevice::setTxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return 0.0;
|
||||
}
|
||||
double actual = 0;
|
||||
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||
|
||||
int res = xtrx_set_gain(device, XTRX_CH_AB, XTRX_TX_PAD_GAIN, -10, &actual);
|
||||
if (res) {
|
||||
LOG(ERR) << "Error setting TX gain res: " << res;
|
||||
}
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
|
||||
double XTRXDevice::setRxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return 0.0;
|
||||
}
|
||||
double actual = 0;
|
||||
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
|
||||
|
||||
int res = xtrx_set_gain(device, XTRX_CH_AB, XTRX_RX_LNA_GAIN, 25, &actual);
|
||||
if (res) {
|
||||
LOG(ERR) << "Error setting RX gain res: " << res;
|
||||
}
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
// NOTE: Assumes sequential reads
|
||||
int XTRXDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
||||
{
|
||||
if (!started)
|
||||
return -1;
|
||||
|
||||
if (RSSI) {
|
||||
*RSSI = 10; // TODO
|
||||
}
|
||||
|
||||
struct xtrx_recv_ex_info ri;
|
||||
ri.samples = len;
|
||||
ri.buffer_count = bufs.size();
|
||||
ri.buffers = (void* const*)&bufs[0];
|
||||
ri.flags = 0;
|
||||
|
||||
int res = xtrx_recv_sync_ex(device, &ri);
|
||||
if (res) {
|
||||
LOG(ALERT) << "xtrx_recv_sync failed res " << res << " current TS " << timeRx << " req TS" << timestamp;
|
||||
return -1;
|
||||
}
|
||||
timeRx += len;
|
||||
|
||||
// TODO: remove this
|
||||
int i;
|
||||
for (i = 0; i < len * 2; i++)
|
||||
bufs[0][i] <<= 4;
|
||||
|
||||
if (underrun) {
|
||||
*underrun = (ri.out_events & RCVEX_EVENT_FILLED_ZERO);
|
||||
}
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
int XTRXDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
bool *underrun, unsigned long long timestamp,
|
||||
bool isControl)
|
||||
{
|
||||
if (!started)
|
||||
return 0;
|
||||
|
||||
xtrx_send_ex_info_t nfo;
|
||||
nfo.buffers = (const void* const*)&bufs[0];
|
||||
nfo.buffer_count = bufs.size();
|
||||
nfo.flags = XTRX_TX_DONT_BUFFER;
|
||||
nfo.samples = len;
|
||||
nfo.ts = timestamp - time_tx_corr;
|
||||
|
||||
int res = xtrx_send_sync_ex(device, &nfo);
|
||||
if (res != 0) {
|
||||
LOG(ALERT) << "xtrx_send_sync_ex returned " << res << " len=" << len << " ts=" << timestamp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*underrun) {
|
||||
*underrun = (nfo.out_flags & XTRX_TX_DISCARDED_TO);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
bool XTRXDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
{
|
||||
LOG(ALERT) << "Update Aligment " << timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XTRXDevice::setTxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
int res;
|
||||
double actual = 0;
|
||||
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((res = xtrx_tune(device, XTRX_TUNE_TX_FDD, wFreq, &actual)) == 0) {
|
||||
LOG(INFO) << "set RX: " << wFreq << std::endl
|
||||
<< " actual freq: " << actual << std::endl;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
LOG(ALERT) << "set RX: " << wFreq << "failed (code: " << res << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool XTRXDevice::setRxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
int res;
|
||||
double actual = 0;
|
||||
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((res = xtrx_tune(device, XTRX_TUNE_RX_FDD, wFreq, &actual)) == 0) {
|
||||
LOG(INFO) << "set RX: " << wFreq << std::endl
|
||||
<< " actual freq: " << actual << std::endl;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
LOG(ALERT) << "set RX: " << wFreq << "failed (code: " << res << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||
size_t chans, double offset)
|
||||
{
|
||||
return new XTRXDevice(tx_sps, rx_sps);
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
#ifndef _XTRX_DEVICE_H_
|
||||
#define _XTRX_DEVICE_H_
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "radioDevice.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "Threads.h"
|
||||
#include <xtrx_api.h>
|
||||
|
||||
class XTRXDevice: public RadioDevice {
|
||||
private:
|
||||
int txsps;
|
||||
int rxsps;
|
||||
double actualTXSampleRate; ///< the actual XTRX sampling rate
|
||||
double actualRXSampleRate; ///< the actual XTRX sampling rate
|
||||
//unsigned int decimRate; ///< the XTRX decimation rate
|
||||
//unsigned int interRate; ///< the XTRX decimation rate
|
||||
|
||||
unsigned long long samplesRead; ///< number of samples read from XTRX
|
||||
unsigned long long samplesWritten; ///< number of samples sent to XTRX
|
||||
|
||||
bool started; ///< flag indicates XTRX has started
|
||||
|
||||
short *data;
|
||||
unsigned long dataStart;
|
||||
unsigned long dataEnd;
|
||||
TIMESTAMP timeStart;
|
||||
TIMESTAMP timeEnd;
|
||||
|
||||
TIMESTAMP timeRx;
|
||||
bool isAligned;
|
||||
|
||||
Mutex writeLock;
|
||||
|
||||
short *currData; ///< internal data buffer when reading from XTRX
|
||||
TIMESTAMP currTimestamp; ///< timestamp of internal data buffer
|
||||
unsigned currLen; ///< size of internal data buffer
|
||||
|
||||
TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks
|
||||
TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command
|
||||
TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response
|
||||
|
||||
unsigned long hi32Timestamp;
|
||||
unsigned long lastPktTimestamp;
|
||||
|
||||
double rxGain;
|
||||
bool loopback;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
short loopbackBuffer[1000000];
|
||||
int loopbackBufferSize;
|
||||
double samplePeriod;
|
||||
|
||||
struct timeval startTime;
|
||||
struct timeval lastReadTime;
|
||||
bool firstRead;
|
||||
#endif
|
||||
|
||||
xtrx_dev* device;
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
XTRXDevice(size_t txsps, size_t rxsps);
|
||||
|
||||
~XTRXDevice();
|
||||
|
||||
/** Instantiate the XTRX */
|
||||
int open(const std::string &args, int ref, bool swap_channels);
|
||||
|
||||
/** Start the XTRX */
|
||||
bool start();
|
||||
|
||||
/** Stop the XTRX */
|
||||
bool stop();
|
||||
|
||||
/** Set priority not supported */
|
||||
void setPriority(float prio = 0.5) { }
|
||||
|
||||
enum TxWindowType getWindowType() { return TX_WINDOW_FIXED; }
|
||||
|
||||
/**
|
||||
Read samples from the XTRX.
|
||||
@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 XTRX does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL);
|
||||
/**
|
||||
Write samples to the XTRX.
|
||||
@param buf Contains the data to be written.
|
||||
@param len number of samples to write.
|
||||
@param underrun Set if XTRX 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.
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
|
||||
/** Set the transmitter frequency */
|
||||
bool setTxFreq(double wFreq, size_t chan = 0);
|
||||
|
||||
/** Set the receiver frequency */
|
||||
bool setRxFreq(double wFreq, size_t chan = 0);
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
TIMESTAMP initialWriteTimestamp(void);
|
||||
|
||||
/** Returns the starting read Timestamp*/
|
||||
TIMESTAMP initialReadTimestamp(void) { return 20000;}
|
||||
|
||||
/** returns the full-scale transmit amplitude **/
|
||||
double fullScaleInputValue() {return (double) 32767*0.7;}
|
||||
|
||||
/** returns the full-scale receive amplitude **/
|
||||
double fullScaleOutputValue() {return (double) 32767;}
|
||||
|
||||
/** sets the receive chan gain, returns the gain setting **/
|
||||
double setRxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** get the current receive gain */
|
||||
double getRxGain(size_t chan = 0) { return rxGain; }
|
||||
|
||||
/** return maximum Rx Gain **/
|
||||
double maxRxGain(void);
|
||||
|
||||
/** return minimum Rx Gain **/
|
||||
double minRxGain(void);
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
double setTxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** return maximum Tx Gain **/
|
||||
double maxTxGain(void);
|
||||
|
||||
/** return minimum Rx Gain **/
|
||||
double minTxGain(void);
|
||||
|
||||
/** Return internal status values */
|
||||
inline double getTxFreq(size_t chan = 0) { return 0; }
|
||||
inline double getRxFreq(size_t chan = 0) { return 0; }
|
||||
inline double getSampleRate() { return actualTXSampleRate; }
|
||||
inline double numberRead() { return samplesRead; }
|
||||
inline double numberWritten() { return samplesWritten; }
|
||||
|
||||
};
|
||||
|
||||
#endif // _XTRX_DEVICE_H_
|
||||
|
||||
490
Transceiver52M/osmo-siggen.cpp
Normal file
490
Transceiver52M/osmo-siggen.cpp
Normal file
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* GSM Signal Generator
|
||||
*
|
||||
* Copyright (C) 2017 Ettus Research LLC
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author: Tom Tsou <tom.tsou@ettus.com>
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <GSMCommon.h>
|
||||
#include <Logger.h>
|
||||
#include <Configuration.h>
|
||||
#include <GSMCommon.h>
|
||||
#include "sigProcLib.h"
|
||||
#include "radioDevice.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convolve.h"
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
#define DEFAULT_TX_SPS 4
|
||||
#define DEFAULT_TX_AMPL 0.5
|
||||
#define DEFAULT_TX_GAIN 50
|
||||
#define DEFAULT_TX_FREQ 1e9
|
||||
#define DEFAULT_OFFSET 0.0
|
||||
|
||||
using namespace std;
|
||||
|
||||
enum GsmModType {
|
||||
MOD_LAURENT4,
|
||||
MOD_LAURENT2,
|
||||
MOD_LAURENT1,
|
||||
MOD_NCO,
|
||||
NUM_MODS,
|
||||
};
|
||||
|
||||
enum BurstType {
|
||||
BURST_NORMAL,
|
||||
BURST_ACCESS,
|
||||
BURST_FREQ,
|
||||
BURST_SYNC,
|
||||
BURST_EDGE,
|
||||
NUM_BURSTS,
|
||||
};
|
||||
|
||||
enum BurstTSC {
|
||||
TSC0, TSC1, TSC2, TSC3, TSC4, TSC5, TSC6, TSC7,
|
||||
};
|
||||
|
||||
struct Config {
|
||||
string args = "";
|
||||
string logl = "NOTICE";
|
||||
unsigned sps = DEFAULT_TX_SPS;
|
||||
double offset = DEFAULT_OFFSET;
|
||||
bool swap = false;
|
||||
float ampl = DEFAULT_TX_AMPL;
|
||||
double freq = DEFAULT_TX_FREQ;
|
||||
double gain = DEFAULT_TX_GAIN;
|
||||
BurstTSC tsc = TSC0;
|
||||
GsmModType mod = MOD_LAURENT2;
|
||||
BurstType burst = BURST_NORMAL;
|
||||
RadioDevice::ReferenceType ref = RadioDevice::REF_INTERNAL;
|
||||
};
|
||||
|
||||
static shared_ptr<signalVector> modulateGMSK(BitVector &bits, GsmModType modType)
|
||||
{
|
||||
switch (modType) {
|
||||
case MOD_LAURENT4: return shared_ptr<signalVector>(modulateBurstLaurent4(bits));
|
||||
case MOD_LAURENT2: return shared_ptr<signalVector>(modulateBurstLaurent2(bits));
|
||||
case MOD_LAURENT1: return shared_ptr<signalVector>(modulateBurstLaurent1(bits));
|
||||
case MOD_NCO: return shared_ptr<signalVector>(modulateBurstNCO(bits));
|
||||
default: return shared_ptr<signalVector>(modulateBurstLaurent2(bits));
|
||||
};
|
||||
}
|
||||
|
||||
static shared_ptr<signalVector> generateNormalBurst(BurstTSC tsc, GsmModType modType)
|
||||
{
|
||||
auto tail = vector<char>(3, 0);
|
||||
auto data0 = vector<char>(57);
|
||||
auto data1 = vector<char>(57);
|
||||
auto steal = vector<char>(1, 0);
|
||||
auto train = vector<char>(26);
|
||||
|
||||
auto ti = begin(GSM::gTrainingSequence[tsc]);
|
||||
for (auto &t : train) t = *ti++;
|
||||
for (auto &d : data0) d = rand() % 2;
|
||||
for (auto &d : data1) d = rand() % 2;
|
||||
|
||||
auto bits = BitVector(NORMAL_BURST_NBITS);
|
||||
auto bi = bits.begin();
|
||||
|
||||
for (auto t : tail) *bi++ = t;
|
||||
for (auto d : data0) *bi++ = d;
|
||||
for (auto s : steal) *bi++ = s;
|
||||
for (auto t : train) *bi++ = t;
|
||||
for (auto s : steal) *bi++ = s;
|
||||
for (auto d : data1) *bi++ = d;
|
||||
for (auto t : tail) *bi++ = t;
|
||||
|
||||
return modulateGMSK(bits, modType);
|
||||
}
|
||||
|
||||
static shared_ptr<signalVector> generateRABurst(GsmModType modType)
|
||||
{
|
||||
auto tail0 = vector<char>(8, 0);
|
||||
auto train = vector<char>(41);
|
||||
auto data = vector<char>(36);
|
||||
auto tail1 = vector<char>(3, 0);
|
||||
|
||||
auto ti = begin(GSM::gRACHBurst);
|
||||
for (auto &t : train) t = *ti++;
|
||||
for (auto &d : data) d = rand() % 2;
|
||||
|
||||
auto bits = BitVector(88);
|
||||
auto bi = bits.begin();
|
||||
|
||||
for (auto t : tail0) *bi++ = t;
|
||||
for (auto t : train) *bi++ = t;
|
||||
for (auto d : data) *bi++ = d;
|
||||
for (auto t : tail1) *bi++ = t;
|
||||
|
||||
return modulateGMSK(bits, modType);
|
||||
}
|
||||
|
||||
static shared_ptr<signalVector> generateFreqBurst(GsmModType modType)
|
||||
{
|
||||
auto tail = vector<char>(3, 0);
|
||||
auto fixed = vector<char>(142);
|
||||
|
||||
auto bits = BitVector(148);
|
||||
auto bi = bits.begin();
|
||||
|
||||
for (auto t : tail) *bi++ = t;
|
||||
for (auto f : fixed) *bi++ = f;
|
||||
for (auto t : tail) *bi++ = t;
|
||||
|
||||
return modulateGMSK(bits, modType);
|
||||
}
|
||||
|
||||
static shared_ptr<signalVector> generateSyncBurst(GsmModType modType)
|
||||
{
|
||||
auto tail = vector<char>(3, 0);
|
||||
auto data0 = vector<char>(39);
|
||||
auto data1 = vector<char>(39);
|
||||
|
||||
/* 64 length synchronization sequence */
|
||||
vector<char> train {
|
||||
1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
|
||||
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1,
|
||||
};
|
||||
for (auto &d : data0) d = rand() % 2;
|
||||
for (auto &d : data1) d = rand() % 2;
|
||||
|
||||
auto bits = BitVector(148);
|
||||
auto bi = bits.begin();
|
||||
|
||||
for (auto t : tail) *bi++ = t;
|
||||
for (auto d : data0) *bi++ = d;
|
||||
for (auto t : train) *bi++ = t;
|
||||
for (auto d : data1) *bi++ = d;
|
||||
for (auto t : tail) *bi++ = t;
|
||||
|
||||
return modulateGMSK(bits, modType);
|
||||
}
|
||||
|
||||
static shared_ptr<signalVector> generateEDGEBurst(BurstTSC tsc)
|
||||
{
|
||||
auto tail = vector<Complex<float>>(3);
|
||||
auto data0 = vector<Complex<float>>(58);
|
||||
auto train = vector<Complex<float>>(26);
|
||||
auto data1 = vector<Complex<float>>(58);
|
||||
|
||||
extern const Complex<float> psk8_table[8];
|
||||
for (auto &t : tail) t = psk8_table[0b111];
|
||||
for (auto &d : data0) d = psk8_table[rand() % 8];
|
||||
for (auto &d : data1) d = psk8_table[rand() % 8];
|
||||
|
||||
auto ti = begin(GSM::gEdgeTrainingSequence[tsc]);
|
||||
for (auto &t : train) {
|
||||
unsigned i = (*(ti + 0) & 0b001) << 0 |
|
||||
(*(ti + 1) & 0b001) << 1 |
|
||||
(*(ti + 2) & 0b001) << 2;
|
||||
t = psk8_table[i];
|
||||
ti += 3;
|
||||
}
|
||||
|
||||
/* NBITS refers to 148 symbols in this case */
|
||||
auto burst = signalVector(NORMAL_BURST_NBITS);
|
||||
auto bi = burst.begin();
|
||||
|
||||
for (auto t : tail) *bi++ = t;
|
||||
for (auto d : data0) *bi++ = d;
|
||||
for (auto t : train) *bi++ = t;
|
||||
for (auto d : data1) *bi++ = d;
|
||||
for (auto t : tail) *bi++ = t;
|
||||
|
||||
return shared_ptr<signalVector>(shapeEdgeBurst(burst));
|
||||
}
|
||||
|
||||
/* Perform float-integer conversion and write to the device */
|
||||
static void sendBurst(shared_ptr<RadioDevice> usrp, TIMESTAMP &ts,
|
||||
shared_ptr<signalVector> sv, float ampl)
|
||||
{
|
||||
auto buffer = vector<Complex<short>>(sv->size());
|
||||
|
||||
transform(sv->begin(), sv->end(), buffer.begin(), [ampl](Complex<float> x) {
|
||||
const float scale = SHRT_MAX * ampl;
|
||||
return Complex<short>(x.real()*scale, x.imag()*scale);
|
||||
});
|
||||
|
||||
auto buffers = vector<short *>(1, reinterpret_cast<short *>(&buffer.front()));
|
||||
ts += usrp->writeSamples(buffers, buffer.size(), nullptr, ts, true);
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
fprintf(stdout, "Options:\n"
|
||||
" -h, --help This text\n"
|
||||
" -a, --args UHD device args\n"
|
||||
" -l --log Logging level (%s)\n"
|
||||
" -b, --burst Burst type (%s)\n"
|
||||
" -r, --ref Frequency reference (%s)\n"
|
||||
" -f, --freq Tx RF frequency\n"
|
||||
" -g, --gain Tx RF gain\n"
|
||||
" -s, --sps Tx samples-per-symbol (only 4 supported)\n"
|
||||
" -m, --mod GSMK modulator type (%s)\n"
|
||||
" -p, --ampl Tx amplitude (0.0 - 1.0)\n"
|
||||
" -o, --offset Baseband frequency offset\n"
|
||||
" -t, --tsc Normal and EDGE burst training sequence (0-7)\n"
|
||||
" -S, --swap Swap channels\n\n",
|
||||
"'err', 'warn', 'notice', 'info', 'debug'",
|
||||
"'normal', 'access', 'freq', 'sync', 'edge'",
|
||||
"'internal', 'external', 'gps'",
|
||||
"'laurent4', 'laurent2', 'laurent1', 'nco'"
|
||||
);
|
||||
}
|
||||
|
||||
static void print_config(Config &config)
|
||||
{
|
||||
const map<GsmModType, string> modMap = {
|
||||
{ MOD_LAURENT4, "Laurent-4" },
|
||||
{ MOD_LAURENT2, "Laurent-2" },
|
||||
{ MOD_LAURENT1, "Laurent-1" },
|
||||
{ MOD_NCO, "NCO" },
|
||||
};
|
||||
|
||||
const map<BurstType, string> burstMap = {
|
||||
{ BURST_NORMAL, "Normal" },
|
||||
{ BURST_ACCESS, "Access" },
|
||||
{ BURST_FREQ, "Frequency" },
|
||||
{ BURST_SYNC, "Synchronization" },
|
||||
{ BURST_EDGE, "EDGE" },
|
||||
};
|
||||
|
||||
const map<RadioDevice::ReferenceType, string> refMap = {
|
||||
{ RadioDevice::REF_INTERNAL, "Internal" },
|
||||
{ RadioDevice::REF_EXTERNAL, "External" },
|
||||
{ RadioDevice::REF_GPS, "GPS" },
|
||||
};
|
||||
|
||||
auto yesno = [](bool x) { return x ? "yes" : "no"; };
|
||||
|
||||
ostringstream ost("");
|
||||
ost << "Config Settings" << endl;
|
||||
ost << " Log level............... " << config.logl << std::endl;
|
||||
ost << " Device args............. " << "\"" << config.args << "\"" << endl;
|
||||
ost << " Samples-per-Symbol...... " << config.sps << endl;
|
||||
ost << " RF frequency............ " << config.freq/1e9 << " GHz" << endl;
|
||||
ost << " RF gain................. " << config.gain << " dB" << endl;
|
||||
ost << " Reference............... " << refMap.at(config.ref) << endl;
|
||||
ost << " Burst type.............. " << burstMap.at(config.burst) << endl;
|
||||
ost << " Modulator type.......... " << modMap.at(config.mod) << endl;
|
||||
ost << " Baseband offset......... " << config.offset/1e6 << " MHz" << endl;
|
||||
ost << " Swap channels........... " << yesno(config.swap) << endl;
|
||||
cout << ost << endl;
|
||||
}
|
||||
|
||||
static bool handle_options(int argc, char **argv, Config &config)
|
||||
{
|
||||
int option;
|
||||
|
||||
const struct option longopts[] = {
|
||||
{ "help", 0, nullptr, 'h' },
|
||||
{ "log", 1, nullptr, 'l' },
|
||||
{ "args", 1, nullptr, 'a' },
|
||||
{ "ref" , 1, nullptr, 'r' },
|
||||
{ "freq", 1, nullptr, 'f' },
|
||||
{ "gain", 1, nullptr, 'g' },
|
||||
{ "mod", 1, nullptr, 'm' },
|
||||
{ "offset", 1, nullptr, 'o' },
|
||||
{ "sps", 1, nullptr, 's' },
|
||||
{ "ampl", 1, nullptr, 'p' },
|
||||
{ "tsc", 1, nullptr, 'r' },
|
||||
{ "burst", 1, nullptr, 'b' },
|
||||
{ "swap", 1, nullptr, 'w' },
|
||||
};
|
||||
|
||||
const map<string, string> logMap = {
|
||||
{ "emerg", "EMERG" },
|
||||
{ "EMERG", "EMERG" },
|
||||
{ "alert", "ALERT" },
|
||||
{ "ALERT", "ALERT" },
|
||||
{ "err", "ERR" },
|
||||
{ "ERR", "ERR" },
|
||||
{ "warn", "WARNING" },
|
||||
{ "WARN", "WARNING" },
|
||||
{ "notice", "NOTICE" },
|
||||
{ "NOTICE", "NOTICE" },
|
||||
{ "info", "INFO" },
|
||||
{ "INFO", "INFO" },
|
||||
{ "debug", "DEBUG" },
|
||||
{ "DEBUG", "DEBUG" },
|
||||
};
|
||||
|
||||
const map<string, GsmModType> modMap = {
|
||||
{ "laurent4", MOD_LAURENT4 },
|
||||
{ "laurent2", MOD_LAURENT2 },
|
||||
{ "laurent1", MOD_LAURENT1 },
|
||||
{ "nco", MOD_NCO },
|
||||
};
|
||||
|
||||
const map<string, BurstType> burstMap = {
|
||||
{ "normal", BURST_NORMAL },
|
||||
{ "access", BURST_ACCESS },
|
||||
{ "freq", BURST_FREQ },
|
||||
{ "sync", BURST_SYNC },
|
||||
{ "edge", BURST_EDGE },
|
||||
};
|
||||
|
||||
const map<string, RadioDevice::ReferenceType> refMap = {
|
||||
{ "internal", RadioDevice::REF_INTERNAL },
|
||||
{ "external", RadioDevice::REF_EXTERNAL },
|
||||
{ "gpsdo", RadioDevice::REF_GPS },
|
||||
{ "gps", RadioDevice::REF_GPS },
|
||||
};
|
||||
|
||||
while ((option = getopt_long(argc, argv, "ha:l:r:f:g:m:o:s:p:t:b:w", longopts, nullptr)) != -1) {
|
||||
switch (option) {
|
||||
case 'a':
|
||||
config.args = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
config.freq = atof(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
config.gain = atof(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
config.offset = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
if (atoi(optarg) != 4) {
|
||||
printf("Unsupported SPS = %i\n", atoi(optarg));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
config.ampl = atof(optarg);
|
||||
break;
|
||||
case 't':
|
||||
if (atoi(optarg) < TSC0 || atoi(optarg) > TSC7) {
|
||||
printf("Invalid training sequence %i", atoi(optarg));
|
||||
return false;
|
||||
}
|
||||
config.tsc = static_cast<BurstTSC>(atoi(optarg));
|
||||
break;
|
||||
case 'w':
|
||||
config.swap = true;
|
||||
break;
|
||||
case 'l':
|
||||
if (logMap.count(optarg) > 0) {
|
||||
config.logl = logMap.at(optarg);
|
||||
} else {
|
||||
printf("Invalid log parameter '%s'\n\n", optarg);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (refMap.count(optarg) > 0) {
|
||||
config.ref = refMap.at(optarg);
|
||||
} else {
|
||||
printf("Invalid reference parameter '%s'\n\n", optarg);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (modMap.count(optarg) > 0) {
|
||||
config.mod = modMap.at(optarg);
|
||||
} else {
|
||||
printf("Invalid modulation parameter '%s'\n\n", optarg);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
if (burstMap.count(optarg) > 0) {
|
||||
config.burst = burstMap.at(optarg);
|
||||
} else {
|
||||
printf("Invalid burst type parameter '%s'\n\n", optarg);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Config config;
|
||||
if (!handle_options(argc, argv, config)) {
|
||||
print_help();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
print_config(config);
|
||||
|
||||
gLogInit("osmo-siggen", config.logl.c_str(), LOG_LOCAL7);
|
||||
|
||||
convolve_init();
|
||||
convert_init();
|
||||
sigProcLibSetup();
|
||||
|
||||
/* Device setup */
|
||||
shared_ptr<RadioDevice> usrp(RadioDevice::make(config.sps, config.sps, RadioDevice::NORMAL, 1, config.offset));
|
||||
usrp->open(config.args, config.ref, config.swap);
|
||||
usrp->setTxFreq(config.freq);
|
||||
usrp->setTxGain(config.gain);
|
||||
usrp->start(true);
|
||||
usrp->setPriority(0.5);
|
||||
|
||||
/* Bind all burst-modulator configurations */
|
||||
auto makeBurstGenerator = [&config]()->function<shared_ptr<signalVector>()> {
|
||||
switch (config.burst) {
|
||||
case BURST_EDGE: return bind(generateEDGEBurst, config.tsc);
|
||||
case BURST_ACCESS: return bind(generateRABurst, config.mod);
|
||||
case BURST_FREQ: return bind(generateFreqBurst, config.mod);
|
||||
case BURST_SYNC: return bind(generateSyncBurst, config.mod);
|
||||
case BURST_NORMAL:
|
||||
default: return bind(generateNormalBurst, config.tsc, config.mod);
|
||||
}
|
||||
};
|
||||
|
||||
auto burstGenerator = makeBurstGenerator();
|
||||
auto ts = usrp->initialWriteTimestamp();
|
||||
auto frameTrigger = []() {
|
||||
static int tn = 0;
|
||||
return ++tn % 8 == 0;
|
||||
};
|
||||
|
||||
while (1) {
|
||||
try {
|
||||
if (frameTrigger()) usrp->triggerGPIO(ts);
|
||||
sendBurst(usrp, ts, burstGenerator(), config.ampl);
|
||||
} catch (const exception &e) {
|
||||
cout << e.what() << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sigProcLibDestroy();
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <GSMCommon.h>
|
||||
#include <Logger.h>
|
||||
@@ -62,8 +61,7 @@ extern "C" {
|
||||
|
||||
struct trx_config {
|
||||
std::string log_level;
|
||||
std::string local_addr;
|
||||
std::string remote_addr;
|
||||
std::string addr;
|
||||
std::string dev_args;
|
||||
unsigned port;
|
||||
unsigned tx_sps;
|
||||
@@ -79,7 +77,6 @@ struct trx_config {
|
||||
double rssi_offset;
|
||||
bool swap_channels;
|
||||
bool edge;
|
||||
int sched_rr;
|
||||
};
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
@@ -135,8 +132,7 @@ bool trx_setup_config(struct trx_config *config)
|
||||
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->local_addr << std::endl;
|
||||
ost << " GSM Core Address........." << config->remote_addr << std::endl;
|
||||
ost << " TRX Address............. " << config->addr << std::endl;
|
||||
ost << " Channels................ " << config->chans << std::endl;
|
||||
ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
|
||||
ost << " Rx Samples-per-Symbol... " << config->rx_sps << std::endl;
|
||||
@@ -169,19 +165,15 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
|
||||
radio = new RadioInterface(usrp, config->tx_sps,
|
||||
config->rx_sps, config->chans);
|
||||
break;
|
||||
#ifndef NO_RESAMPLER
|
||||
case RadioDevice::RESAMP_64M:
|
||||
case RadioDevice::RESAMP_100M:
|
||||
radio = new RadioInterfaceResamp(usrp, config->tx_sps,
|
||||
config->rx_sps);
|
||||
break;
|
||||
#endif
|
||||
#ifndef NO_MULTIARFCN
|
||||
case RadioDevice::MULTI_ARFCN:
|
||||
radio = new RadioInterfaceMulti(usrp, config->tx_sps,
|
||||
config->rx_sps, config->chans);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported radio interface configuration";
|
||||
return NULL;
|
||||
@@ -206,10 +198,9 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
|
||||
Transceiver *trx;
|
||||
VectorFIFO *fifo;
|
||||
|
||||
trx = new Transceiver(config->port, config->local_addr.c_str(),
|
||||
config->remote_addr.c_str(), config->tx_sps,
|
||||
config->rx_sps, config->chans, GSM::Time(3,0),
|
||||
radio, config->rssi_offset);
|
||||
trx = new Transceiver(config->port, config->addr.c_str(),
|
||||
config->tx_sps, config->rx_sps, config->chans,
|
||||
GSM::Time(3,0), radio, config->rssi_offset);
|
||||
if (!trx->init(config->filler, config->rtsc,
|
||||
config->rach_delay, config->edge)) {
|
||||
LOG(ALERT) << "Failed to initialize transceiver";
|
||||
@@ -255,7 +246,6 @@ static void print_help()
|
||||
" -a UHD device args\n"
|
||||
" -l Logging level (%s)\n"
|
||||
" -i IP address of GSM core\n"
|
||||
" -j IP address of osmo-trx\n"
|
||||
" -p Base port number\n"
|
||||
" -e Enable EDGE receiver\n"
|
||||
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
|
||||
@@ -269,8 +259,7 @@ static void print_help()
|
||||
" -r Random Normal Burst test mode with TSC\n"
|
||||
" -A Random Access Burst test mode with delay\n"
|
||||
" -R RSSI to dBm offset in dB (default=0)\n"
|
||||
" -S Swap channels (UmTRX only)\n"
|
||||
" -t SCHED_RR real-time priority (1..32)\n",
|
||||
" -S Swap channels (UmTRX only)\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||
}
|
||||
|
||||
@@ -279,8 +268,7 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
int option;
|
||||
|
||||
config->log_level = "NOTICE";
|
||||
config->local_addr = DEFAULT_TRX_IP;
|
||||
config->remote_addr = DEFAULT_TRX_IP;
|
||||
config->addr = DEFAULT_TRX_IP;
|
||||
config->port = DEFAULT_TRX_PORT;
|
||||
config->tx_sps = DEFAULT_TX_SPS;
|
||||
config->rx_sps = DEFAULT_RX_SPS;
|
||||
@@ -295,9 +283,8 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
config->rssi_offset = 0.0;
|
||||
config->swap_channels = false;
|
||||
config->edge = false;
|
||||
config->sched_rr = -1;
|
||||
|
||||
while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:")) != -1) {
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dmxgfo:s:b:r:A:R:Se")) != -1) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
@@ -310,10 +297,7 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
config->log_level = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
config->remote_addr = optarg;
|
||||
break;
|
||||
case 'j':
|
||||
config->local_addr = optarg;
|
||||
config->addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
config->port = atoi(optarg);
|
||||
@@ -359,9 +343,6 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
case 'e':
|
||||
config->edge = true;
|
||||
break;
|
||||
case 't':
|
||||
config->sched_rr = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
exit(0);
|
||||
@@ -405,21 +386,6 @@ bad_config:
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int set_sched_rr(int prio)
|
||||
{
|
||||
struct sched_param param;
|
||||
int rc;
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = prio;
|
||||
printf("Setting SCHED_RR priority(%d)\n", param.sched_priority);
|
||||
rc = sched_setscheduler(getpid(), SCHED_RR, ¶m);
|
||||
if (rc != 0) {
|
||||
std::cerr << "Config: Setting SCHED_RR failed" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int type, chans, ref;
|
||||
@@ -458,11 +424,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
handle_options(argc, argv, &config);
|
||||
|
||||
if (config.sched_rr != -1) {
|
||||
if (set_sched_rr(config.sched_rr) < 0)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
setup_signal_handlers();
|
||||
|
||||
/* Check database sanity */
|
||||
|
||||
@@ -58,7 +58,7 @@ class RadioDevice {
|
||||
virtual ~RadioDevice() { }
|
||||
|
||||
/** Start the USRP */
|
||||
virtual bool start()=0;
|
||||
virtual bool start(bool txOnly = false)=0;
|
||||
|
||||
/** Stop the USRP */
|
||||
virtual bool stop()=0;
|
||||
@@ -91,6 +91,7 @@ class RadioDevice {
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
virtual void triggerGPIO(TIMESTAMP timestamp) = 0;
|
||||
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp, bool isControl = false) = 0;
|
||||
|
||||
|
||||
@@ -46,10 +46,14 @@ using namespace GSM;
|
||||
#define CLIP_THRESH 30000.0f
|
||||
|
||||
/** Lookup tables for trigonometric approximation */
|
||||
static float sincTable[TABLESIZE+1]; // add 1 element for wrap around
|
||||
static float cosTable[TABLESIZE+1]; // add 1 element for wrap around
|
||||
static float sinTable[TABLESIZE+1];
|
||||
static float sincTable[TABLESIZE+1];
|
||||
|
||||
/** Constants */
|
||||
static const float M_PI_F = (float)M_PI;
|
||||
static const float M_2PI_F = (float)(2.0*M_PI);
|
||||
static const float M_1_2PI_F = 1/M_2PI_F;
|
||||
|
||||
/* Precomputed rotation vectors */
|
||||
static signalVector *GMSKRotation4 = NULL;
|
||||
@@ -60,7 +64,7 @@ static signalVector *GMSKReverseRotation1 = NULL;
|
||||
/* Precomputed fractional delay filters */
|
||||
static signalVector *delayFilters[DELAYFILTS];
|
||||
|
||||
static const Complex<float> psk8_table[8] = {
|
||||
extern const Complex<float> psk8_table[8] = {
|
||||
Complex<float>(-0.70710678, 0.70710678),
|
||||
Complex<float>( 0.0, -1.0),
|
||||
Complex<float>( 0.0, 1.0),
|
||||
@@ -106,8 +110,9 @@ struct CorrelationSequence {
|
||||
* for SSE instructions.
|
||||
*/
|
||||
struct PulseSequence {
|
||||
PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL),
|
||||
c0_buffer(NULL), c1_buffer(NULL), c0_inv_buffer(NULL)
|
||||
PulseSequence() : c0(NULL), c1(NULL), c2(NULL), c3(NULL), c0_inv(NULL), empty(NULL),
|
||||
c0_buffer(NULL), c1_buffer(NULL), c2_buffer(NULL),
|
||||
c3_buffer(NULL), c0_inv_buffer(NULL), g(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -115,19 +120,29 @@ struct PulseSequence {
|
||||
{
|
||||
delete c0;
|
||||
delete c1;
|
||||
delete c2;
|
||||
delete c3;
|
||||
delete c0_inv;
|
||||
delete empty;
|
||||
delete g;
|
||||
free(c0_buffer);
|
||||
free(c1_buffer);
|
||||
free(c2_buffer);
|
||||
free(c3_buffer);
|
||||
}
|
||||
|
||||
signalVector *c0;
|
||||
signalVector *c1;
|
||||
signalVector *c2;
|
||||
signalVector *c3;
|
||||
signalVector *c0_inv;
|
||||
signalVector *empty;
|
||||
void *c0_buffer;
|
||||
void *c1_buffer;
|
||||
void *c2_buffer;
|
||||
void *c3_buffer;
|
||||
void *c0_inv_buffer;
|
||||
signalVector *g;
|
||||
};
|
||||
|
||||
static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
|
||||
@@ -178,6 +193,29 @@ static float vectorNorm2(const signalVector &x)
|
||||
return Energy;
|
||||
}
|
||||
|
||||
/** compute e^(-jx) via lookup table. */
|
||||
static complex expjLookup(float x)
|
||||
{
|
||||
float arg = x*M_1_2PI_F;
|
||||
while (arg > 1.0F) arg -= 1.0F;
|
||||
while (arg < 0.0F) arg += 1.0F;
|
||||
|
||||
const float argT = arg*((float)TABLESIZE);
|
||||
const int argI = (int)argT;
|
||||
const float delta = argT-argI;
|
||||
const float iDelta = 1.0F-delta;
|
||||
return complex(iDelta*cosTable[argI] + delta*cosTable[argI+1],
|
||||
iDelta*sinTable[argI] + delta*sinTable[argI+1]);
|
||||
}
|
||||
|
||||
/** Library setup functions */
|
||||
static void initTrigTables() {
|
||||
for (int i = 0; i < TABLESIZE+1; i++) {
|
||||
cosTable[i] = cos(2.0*M_PI*i/TABLESIZE);
|
||||
sinTable[i] = sin(2.0*M_PI*i/TABLESIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize 4 sps and 1 sps rotation tables
|
||||
*/
|
||||
@@ -189,11 +227,11 @@ static void initGMSKRotationTables()
|
||||
GMSKReverseRotation4 = new signalVector(len4);
|
||||
signalVector::iterator rotPtr = GMSKRotation4->begin();
|
||||
signalVector::iterator revPtr = GMSKReverseRotation4->begin();
|
||||
auto phase = 0.0;
|
||||
float phase = 0.0;
|
||||
while (rotPtr != GMSKRotation4->end()) {
|
||||
*rotPtr++ = complex(cos(phase), sin(phase));
|
||||
*revPtr++ = complex(cos(-phase), sin(-phase));
|
||||
phase += M_PI / 2.0 / 4.0;
|
||||
*rotPtr++ = expjLookup(phase);
|
||||
*revPtr++ = expjLookup(-phase);
|
||||
phase += M_PI_F / 2.0F / 4.0;
|
||||
}
|
||||
|
||||
GMSKRotation1 = new signalVector(len1);
|
||||
@@ -202,9 +240,9 @@ static void initGMSKRotationTables()
|
||||
revPtr = GMSKReverseRotation1->begin();
|
||||
phase = 0.0;
|
||||
while (rotPtr != GMSKRotation1->end()) {
|
||||
*rotPtr++ = complex(cos(phase), sin(phase));
|
||||
*revPtr++ = complex(cos(-phase), sin(-phase));
|
||||
phase += M_PI / 2.0;
|
||||
*rotPtr++ = expjLookup(phase);
|
||||
*revPtr++ = expjLookup(-phase);
|
||||
phase += M_PI_F / 2.0F;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,6 +453,121 @@ static bool generateInvertC0Pulse(PulseSequence *pulse)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool generateGPulse(int sps, PulseSequence *pulse)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (!pulse)
|
||||
return false;
|
||||
|
||||
switch (sps) {
|
||||
case 4:
|
||||
len = 12;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
pulse->g = new signalVector(len);
|
||||
pulse->g->isReal(true);
|
||||
|
||||
/* Enable alignment for SSE usage */
|
||||
pulse->c3->setAligned(true);
|
||||
|
||||
signalVector::iterator xP = pulse->g->begin();
|
||||
|
||||
switch (sps) {
|
||||
case 4:
|
||||
*xP++ = 9.36941412e-03;
|
||||
*xP++ = 3.08922969e-02;
|
||||
*xP++ = 7.76167091e-02;
|
||||
*xP++ = 1.50953651e-01;
|
||||
*xP++ = 2.31509315e-01;
|
||||
*xP++ = 2.85056778e-01;
|
||||
*xP++ = 2.85056778e-01;
|
||||
*xP++ = 2.31509315e-01;
|
||||
*xP++ = 1.50953651e-01;
|
||||
*xP++ = 7.76167091e-02;
|
||||
*xP++ = 3.08922969e-02;
|
||||
*xP++ = 9.36941412e-03;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool generateC3Pulse(int sps, PulseSequence *pulse)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (!pulse)
|
||||
return false;
|
||||
|
||||
switch (sps) {
|
||||
case 4:
|
||||
len = 4;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
pulse->c3_buffer = convolve_h_alloc(len);
|
||||
pulse->c3 = new signalVector((complex *) pulse->c3_buffer, 0, len);
|
||||
pulse->c3->isReal(true);
|
||||
|
||||
/* Enable alignment for SSE usage */
|
||||
pulse->c3->setAligned(true);
|
||||
|
||||
signalVector::iterator xP = pulse->c3->begin();
|
||||
|
||||
switch (sps) {
|
||||
case 4:
|
||||
/* BT = 0.30 */
|
||||
*xP++ = 0.0;
|
||||
*xP++ = 9.66809925e-04;
|
||||
*xP++ = 1.14560468e-03;
|
||||
*xP++ = 5.28599308e-04;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool generateC2Pulse(int sps, PulseSequence *pulse)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (!pulse)
|
||||
return false;
|
||||
|
||||
switch (sps) {
|
||||
case 4:
|
||||
len = 4;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
pulse->c2_buffer = convolve_h_alloc(len);
|
||||
pulse->c2 = new signalVector((complex *) pulse->c2_buffer, 0, len);
|
||||
pulse->c2->isReal(true);
|
||||
|
||||
/* Enable alignment for SSE usage */
|
||||
pulse->c2->setAligned(true);
|
||||
|
||||
signalVector::iterator xP = pulse->c2->begin();
|
||||
|
||||
switch (sps) {
|
||||
case 4:
|
||||
/* BT = 0.30 */
|
||||
*xP++ = 0.0;
|
||||
*xP++ = 5.28599308e-04;
|
||||
*xP++ = 1.14560468e-03;
|
||||
*xP++ = 9.66809925e-04;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool generateC1Pulse(int sps, PulseSequence *pulse)
|
||||
{
|
||||
int len;
|
||||
@@ -513,6 +666,9 @@ static PulseSequence *generateGSMPulse(int sps)
|
||||
*xP++ = 2.84385729e-02;
|
||||
*xP++ = 4.46348606e-03;
|
||||
generateC1Pulse(sps, pulse);
|
||||
generateC2Pulse(sps, pulse);
|
||||
generateC3Pulse(sps, pulse);
|
||||
generateGPulse(sps, pulse);
|
||||
} else {
|
||||
center = (float) (len - 1.0) / 2.0;
|
||||
|
||||
@@ -558,7 +714,7 @@ static signalVector *rotateBurst(const BitVector &wBurst,
|
||||
int guardPeriodLength, int sps)
|
||||
{
|
||||
int burst_len;
|
||||
signalVector *pulse, rotated;
|
||||
signalVector *pulse, rotated, *shaped;
|
||||
signalVector::iterator itr;
|
||||
|
||||
pulse = GSMPulse1->empty;
|
||||
@@ -575,7 +731,11 @@ static signalVector *rotateBurst(const BitVector &wBurst,
|
||||
rotated.isReal(false);
|
||||
|
||||
/* Dummy filter operation */
|
||||
return convolve(&rotated, pulse, NULL, START_ONLY);
|
||||
shaped = convolve(&rotated, pulse, NULL, START_ONLY);
|
||||
if (!shaped)
|
||||
return NULL;
|
||||
|
||||
return shaped;
|
||||
}
|
||||
|
||||
static void rotateBurst2(signalVector &burst, double phase)
|
||||
@@ -586,32 +746,85 @@ static void rotateBurst2(signalVector &burst, double phase)
|
||||
burst[i] = burst[i] * rot;
|
||||
}
|
||||
|
||||
signalVector *modulateBurstNCO(const BitVector &bits)
|
||||
{
|
||||
auto sps = 4;
|
||||
auto burst = signalVector(625);
|
||||
auto it = burst.begin();
|
||||
burst.isReal(true);
|
||||
|
||||
/* Leading differential bit */
|
||||
*it = 2.0 * (bits[0] & 0x01) - 1.0;
|
||||
it += sps;
|
||||
|
||||
/* Main burst bits */
|
||||
for (size_t i = 1; i < bits.size(); i++) {
|
||||
*it = 2.0 * ((bits[i-1] & 0x01) ^ (bits[i] & 0x01)) - 1.0;
|
||||
it += sps;
|
||||
}
|
||||
|
||||
/* Trailing differential bit */
|
||||
*it = 2.0 * (bits[bits.size()-1] & 0x01) - 1.0;
|
||||
|
||||
auto shaped = convolve(&burst, GSMPulse4->g, NULL, START_ONLY);
|
||||
auto rotate = new signalVector(shaped->size());
|
||||
|
||||
auto itr = rotate->begin();
|
||||
auto its = shaped->begin();
|
||||
double accum = 0.0;
|
||||
while (itr != rotate->end()) {
|
||||
*itr++ = Complex<float>(cos(accum), sin(accum));
|
||||
accum -= its++->real();
|
||||
}
|
||||
|
||||
/*
|
||||
* Hack off guard interval at start and end
|
||||
* These values make E4406A happy with TSC detection
|
||||
*/
|
||||
itr = rotate->end() - 25;
|
||||
while (itr != rotate->end())
|
||||
*itr++ = Complex<float>(0, 0);
|
||||
|
||||
itr = rotate->begin();
|
||||
while (itr != rotate->begin() + 1)
|
||||
*itr++ = Complex<float>(0, 0);
|
||||
|
||||
delete shaped;
|
||||
return rotate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore the guard length argument in the GMSK modulator interface
|
||||
* because it results in 624/628 sized bursts instead of the preferred
|
||||
* burst length of 625. Only 4 SPS is supported.
|
||||
*/
|
||||
static signalVector *modulateBurstLaurent(const BitVector &bits)
|
||||
signalVector *modulateBurstLaurent4(const BitVector &bits)
|
||||
{
|
||||
int burst_len, sps = 4;
|
||||
float phase;
|
||||
signalVector *c0_pulse, *c1_pulse, *c0_shaped, *c1_shaped;
|
||||
signalVector::iterator c0_itr, c1_itr;
|
||||
signalVector *c0_pulse, *c1_pulse, *c2_pulse, *c3_pulse,
|
||||
*c0_shaped, *c1_shaped, *c2_shaped, *c3_shaped;
|
||||
signalVector::iterator c0_itr, c1_itr, c2_itr, c3_itr;
|
||||
|
||||
c0_pulse = GSMPulse4->c0;
|
||||
c1_pulse = GSMPulse4->c1;
|
||||
c2_pulse = GSMPulse4->c2;
|
||||
c3_pulse = GSMPulse4->c3;
|
||||
|
||||
if (bits.size() > 156)
|
||||
return NULL;
|
||||
|
||||
burst_len = 625;
|
||||
|
||||
signalVector c0_burst(burst_len, c0_pulse->size());
|
||||
c0_burst.isReal(true);
|
||||
c0_itr = c0_burst.begin();
|
||||
auto c0_burst = signalVector(burst_len, c0_pulse->size());
|
||||
auto c1_burst = signalVector(burst_len, c1_pulse->size());
|
||||
auto c2_burst = signalVector(burst_len, c2_pulse->size());
|
||||
auto c3_burst = signalVector(burst_len, c3_pulse->size());
|
||||
|
||||
signalVector c1_burst(burst_len, c1_pulse->size());
|
||||
c0_itr = c0_burst.begin();
|
||||
c1_itr = c1_burst.begin();
|
||||
c2_itr = c2_burst.begin();
|
||||
c3_itr = c3_burst.begin();
|
||||
|
||||
/* Padded differential tail bits */
|
||||
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
||||
@@ -630,9 +843,132 @@ static signalVector *modulateBurstLaurent(const BitVector &bits)
|
||||
GMSKRotate(c0_burst, sps);
|
||||
c0_burst.isReal(false);
|
||||
|
||||
/* Generate C1, C2, C3 phase coefficients */
|
||||
c0_itr = c0_burst.begin();
|
||||
c0_itr += sps * 2;
|
||||
c1_itr += sps * 2;
|
||||
c2_itr += sps * 2;
|
||||
c3_itr += sps * 2;
|
||||
|
||||
/* Bit 0 */
|
||||
auto p1 = bits[0] & 0x01;
|
||||
auto p2 = 0;
|
||||
auto p3 = p1 ^ p2;
|
||||
|
||||
*c1_itr = *c0_itr * Complex<float>(0, 2.0 * p1 - 1.0);
|
||||
*c2_itr = *c0_itr * Complex<float>(0, 2.0 * p2 - 1.0);
|
||||
*c3_itr = *c0_itr * Complex<float>(2.0 * p3 - 1.0, 0);
|
||||
|
||||
c0_itr += sps;
|
||||
c1_itr += sps;
|
||||
c2_itr += sps;
|
||||
c3_itr += sps;
|
||||
|
||||
/* Bit 1 */
|
||||
p1 = (bits[1] & 0x01) ^ (bits[0] & 0x01);
|
||||
p2 = (bits[0] & 0x01);
|
||||
p3 = p1 ^ p2;
|
||||
|
||||
*c1_itr = *c0_itr * Complex<float>(0, 2.0 * p1 - 1.0);
|
||||
*c2_itr = *c0_itr * Complex<float>(0, 2.0 * p2 - 1.0);
|
||||
*c3_itr = *c0_itr * Complex<float>(2.0 * p3 - 1.0, 0);
|
||||
|
||||
c0_itr += sps;
|
||||
c1_itr += sps;
|
||||
c2_itr += sps;
|
||||
c3_itr += sps;
|
||||
|
||||
/* Bit 2 - end */
|
||||
for (size_t i = 3; i < bits.size(); i++) {
|
||||
p1 = (bits[i-1] & 0x01) ^ (bits[i-2] & 0x01);
|
||||
p2 = (bits[i-2] & 0x01) ^ (bits[i-3] & 0x01);
|
||||
p3 = p1 ^ p2;
|
||||
|
||||
*c1_itr = *c0_itr * Complex<float>(0, 2.0 * p1 - 1.0);
|
||||
*c2_itr = *c0_itr * Complex<float>(0, 2.0 * p2 - 1.0);
|
||||
*c3_itr = *c0_itr * Complex<float>(2.0 * p3 - 1.0, 0);
|
||||
|
||||
c0_itr += sps;
|
||||
c1_itr += sps;
|
||||
c2_itr += sps;
|
||||
c3_itr += sps;
|
||||
}
|
||||
|
||||
/* Residual bits (unfinished) */
|
||||
int i = bits.size();
|
||||
phase = 2.0 * ((bits[i-1] & 0x01) ^ (bits[i-2] & 0x01)) - 1.0;
|
||||
*c1_itr = *c0_itr * Complex<float>(0, phase);
|
||||
|
||||
/* Pulse shape all component functions */
|
||||
c0_shaped = convolve(&c0_burst, c0_pulse, NULL, START_ONLY);
|
||||
c1_shaped = convolve(&c1_burst, c1_pulse, NULL, START_ONLY);
|
||||
c2_shaped = convolve(&c2_burst, c2_pulse, NULL, START_ONLY);
|
||||
c3_shaped = convolve(&c3_burst, c3_pulse, NULL, START_ONLY);
|
||||
|
||||
/* Combine shaped outputs into C0 */
|
||||
c0_itr = c0_shaped->begin();
|
||||
c1_itr = c1_shaped->begin();
|
||||
c2_itr = c2_shaped->begin();
|
||||
c3_itr = c3_shaped->begin();
|
||||
for (unsigned i = 0; i < c0_shaped->size(); i++ )
|
||||
*c0_itr++ += *c1_itr++ + *c2_itr++ + *c3_itr++;
|
||||
|
||||
delete c1_shaped;
|
||||
delete c2_shaped;
|
||||
delete c3_shaped;
|
||||
|
||||
return c0_shaped;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore the guard length argument in the GMSK modulator interface
|
||||
* because it results in 624/628 sized bursts instead of the preferred
|
||||
* burst length of 625. Only 4 SPS is supported.
|
||||
*/
|
||||
signalVector *modulateBurstLaurent2(const BitVector &bits)
|
||||
{
|
||||
int burst_len, sps = 4;
|
||||
float phase;
|
||||
signalVector *c0_pulse, *c1_pulse, *c0_burst;
|
||||
signalVector *c1_burst, *c0_shaped, *c1_shaped;
|
||||
signalVector::iterator c0_itr, c1_itr;
|
||||
|
||||
c0_pulse = GSMPulse4->c0;
|
||||
c1_pulse = GSMPulse4->c1;
|
||||
|
||||
if (bits.size() > 156)
|
||||
return NULL;
|
||||
|
||||
burst_len = 625;
|
||||
|
||||
c0_burst = new signalVector(burst_len, c0_pulse->size());
|
||||
c0_burst->isReal(true);
|
||||
c0_itr = c0_burst->begin();
|
||||
|
||||
c1_burst = new signalVector(burst_len, c1_pulse->size());
|
||||
c1_burst->isReal(true);
|
||||
c1_itr = c1_burst->begin();
|
||||
|
||||
/* Padded differential tail bits */
|
||||
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
||||
c0_itr += sps;
|
||||
|
||||
/* Main burst bits */
|
||||
for (unsigned i = 0; i < bits.size(); i++) {
|
||||
*c0_itr = 2.0 * (bits[i] & 0x01) - 1.0;
|
||||
c0_itr += sps;
|
||||
}
|
||||
|
||||
/* Padded differential tail bits */
|
||||
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
||||
|
||||
/* Generate C0 phase coefficients */
|
||||
GMSKRotate(*c0_burst, sps);
|
||||
c0_burst->isReal(false);
|
||||
|
||||
c0_itr = c0_burst->begin();
|
||||
c0_itr += sps * 2;
|
||||
c1_itr += sps * 2;
|
||||
|
||||
/* Start magic */
|
||||
phase = 2.0 * ((0x01 & 0x01) ^ (0x01 & 0x01)) - 1.0;
|
||||
@@ -655,8 +991,8 @@ static signalVector *modulateBurstLaurent(const BitVector &bits)
|
||||
*c1_itr = *c0_itr * Complex<float>(0, phase);
|
||||
|
||||
/* Primary (C0) and secondary (C1) pulse shaping */
|
||||
c0_shaped = convolve(&c0_burst, c0_pulse, NULL, START_ONLY);
|
||||
c1_shaped = convolve(&c1_burst, c1_pulse, NULL, START_ONLY);
|
||||
c0_shaped = convolve(c0_burst, c0_pulse, NULL, START_ONLY);
|
||||
c1_shaped = convolve(c1_burst, c1_pulse, NULL, START_ONLY);
|
||||
|
||||
/* Sum shaped outputs into C0 */
|
||||
c0_itr = c0_shaped->begin();
|
||||
@@ -664,10 +1000,48 @@ static signalVector *modulateBurstLaurent(const BitVector &bits)
|
||||
for (unsigned i = 0; i < c0_shaped->size(); i++ )
|
||||
*c0_itr++ += *c1_itr++;
|
||||
|
||||
delete c0_burst;
|
||||
delete c1_burst;
|
||||
delete c1_shaped;
|
||||
|
||||
return c0_shaped;
|
||||
}
|
||||
|
||||
signalVector *modulateBurstLaurent1(const BitVector &bits)
|
||||
{
|
||||
if (bits.size() > 156) return NULL;
|
||||
|
||||
int sps = 4;
|
||||
auto burst = signalVector(625, GSMPulse4->c0->size());
|
||||
auto itr = burst.begin();
|
||||
burst.isReal(true);
|
||||
|
||||
/* Padded differential tail bits */
|
||||
*itr = -1.0;
|
||||
itr += sps;
|
||||
|
||||
/* Main burst bits */
|
||||
for (unsigned i = 0; i < bits.size(); i++) {
|
||||
*itr = 2.0 * (bits[i] & 0x01) - 1.0;
|
||||
itr += sps;
|
||||
}
|
||||
|
||||
/* Padded differential tail bits */
|
||||
*itr = -1.0;
|
||||
|
||||
/* Generate C0 phase coefficients */
|
||||
GMSKRotate(burst, sps);
|
||||
burst.isReal(false);
|
||||
|
||||
/* Pulse shaping */
|
||||
return convolve(&burst, GSMPulse4->c0, NULL, START_ONLY);
|
||||
}
|
||||
|
||||
signalVector *modulateBurstLaurent(const BitVector &bits)
|
||||
{
|
||||
return modulateBurstLaurent2(bits);
|
||||
}
|
||||
|
||||
static signalVector *rotateEdgeBurst(const signalVector &symbols, int sps)
|
||||
{
|
||||
signalVector *burst;
|
||||
@@ -735,9 +1109,10 @@ static signalVector *mapEdgeSymbols(const BitVector &bits)
|
||||
* pulse filter combination of the GMSK Laurent represenation whereas 8-PSK
|
||||
* uses a single pulse linear filter.
|
||||
*/
|
||||
static signalVector *shapeEdgeBurst(const signalVector &symbols)
|
||||
signalVector *shapeEdgeBurst(const signalVector &symbols)
|
||||
{
|
||||
size_t nsyms, nsamps = 625, sps = 4;
|
||||
signalVector *burst, *shape;
|
||||
signalVector::iterator burst_itr;
|
||||
|
||||
nsyms = symbols.size();
|
||||
@@ -745,10 +1120,10 @@ static signalVector *shapeEdgeBurst(const signalVector &symbols)
|
||||
if (nsyms * sps > nsamps)
|
||||
nsyms = 156;
|
||||
|
||||
signalVector burst(nsamps, GSMPulse4->c0->size());
|
||||
burst = new signalVector(nsamps, GSMPulse4->c0->size());
|
||||
|
||||
/* Delay burst by 1 symbol */
|
||||
burst_itr = burst.begin() + sps;
|
||||
burst_itr = burst->begin() + sps;
|
||||
for (size_t i = 0; i < nsyms; i++) {
|
||||
float phase = i * 3.0f * M_PI / 8.0f;
|
||||
Complex<float> rot = Complex<float>(cos(phase), sin(phase));
|
||||
@@ -758,7 +1133,10 @@ static signalVector *shapeEdgeBurst(const signalVector &symbols)
|
||||
}
|
||||
|
||||
/* Single Gaussian pulse approximation shaping */
|
||||
return convolve(&burst, GSMPulse4->c0, NULL, START_ONLY);
|
||||
shape = convolve(burst, GSMPulse4->c0, NULL, START_ONLY);
|
||||
delete burst;
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -772,36 +1150,40 @@ signalVector *genRandNormalBurst(int tsc, int sps, int tn)
|
||||
return NULL;
|
||||
|
||||
int i = 0;
|
||||
BitVector bits(148);
|
||||
BitVector *bits = new BitVector(148);
|
||||
signalVector *burst;
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 3; i++)
|
||||
bits[i] = 0;
|
||||
(*bits)[i] = 0;
|
||||
|
||||
/* Random bits */
|
||||
for (; i < 60; i++)
|
||||
bits[i] = rand() % 2;
|
||||
(*bits)[i] = rand() % 2;
|
||||
|
||||
/* Stealing bit */
|
||||
bits[i++] = 0;
|
||||
(*bits)[i++] = 0;
|
||||
|
||||
/* Training sequence */
|
||||
for (int n = 0; i < 87; i++, n++)
|
||||
bits[i] = gTrainingSequence[tsc][n];
|
||||
(*bits)[i] = gTrainingSequence[tsc][n];
|
||||
|
||||
/* Stealing bit */
|
||||
bits[i++] = 0;
|
||||
(*bits)[i++] = 0;
|
||||
|
||||
/* Random bits */
|
||||
for (; i < 145; i++)
|
||||
bits[i] = rand() % 2;
|
||||
(*bits)[i] = rand() % 2;
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 148; i++)
|
||||
bits[i] = 0;
|
||||
(*bits)[i] = 0;
|
||||
|
||||
int guard = 8 + !(tn % 4);
|
||||
return modulateBurst(bits, guard, sps);
|
||||
burst = modulateBurst(*bits, guard, sps);
|
||||
delete bits;
|
||||
|
||||
return burst;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -817,26 +1199,30 @@ signalVector *genRandAccessBurst(int delay, int sps, int tn)
|
||||
return NULL;
|
||||
|
||||
int i = 0;
|
||||
BitVector bits(88 + delay);
|
||||
BitVector *bits = new BitVector(88+delay);
|
||||
signalVector *burst;
|
||||
|
||||
/* delay */
|
||||
for (; i < delay; i++)
|
||||
bits[i] = 0;
|
||||
(*bits)[i] = 0;
|
||||
|
||||
/* head and synch bits */
|
||||
for (int n = 0; i < 49+delay; i++, n++)
|
||||
bits[i] = gRACHBurst[n];
|
||||
(*bits)[i] = gRACHBurst[n];
|
||||
|
||||
/* Random bits */
|
||||
for (; i < 85+delay; i++)
|
||||
bits[i] = rand() % 2;
|
||||
(*bits)[i] = rand() % 2;
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 88+delay; i++)
|
||||
bits[i] = 0;
|
||||
(*bits)[i] = 0;
|
||||
|
||||
int guard = 68-delay + !(tn % 4);
|
||||
return modulateBurst(bits, guard, sps);
|
||||
burst = modulateBurst(*bits, guard, sps);
|
||||
delete bits;
|
||||
|
||||
return burst;
|
||||
}
|
||||
|
||||
signalVector *generateEmptyBurst(int sps, int tn)
|
||||
@@ -873,17 +1259,17 @@ signalVector *generateEdgeBurst(int tsc)
|
||||
if ((tsc < 0) || (tsc > 7))
|
||||
return NULL;
|
||||
|
||||
signalVector burst(148);
|
||||
signalVector *shape, *burst = new signalVector(148);
|
||||
const BitVector *midamble = &gEdgeTrainingSequence[tsc];
|
||||
|
||||
/* Tail */
|
||||
int n, i = 0;
|
||||
for (; i < tail; i++)
|
||||
burst[i] = psk8_table[7];
|
||||
(*burst)[i] = psk8_table[7];
|
||||
|
||||
/* Body */
|
||||
for (; i < tail + data; i++)
|
||||
burst[i] = psk8_table[rand() % 8];
|
||||
(*burst)[i] = psk8_table[rand() % 8];
|
||||
|
||||
/* TSC */
|
||||
for (n = 0; i < tail + data + train; i++, n++) {
|
||||
@@ -891,18 +1277,21 @@ signalVector *generateEdgeBurst(int tsc)
|
||||
(((unsigned) (*midamble)[3 * n + 1] & 0x01) << 1) |
|
||||
(((unsigned) (*midamble)[3 * n + 2] & 0x01) << 2);
|
||||
|
||||
burst[i] = psk8_table[index];
|
||||
(*burst)[i] = psk8_table[index];
|
||||
}
|
||||
|
||||
/* Body */
|
||||
for (; i < tail + data + train + data; i++)
|
||||
burst[i] = psk8_table[rand() % 8];
|
||||
(*burst)[i] = psk8_table[rand() % 8];
|
||||
|
||||
/* Tail */
|
||||
for (; i < tail + data + train + data + tail; i++)
|
||||
burst[i] = psk8_table[7];
|
||||
(*burst)[i] = psk8_table[7];
|
||||
|
||||
return shapeEdgeBurst(burst);
|
||||
shape = shapeEdgeBurst(*burst);
|
||||
delete burst;
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -938,7 +1327,7 @@ static signalVector *modulateBurstBasic(const BitVector &bits,
|
||||
int guard_len, int sps)
|
||||
{
|
||||
int burst_len;
|
||||
signalVector *pulse;
|
||||
signalVector *pulse, *burst, *shaped;
|
||||
signalVector::iterator burst_itr;
|
||||
|
||||
if (sps == 1)
|
||||
@@ -948,9 +1337,9 @@ static signalVector *modulateBurstBasic(const BitVector &bits,
|
||||
|
||||
burst_len = sps * (bits.size() + guard_len);
|
||||
|
||||
signalVector burst(burst_len, pulse->size());
|
||||
burst.isReal(true);
|
||||
burst_itr = burst.begin();
|
||||
burst = new signalVector(burst_len, pulse->size());
|
||||
burst->isReal(true);
|
||||
burst_itr = burst->begin();
|
||||
|
||||
/* Raw bits are not differentially encoded */
|
||||
for (unsigned i = 0; i < bits.size(); i++) {
|
||||
@@ -958,11 +1347,15 @@ static signalVector *modulateBurstBasic(const BitVector &bits,
|
||||
burst_itr += sps;
|
||||
}
|
||||
|
||||
GMSKRotate(burst, sps);
|
||||
burst.isReal(false);
|
||||
GMSKRotate(*burst, sps);
|
||||
burst->isReal(false);
|
||||
|
||||
/* Single Gaussian pulse approximation shaping */
|
||||
return convolve(&burst, pulse, NULL, START_ONLY);
|
||||
shaped = convolve(burst, pulse, NULL, START_ONLY);
|
||||
|
||||
delete burst;
|
||||
|
||||
return shaped;
|
||||
}
|
||||
|
||||
/* Assume input bits are not differentially encoded */
|
||||
@@ -979,10 +1372,16 @@ signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength,
|
||||
|
||||
static void generateSincTable()
|
||||
{
|
||||
float x;
|
||||
|
||||
for (int i = 0; i < TABLESIZE; i++) {
|
||||
auto x = (double) i / TABLESIZE * 8 * M_PI;
|
||||
auto y = sin(x) / x;
|
||||
sincTable[i] = std::isnan(y) ? 1.0 : y;
|
||||
x = (float) i / TABLESIZE * 8 * M_PI;
|
||||
if (fabs(x) < 0.01) {
|
||||
sincTable[i] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
sincTable[i] = sinf(x) / x;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1455,16 +1854,19 @@ float energyDetect(const signalVector &rxBurst, unsigned windowLength)
|
||||
|
||||
static signalVector *downsampleBurst(const signalVector &burst)
|
||||
{
|
||||
signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len());
|
||||
signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN);
|
||||
memcpy(in.begin(), burst.begin(), DOWNSAMPLE_IN_LEN * 2 * sizeof(float));
|
||||
signalVector *in, *out;
|
||||
|
||||
if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
|
||||
in = new signalVector(DOWNSAMPLE_IN_LEN, dnsampler->len());
|
||||
out = new signalVector(DOWNSAMPLE_OUT_LEN);
|
||||
memcpy(in->begin(), burst.begin(), DOWNSAMPLE_IN_LEN * 2 * sizeof(float));
|
||||
|
||||
if (dnsampler->rotate((float *) in->begin(), DOWNSAMPLE_IN_LEN,
|
||||
(float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
|
||||
delete out;
|
||||
out = NULL;
|
||||
}
|
||||
|
||||
delete in;
|
||||
return out;
|
||||
};
|
||||
|
||||
@@ -1557,6 +1959,7 @@ static int detectGeneralBurst(const signalVector &rxBurst,
|
||||
{
|
||||
int rc, start, len;
|
||||
bool clipping = false;
|
||||
signalVector *corr;
|
||||
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return -SIGERR_UNSUPPORTED;
|
||||
@@ -1572,10 +1975,12 @@ static int detectGeneralBurst(const signalVector &rxBurst,
|
||||
|
||||
start = target - head - 1;
|
||||
len = head + tail;
|
||||
signalVector corr(len);
|
||||
corr = new signalVector(len);
|
||||
|
||||
rc = detectBurst(rxBurst, corr, sync,
|
||||
rc = detectBurst(rxBurst, *corr, sync,
|
||||
thresh, sps, &, &toa, start, len);
|
||||
delete corr;
|
||||
|
||||
if (rc < 0) {
|
||||
return -SIGERR_INTERNAL;
|
||||
} else if (!rc) {
|
||||
@@ -1854,6 +2259,7 @@ SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp,
|
||||
|
||||
bool sigProcLibSetup()
|
||||
{
|
||||
initTrigTables();
|
||||
generateSincTable();
|
||||
initGMSKRotationTables();
|
||||
|
||||
|
||||
@@ -65,12 +65,18 @@ signalVector *modulateBurst(const BitVector &wBurst,
|
||||
int guardPeriodLength,
|
||||
int sps, bool emptyPulse = false);
|
||||
|
||||
signalVector *modulateBurstLaurent4(const BitVector &wBurst);
|
||||
signalVector *modulateBurstLaurent2(const BitVector &wBurst);
|
||||
signalVector *modulateBurstLaurent1(const BitVector &wBurst);
|
||||
signalVector *modulateBurstNCO(const BitVector &wBurst);
|
||||
|
||||
/** 8-PSK modulate a burst of bits */
|
||||
signalVector *modulateEdgeBurst(const BitVector &bits,
|
||||
int sps, bool emptyPulse = false);
|
||||
|
||||
/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
|
||||
signalVector *generateEdgeBurst(int tsc);
|
||||
signalVector *shapeEdgeBurst(const signalVector &symbols);
|
||||
|
||||
/** Generate an empty burst - 4 or 1 SPS */
|
||||
signalVector *generateEmptyBurst(int sps, int tn);
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
include_directories(../common)
|
||||
|
||||
set(libarch_files
|
||||
../common/convert_base.c
|
||||
../common/convolve_base.c
|
||||
convert.c
|
||||
convolve.c)
|
||||
|
||||
# TODO move to cmakedef
|
||||
add_definitions(-DHAVE___BUILTIN_CPU_SUPPORTS)
|
||||
|
||||
if(HAVE_SSE4_1)
|
||||
add_definitions(-DHAVE_SSE4_1)
|
||||
set(libarch_files ${libarch_files} convert_sse_4_1.c)
|
||||
endif(HAVE_SSE4_1)
|
||||
|
||||
if(HAVE_SSE3)
|
||||
add_definitions(-HAVE_SSE3)
|
||||
set(libarch_files ${libarch_files} convert_sse_3.c convert_sse_3.c)
|
||||
endif(HAVE_SSE3)
|
||||
|
||||
|
||||
add_library(arch ${libarch_files})
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
# - Find the FFTW library
|
||||
#
|
||||
# Usage:
|
||||
# find_package(FFTW [REQUIRED] [QUIET] )
|
||||
#
|
||||
# It sets the following variables:
|
||||
# FFTW_FOUND ... true if fftw is found on the system
|
||||
# FFTW_LIBRARIES ... full path to fftw library
|
||||
# FFTW_INCLUDES ... fftw include directory
|
||||
#
|
||||
# The following variables will be checked by the function
|
||||
# FFTW_USE_STATIC_LIBS ... if true, only static libraries are found
|
||||
# FFTW_ROOT ... if set, the libraries are exclusively searched
|
||||
# under this path
|
||||
# FFTW_LIBRARY ... fftw library to use
|
||||
# FFTW_INCLUDE_DIR ... fftw include directory
|
||||
#
|
||||
#If environment variable FFTWDIR is specified, it has same effect as FFTW_ROOT
|
||||
if( NOT FFTW_ROOT AND ENV{FFTWDIR} )
|
||||
set( FFTW_ROOT $ENV{FFTWDIR} )
|
||||
endif()
|
||||
# Check if we can use PkgConfig
|
||||
find_package(PkgConfig)
|
||||
#Determine from PKG
|
||||
if( PKG_CONFIG_FOUND AND NOT FFTW_ROOT )
|
||||
pkg_check_modules( PKG_FFTW QUIET "fftw3" )
|
||||
endif()
|
||||
#Check whether to search static or dynamic libs
|
||||
set( CMAKE_FIND_LIBRARY_SUFFIXES_SAV ${CMAKE_FIND_LIBRARY_SUFFIXES} )
|
||||
if( ${FFTW_USE_STATIC_LIBS} )
|
||||
set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} )
|
||||
else()
|
||||
set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX} )
|
||||
endif()
|
||||
if( FFTW_ROOT )
|
||||
#find libs
|
||||
find_library(
|
||||
FFTW_LIB
|
||||
NAMES "fftw3"
|
||||
PATHS ${FFTW_ROOT}
|
||||
PATH_SUFFIXES "lib" "lib64"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
find_library(
|
||||
FFTWF_LIB
|
||||
NAMES "fftw3f"
|
||||
PATHS ${FFTW_ROOT}
|
||||
PATH_SUFFIXES "lib" "lib64"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
find_library(
|
||||
FFTWL_LIB
|
||||
NAMES "fftw3l"
|
||||
PATHS ${FFTW_ROOT}
|
||||
PATH_SUFFIXES "lib" "lib64"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
#find includes
|
||||
find_path(
|
||||
FFTW_INCLUDES
|
||||
NAMES "fftw3.h"
|
||||
PATHS ${FFTW_ROOT}
|
||||
PATH_SUFFIXES "include"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
else()
|
||||
find_library(
|
||||
FFTW_LIB
|
||||
NAMES "fftw3"
|
||||
PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR}
|
||||
)
|
||||
find_library(
|
||||
FFTWF_LIB
|
||||
NAMES "fftw3f"
|
||||
PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR}
|
||||
)
|
||||
find_library(
|
||||
FFTWL_LIB
|
||||
NAMES "fftw3l"
|
||||
PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR}
|
||||
)
|
||||
find_path(
|
||||
FFTW_INCLUDES
|
||||
NAMES "fftw3.h"
|
||||
PATHS ${PKG_FFTW_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR}
|
||||
)
|
||||
endif( FFTW_ROOT )
|
||||
set(FFTW_LIBRARIES ${FFTW_LIB} ${FFTWF_LIB})
|
||||
if(FFTWL_LIB)
|
||||
set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTWL_LIB})
|
||||
endif()
|
||||
set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} )
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(FFTW DEFAULT_MSG
|
||||
FFTW_INCLUDES FFTW_LIBRARIES)
|
||||
mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES FFTW_LIB FFTWF_LIB FFTWL_LIB)
|
||||
@@ -1,69 +0,0 @@
|
||||
# - Find the XTRX library
|
||||
#
|
||||
# Usage:
|
||||
# find_package(XTRX [REQUIRED] [QUIET] )
|
||||
#
|
||||
# It sets the following variables:
|
||||
# XTRX_FOUND ... true if XTRX is found on the system
|
||||
# XTRX_LIBRARIES ... full path to XTRX library
|
||||
# XTRX_INCLUDES ... XTRX include directory
|
||||
#
|
||||
# The following variables will be checked by the function
|
||||
# XTRX_USE_STATIC_LIBS ... if true, only static libraries are found
|
||||
# XTRX_ROOT ... if set, the libraries are exclusively searched
|
||||
# under this path
|
||||
# XTRX_LIBRARY ... XTRX library to use
|
||||
# XTRX_INCLUDE_DIR ... XTRX include directory
|
||||
#
|
||||
#If environment variable XTRXDIR is specified, it has same effect as XTRX_ROOT
|
||||
if( NOT XTRX_ROOT AND ENV{XTRXDIR} )
|
||||
set( XTRX_ROOT $ENV{XTRXDIR} )
|
||||
endif()
|
||||
# Check if we can use PkgConfig
|
||||
find_package(PkgConfig)
|
||||
#Determine from PKG
|
||||
if( PKG_CONFIG_FOUND AND NOT XTRX_ROOT )
|
||||
pkg_check_modules( PKG_XTRX QUIET "libxtrx" )
|
||||
endif()
|
||||
#Check whether to search static or dynamic libs
|
||||
set( CMAKE_FIND_LIBRARY_SUFFIXES_SAV ${CMAKE_FIND_LIBRARY_SUFFIXES} )
|
||||
if( ${XTRX_USE_STATIC_LIBS} )
|
||||
set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} )
|
||||
else()
|
||||
set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX} )
|
||||
endif()
|
||||
if( XTRX_ROOT )
|
||||
#find libs
|
||||
find_library(
|
||||
XTRX_LIB
|
||||
NAMES "xtrx"
|
||||
PATHS ${XTRX_ROOT}
|
||||
PATH_SUFFIXES "lib" "lib64"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
#find includes
|
||||
find_path(
|
||||
XTRX_INCLUDES
|
||||
NAMES "xtrx_api.h"
|
||||
PATHS ${XTRX_ROOT}
|
||||
PATH_SUFFIXES "include"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
else()
|
||||
find_library(
|
||||
XTRX_LIB
|
||||
NAMES "xtrx"
|
||||
PATHS ${PKG_XTRX_LIBRARY_DIRS} ${LIB_INSTALL_DIR}
|
||||
)
|
||||
find_path(
|
||||
XTRX_INCLUDES
|
||||
NAMES "xtrx_api.h"
|
||||
PATHS ${PKG_XTRX_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR}
|
||||
)
|
||||
endif( XTRX_ROOT )
|
||||
set(XTRX_LIBRARIES ${XTRX_LIB})
|
||||
set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} )
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(XTRX DEFAULT_MSG
|
||||
XTRX_INCLUDES XTRX_LIBRARIES)
|
||||
mark_as_advanced(XTRX_INCLUDES XTRX_LIBRARIES XTRX_LIB XTRXF_LIB XTRXL_LIB)
|
||||
@@ -18,9 +18,7 @@ dnl You should have received a copy of the GNU General Public License
|
||||
dnl along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
dnl
|
||||
|
||||
AC_INIT([osmo-trx],
|
||||
m4_esyscmd([./git-version-gen .tarball-veresion]),
|
||||
[openbsc@lists.osmocom.org])
|
||||
AC_INIT(openbts,P2.8TRUNK)
|
||||
AC_PREREQ(2.57)
|
||||
AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
@@ -36,10 +34,6 @@ AM_INIT_AUTOMAKE([subdir-objects])
|
||||
dnl Linux kernel KBuild style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl include release helper
|
||||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
AM_PROG_AS
|
||||
AC_PROG_CXX
|
||||
AX_CXX_COMPILE_STDCXX_11
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
autoreconf --install --force
|
||||
./configure
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
167
debian/changelog
vendored
167
debian/changelog
vendored
@@ -1,170 +1,3 @@
|
||||
osmo-trx (0.2.0) unstable; urgency=medium
|
||||
|
||||
[ Alexander Chemeris ]
|
||||
* EDGE: Add support for UmTRX.
|
||||
* Common: Get rid of a compilation warning.
|
||||
* Common: Make sure gLogEarly() log to the same facilities as the normal log.
|
||||
* transceiver: Properly handle MAXDLY.
|
||||
* transceiver: Add an option to generate random Access Bursts.
|
||||
* osmo-trx: Output Rx SPS as a part of configuration output.
|
||||
* transceiver: Do not pass transceiver state struct to function where it's not used.
|
||||
* makefile: Fix build from an external path.
|
||||
* radioDevice: GSMRATE macro must have parentheses around its definition.
|
||||
* uhd: Fix comment.
|
||||
* radioInterface: Initialize power scale with a meaningful default.
|
||||
* transceiver: Log channel number in DEBUG output of demoded bursts.
|
||||
* transceiver: Add an option to emulate a RACH delay in random filler mode.
|
||||
* UHD: Initial LimeSDR support.
|
||||
* CommonLibs: Remove unused files.
|
||||
* sigProcLib: Typo sybols -> symbols
|
||||
* radioBuffer: Remove extra ; at the end of inline function definitions.
|
||||
* sigProcLib: Fix documentation, sync argument names in .cpp and .h files.
|
||||
* sigProcLib: make energyDetect() simpler by returning actual energy.
|
||||
* sigProcLib: Rename demodulateBurst() to demodGmskBurst() for clarity.
|
||||
* sigProcLib: Slice SoftVector instead of signalVector for GMSK demod.
|
||||
* Call vectorSlicer() right before packing bits for transmission to osmo-bts.
|
||||
* CommonLibs: Print soft bits with less confidence to console when printing a soft vector.
|
||||
* BitVector: Remove convolutional codec - we don't use it in osmo-trx.
|
||||
* BitVector: Convert SoftVector from 0..1 to -1..+1 soft bits.
|
||||
* signalVector: Implement segment().
|
||||
* vector: Introduce segmentMove() method to move data inside of a vector.
|
||||
* vector: Introduce shrink() function to shrink vector size without loosing data.
|
||||
* Move CorrType type from Transceiver to sigProcLib.
|
||||
* sigProcLib: rename signalError type to SignalError.
|
||||
* Move Transceiver::detectBurst() to sigProcLib to make it reusable.
|
||||
* Move BURST_THRESH from Transceiver.cpp to sigProcLib.h to make it reusable.
|
||||
* sigProcLib: Add operator<< to print CorrType to a string.
|
||||
* sigProcLib.h: Fix whitespaces. No non-whitespace changes.
|
||||
* Move Transceiver::demodulate() to sigProcLib to make it reusable.
|
||||
* sigProcLib: constify signalVector arguments for detectBurst() functions.
|
||||
* sigProcLib: Constify demodulation functions burst argument.
|
||||
* sigProcLib: Fix number of tail bits in random Normal Bursts and zero Stealing Bits.
|
||||
* Configuration: Variables allocated with 'new' must be freed with 'delete'.
|
||||
* BitVector: Remove Generator class.
|
||||
* PRBS: a Pseudo-random binary sequence (PRBS) generator class.
|
||||
|
||||
[ Tom Tsou ]
|
||||
* EDGE: Fix USRP B210 device support
|
||||
* uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts
|
||||
* EDGE: Fix demodulation slicer input
|
||||
* common: Restrict UDP binding to localhost only
|
||||
* common: Add mandatory length field to UDP receive calls
|
||||
* uhd: Update default E3XX settings
|
||||
* uhd: Set default Tx sampling to 4 sps
|
||||
* uhd: Make device offset check a private method
|
||||
* uhd: Set minimum UHD version requirement for E3XX
|
||||
* sigproc: Expand RACH, TSC, and EDGE correlation windows
|
||||
* transceiver: Do not report error on SETTSC when radio is on
|
||||
* transceiver: Add Rx samples-per-symbol option
|
||||
* radioInterface: Convert diversity argument to general type
|
||||
* iface: Add inner ring-buffer implementation
|
||||
* mcbts: Add multi-ARFCN channelizing filters
|
||||
* mcbts: Add multi-ARFCN radio support
|
||||
* sigproc: Adjust burst detection threshold criteria
|
||||
* egprs: Enable 8-PSK length vectors on the Tx interface
|
||||
* egprs: Enable 8-PSK burst detection when EDGE is enabled
|
||||
* transceiver: Remove HANDOVER warnings
|
||||
* mcbts: Allow out of order channel setup
|
||||
* radioInterface: Fix multi-channel buffer index bug
|
||||
* uhd: Add command line option for GPS reference
|
||||
* transceiver: Fix mixed GSMK / 8-PSK transmission
|
||||
* transceiver: Fix 4 SPS receive TOA value
|
||||
* sigproc: Fix missing 8-PSK tail symbols
|
||||
* uhd: Update USRP2/N200/N210 for 4 SPS Rx
|
||||
* sigproc: Match differential GMSK start/end bits to tail bits
|
||||
* uhd: Add missing B200 sample timing for 4 SPS receive
|
||||
* transceiver: Fix command build warning
|
||||
* uhd: Set minimum supported version to 3.9.0
|
||||
* uhd: Add X300 sample timing for 4 SPS
|
||||
* Revert "uhd: Set minimum supported version to 3.9.0"
|
||||
* uhd: Add support for UHD-3.11 logging control
|
||||
* uhd: Increase MC-BTS FPGA clock rate to 51.2 MHz
|
||||
* Resampler: Fix initialization return checking
|
||||
* sigProcLib: Remove unreachable code and no-effect checks
|
||||
* sigProcLib: Check return status on downsampling
|
||||
* sigProcLib: Fix negative value check on unsigned value
|
||||
* Resampler: Fix non-array delete for filter taps
|
||||
* Transceiver: Remove unsigned negative compares
|
||||
* Configuration: Fix const and signedness compile warnings
|
||||
* config: Remove OpenBTS style sqlite configuration
|
||||
* radioInterface: Remove UmTRX 'diversity' option
|
||||
* build: Require and check for gcc C++11 support
|
||||
* uhd: Use map container for for device parameter access
|
||||
* sigProcLib: Remove unused functions from public interface
|
||||
* uhd: Add non-UmTRX channel swap support
|
||||
* uhd: Fix Tx-RX timing offset setting
|
||||
* uhd: Fix USRP2/N200/N210 device detection
|
||||
* transceiver: Fix POWEROFF crash on USRP2/N200/X300 devices
|
||||
* sigProcLib: Fix complex/real vector flag in Laurent modulator
|
||||
* sigProcLib: Remove heap based signal vector allocations
|
||||
* common: Declare explicit Vector move constructor
|
||||
* sigProcLib: Remove trigonometric tables
|
||||
* sigProcLib: Use explicit NaN check in sinc table generation
|
||||
* sigProcLib: Replace dynamically allocated resampling buffers
|
||||
* sigProcLib: Specify standard namespace for isnan()
|
||||
* uhd: Always specify samples-per-symbol for device lookup
|
||||
* LimeSDR: set approximate tx offset value to make GSM work
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* add basic .gitignore
|
||||
* configure.ac: check for boost/config.hpp header
|
||||
* The INSTALL file is being overwritten by autoreconf, but it is committed as empty file. As a result, the INSTALL file always shows as modified. Instead, remove INSTALL from git and ignore it.
|
||||
* add contrib/jenkins.sh, for gerrit build bot
|
||||
|
||||
[ pierre.baudry ]
|
||||
* transceiver: Fix mismatched allocations and deallocations
|
||||
|
||||
[ Holger Hans Peter Freyther ]
|
||||
* debian: Require fftw3 header files for osmo-trx
|
||||
|
||||
[ Max ]
|
||||
* Add gerrit settings
|
||||
* Integrate Debian packaging changes
|
||||
* Remove embedded sqlite3
|
||||
* Fix building against sqlite3
|
||||
* Add autoconf-archive to dependencies
|
||||
* debian: remove obsolete dependency
|
||||
* deb: remove unused dependency
|
||||
* Remove redundant explicit dependency
|
||||
* Use release helper from libosmocore
|
||||
|
||||
[ Ruben Undheim ]
|
||||
* Do not embed sqlite3 when building
|
||||
|
||||
[ Philipp Maier ]
|
||||
* buildenv: Turn off native architecture builds
|
||||
* cosmetic: Make parameter lists uniform
|
||||
* Add test program to verify convolution implementation
|
||||
* ssedetect: Add runtime CPU detection
|
||||
* cosmetic: remove code duplication
|
||||
* buildenv: Make build CPU invariant
|
||||
* buildenv: Split up SSE3 and SSE4.1 code
|
||||
* cosmetic: Add info about SSE support
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* buildenv: correct the ax_sse macro description
|
||||
* buildenv: actually strip unused cpuid functionality
|
||||
* buildenv: fix build on systems without SIMD support
|
||||
* buildenv: cosmetic changes
|
||||
* buildenv: check for __builtin_cpu_supports call support
|
||||
* ssedetect: call __builtin_cpu_supports() only if supported
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: transciever: Remove trailing whitespaces
|
||||
* transceiver: Avoid sending clock indications when trx is not powered on
|
||||
* Add -j option to bind to specific address
|
||||
|
||||
[ ignasj ]
|
||||
* LimeSDR: Change device detection to work with USB and PCIe versions
|
||||
* LimeSDR: change tx window type to TX_WINDOW_FIXED
|
||||
* LimeSDR: Fix sample value range
|
||||
|
||||
[ Harald Welte ]
|
||||
* Add '-t' command line option to enable SCHED_RR
|
||||
* Import git-version-gen and update AC_INIT()
|
||||
|
||||
-- Harald Welte <laforge@gnumonks.org> Sat, 28 Oct 2017 17:52:32 +0200
|
||||
|
||||
osmo-trx (0.1.9) trusty; urgency=medium
|
||||
|
||||
* Ask Ivan, really
|
||||
|
||||
3
debian/control
vendored
3
debian/control
vendored
@@ -5,6 +5,7 @@ Maintainer: Ivan Klyuchnikov <ivan.kluchnikov@fairwaves.ru>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
autotools-dev,
|
||||
autoconf-archive,
|
||||
libdbd-sqlite3,
|
||||
libsqlite3-dev,
|
||||
pkg-config,
|
||||
dh-autoreconf,
|
||||
@@ -19,7 +20,7 @@ Homepage: https://projects.osmocom.org/projects/osmotrx
|
||||
|
||||
Package: osmo-trx
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3
|
||||
Description: SDR transceiver that implements Layer 1 of a GSM BTS
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
|
||||
151
git-version-gen
151
git-version-gen
@@ -1,151 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-01-28.01
|
||||
|
||||
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# It is probably wise to add these two files to .gitignore, so that you
|
||||
# don't accidentally commit either generated file.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .tarball-version will
|
||||
# exist in distribution tarballs.
|
||||
#
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
case $# in
|
||||
1) ;;
|
||||
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
|
||||
esac
|
||||
|
||||
tarball_version_file=$1
|
||||
nl='
|
||||
'
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || exit 1
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test -z "$v" \
|
||||
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif
|
||||
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
v[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
|
||||
else
|
||||
v=UNKNOWN
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed 's/^v//'`
|
||||
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git status > /dev/null 2>&1
|
||||
|
||||
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d '\012'
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
||||
Reference in New Issue
Block a user