mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-06 07:03:38 +00:00
Compare commits
35 Commits
fairwaves/
...
0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78e1cd20e2 | ||
|
|
db9c1b54cb | ||
|
|
099a44abfb | ||
|
|
8c80095017 | ||
|
|
d49a6aa136 | ||
|
|
81486e053c | ||
|
|
28d8081e25 | ||
|
|
87ed77b937 | ||
|
|
f9d996813d | ||
|
|
aa5acc953c | ||
|
|
934da48618 | ||
|
|
7c405a0c1f | ||
|
|
4cafb0fa15 | ||
|
|
f611569018 | ||
|
|
354741326c | ||
|
|
d2e5c5694e | ||
|
|
a3dce85ffc | ||
|
|
bb0c68ae61 | ||
|
|
87d158cc2d | ||
|
|
7278a87767 | ||
|
|
63eef9faf2 | ||
|
|
d67bd603e9 | ||
|
|
988a464d5d | ||
|
|
1b6ab7d7ee | ||
|
|
980525c8a9 | ||
|
|
70134a01eb | ||
|
|
1fb0ce67d8 | ||
|
|
8ca237b5c2 | ||
|
|
082bbbf8fe | ||
|
|
3bd763d2a1 | ||
|
|
ee57357682 | ||
|
|
8537b90dbe | ||
|
|
038fd7fd70 | ||
|
|
0cd246c27a | ||
|
|
61fbf2ec95 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,8 +3,6 @@
|
|||||||
*.lo
|
*.lo
|
||||||
*.la
|
*.la
|
||||||
Transceiver52M/osmo-trx
|
Transceiver52M/osmo-trx
|
||||||
Transceiver52M/osmo-trx-gen
|
|
||||||
Transceiver52M/osmo-trx-dec
|
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
CommonLibs/BitVectorTest
|
CommonLibs/BitVectorTest
|
||||||
|
|||||||
@@ -223,18 +223,18 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
UDPSocket::UDPSocket(unsigned short wSrcPort)
|
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
|
||||||
:DatagramSocket()
|
:DatagramSocket()
|
||||||
{
|
{
|
||||||
open(wSrcPort);
|
open(wSrcPort, wSrcIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UDPSocket::UDPSocket(unsigned short wSrcPort,
|
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
|
||||||
const char *wDestIP, unsigned short wDestPort)
|
const char *wDestIP, unsigned short wDestPort)
|
||||||
:DatagramSocket()
|
:DatagramSocket()
|
||||||
{
|
{
|
||||||
open(wSrcPort);
|
open(wSrcPort, wSrcIP);
|
||||||
destination(wDestPort, wDestIP);
|
destination(wDestPort, wDestIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UDPSocket::open(unsigned short localPort)
|
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
|
||||||
{
|
{
|
||||||
// create
|
// create
|
||||||
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
||||||
@@ -265,7 +265,7 @@ void UDPSocket::open(unsigned short localPort)
|
|||||||
size_t length = sizeof(address);
|
size_t length = sizeof(address);
|
||||||
bzero(&address,length);
|
bzero(&address,length);
|
||||||
address.sin_family = AF_INET;
|
address.sin_family = AF_INET;
|
||||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
address.sin_addr.s_addr = inet_addr(wlocalIP);
|
||||||
address.sin_port = htons(localPort);
|
address.sin_port = htons(localPort);
|
||||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||||
perror("bind() failed");
|
perror("bind() failed");
|
||||||
|
|||||||
@@ -144,10 +144,10 @@ class UDPSocket : public DatagramSocket {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** Open a USP socket with an OS-assigned port and no default destination. */
|
/** Open a USP socket with an OS-assigned port and no default destination. */
|
||||||
UDPSocket( unsigned short localPort=0);
|
UDPSocket(const char *localIP, unsigned short localPort);
|
||||||
|
|
||||||
/** Given a full specification, open the socket and set the dest address. */
|
/** Given a full specification, open the socket and set the dest address. */
|
||||||
UDPSocket( unsigned short localPort,
|
UDPSocket(const char *localIP, unsigned short localPort,
|
||||||
const char *remoteIP, unsigned short remotePort);
|
const char *remoteIP, unsigned short remotePort);
|
||||||
|
|
||||||
/** Set the destination port. */
|
/** Set the destination port. */
|
||||||
@@ -157,7 +157,7 @@ public:
|
|||||||
unsigned short port() const;
|
unsigned short port() const;
|
||||||
|
|
||||||
/** Open and bind the UDP socket to a local port. */
|
/** Open and bind the UDP socket to a local port. */
|
||||||
void open(unsigned short localPort=0);
|
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
|
||||||
|
|
||||||
/** Give the return address of the most recently received packet. */
|
/** Give the return address of the most recently received packet. */
|
||||||
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ static const int gNumToSend = 10;
|
|||||||
|
|
||||||
void *testReaderIP(void *)
|
void *testReaderIP(void *)
|
||||||
{
|
{
|
||||||
UDPSocket readSocket(5934, "localhost", 5061);
|
UDPSocket readSocket("127.0.0.1", 5934, "localhost", 5061);
|
||||||
readSocket.nonblocking();
|
readSocket.nonblocking();
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
while (rc<gNumToSend) {
|
while (rc<gNumToSend) {
|
||||||
@@ -82,7 +82,7 @@ int main(int argc, char * argv[] )
|
|||||||
Thread readerThreadUnix;
|
Thread readerThreadUnix;
|
||||||
readerThreadUnix.start(testReaderUnix,NULL);
|
readerThreadUnix.start(testReaderUnix,NULL);
|
||||||
|
|
||||||
UDPSocket socket1(5061, "127.0.0.1",5934);
|
UDPSocket socket1("127.0.0.1", 5061, "127.0.0.1", 5934);
|
||||||
UDDSocket socket1U("testSource","testDestination");
|
UDDSocket socket1U("testSource","testDestination");
|
||||||
|
|
||||||
COUT("socket1: " << socket1.port());
|
COUT("socket1: " << socket1.port());
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ template <class T> class Vector {
|
|||||||
/** Build an empty Vector of a given size. */
|
/** Build an empty Vector of a given size. */
|
||||||
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
||||||
|
|
||||||
/** Build a Vector by shifting the data block. */
|
/** Build a Vector by moving another. */
|
||||||
Vector(Vector<T>& other)
|
Vector(Vector<T>&& other)
|
||||||
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
||||||
{ other.mData=NULL; }
|
{ other.mData=NULL; }
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ EXTRA_DIST = \
|
|||||||
COPYING \
|
COPYING \
|
||||||
README
|
README
|
||||||
|
|
||||||
|
@RELMAKE@
|
||||||
|
|
||||||
dox: FORCE
|
dox: FORCE
|
||||||
doxygen doxconfig
|
doxygen doxconfig
|
||||||
|
|||||||
@@ -69,10 +69,7 @@ libtransceiver_la_SOURCES = \
|
|||||||
radioInterfaceResamp.cpp \
|
radioInterfaceResamp.cpp \
|
||||||
radioInterfaceMulti.cpp
|
radioInterfaceMulti.cpp
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = osmo-trx
|
||||||
osmo-trx \
|
|
||||||
osmo-trx-gen \
|
|
||||||
osmo-trx-dec
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
Complex.h \
|
Complex.h \
|
||||||
@@ -102,20 +99,6 @@ osmo_trx_LDADD = \
|
|||||||
$(GSM_LA) \
|
$(GSM_LA) \
|
||||||
$(COMMON_LA) $(SQLITE3_LIBS)
|
$(COMMON_LA) $(SQLITE3_LIBS)
|
||||||
|
|
||||||
osmo_trx_gen_SOURCES = osmo-trx-gen.cpp
|
|
||||||
osmo_trx_gen_LDADD = \
|
|
||||||
libtransceiver.la \
|
|
||||||
$(ARCH_LA) \
|
|
||||||
$(GSM_LA) \
|
|
||||||
$(COMMON_LA) $(SQLITE_LA)
|
|
||||||
|
|
||||||
osmo_trx_dec_SOURCES = osmo-trx-dec.cpp
|
|
||||||
osmo_trx_dec_LDADD = \
|
|
||||||
libtransceiver.la \
|
|
||||||
$(ARCH_LA) \
|
|
||||||
$(GSM_LA) \
|
|
||||||
$(COMMON_LA) $(SQLITE_LA)
|
|
||||||
|
|
||||||
if USRP1
|
if USRP1
|
||||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||||
osmo_trx_LDADD += $(USRP_LIBS)
|
osmo_trx_LDADD += $(USRP_LIBS)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "Resampler.h"
|
#include "Resampler.h"
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define MAX_OUTPUT_LEN 4096
|
#define MAX_OUTPUT_LEN 4096
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
static float sinc(float x)
|
static float sinc(float x)
|
||||||
{
|
{
|
||||||
if (x == 0.0)
|
if (x == 0.0)
|
||||||
@@ -43,32 +46,19 @@ static float sinc(float x)
|
|||||||
return sin(M_PI * x) / (M_PI * x);
|
return sin(M_PI * x) / (M_PI * x);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resampler::initFilters(float bw)
|
void Resampler::initFilters(float bw)
|
||||||
{
|
{
|
||||||
size_t proto_len = p * filt_len;
|
float cutoff;
|
||||||
float *proto, val, cutoff;
|
|
||||||
float sum = 0.0f, scale = 0.0f;
|
float sum = 0.0f, scale = 0.0f;
|
||||||
float midpt = (float) (proto_len - 1.0) / 2.0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate partition filters and the temporary prototype filter
|
* Allocate partition filters and the temporary prototype filter
|
||||||
* according to numerator of the rational rate. Coefficients are
|
* according to numerator of the rational rate. Coefficients are
|
||||||
* real only and must be 16-byte memory aligned for SSE usage.
|
* real only and must be 16-byte memory aligned for SSE usage.
|
||||||
*/
|
*/
|
||||||
proto = new float[proto_len];
|
auto proto = vector<float>(p * filt_len);
|
||||||
if (!proto)
|
for (auto &part : partitions)
|
||||||
return false;
|
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
|
||||||
|
|
||||||
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.
|
* Generate the prototype filter with a Blackman-harris window.
|
||||||
@@ -85,47 +75,26 @@ bool Resampler::initFilters(float bw)
|
|||||||
else
|
else
|
||||||
cutoff = (float) q;
|
cutoff = (float) q;
|
||||||
|
|
||||||
for (size_t i = 0; i < proto_len; i++) {
|
float midpt = (proto.size() - 1) / 2.0;
|
||||||
|
for (size_t i = 0; i < proto.size(); i++) {
|
||||||
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
||||||
proto[i] *= a0 -
|
proto[i] *= a0 -
|
||||||
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
|
a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
|
||||||
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
|
a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
|
||||||
a3 * cos(6 * M_PI * i / (proto_len - 1));
|
a3 * cos(6 * M_PI * i / (proto.size() - 1));
|
||||||
sum += proto[i];
|
sum += proto[i];
|
||||||
}
|
}
|
||||||
scale = p / sum;
|
scale = p / sum;
|
||||||
|
|
||||||
/* Populate filter partitions from the prototype filter */
|
/* Populate filter partitions from the prototype filter */
|
||||||
for (size_t i = 0; i < filt_len; i++) {
|
for (size_t i = 0; i < filt_len; i++) {
|
||||||
for (size_t n = 0; n < p; n++) {
|
for (size_t n = 0; n < p; n++)
|
||||||
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
|
partitions[n][i] = complex<float>(proto[i * p + n] * scale);
|
||||||
partitions[n][2 * i + 1] = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For convolution, we store the filter taps in reverse */
|
/* Store filter taps in reverse */
|
||||||
for (size_t n = 0; n < p; n++) {
|
for (auto &part : partitions)
|
||||||
for (size_t i = 0; i < filt_len / 2; i++) {
|
reverse(&part[0], &part[filt_len]);
|
||||||
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)
|
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||||
@@ -159,14 +128,6 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
|
|||||||
return true;
|
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 Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
|
||||||
{
|
{
|
||||||
int n, path;
|
int n, path;
|
||||||
@@ -180,8 +141,8 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
|
|||||||
path = out_path[i];
|
path = out_path[i];
|
||||||
|
|
||||||
convolve_real(in, in_len,
|
convolve_real(in, in_len,
|
||||||
partitions[path], filt_len,
|
reinterpret_cast<float *>(partitions[path]),
|
||||||
&out[2 * i], out_len - i,
|
filt_len, &out[2 * i], out_len - i,
|
||||||
n, 1, 1, 0);
|
n, 1, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,14 +151,18 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
|
|||||||
|
|
||||||
bool Resampler::init(float bw)
|
bool Resampler::init(float bw)
|
||||||
{
|
{
|
||||||
|
if (p == 0 || q == 0 || filt_len == 0) return false;
|
||||||
|
|
||||||
/* Filterbank filter internals */
|
/* Filterbank filter internals */
|
||||||
if (!initFilters(bw))
|
initFilters(bw);
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Precompute filterbank paths */
|
/* Precompute filterbank paths */
|
||||||
in_index = new size_t[MAX_OUTPUT_LEN];
|
int i = 0;
|
||||||
out_path = new size_t[MAX_OUTPUT_LEN];
|
for (auto &index : in_index)
|
||||||
computePath();
|
index = (q * i++) / p;
|
||||||
|
i = 0;
|
||||||
|
for (auto &path : out_path)
|
||||||
|
path = (q * i++) % p;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -208,7 +173,7 @@ size_t Resampler::len()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||||
: in_index(NULL), out_path(NULL), partitions(NULL)
|
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
|
||||||
{
|
{
|
||||||
this->p = p;
|
this->p = p;
|
||||||
this->q = q;
|
this->q = q;
|
||||||
@@ -217,8 +182,6 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
|||||||
|
|
||||||
Resampler::~Resampler()
|
Resampler::~Resampler()
|
||||||
{
|
{
|
||||||
releaseFilters();
|
for (auto &part : partitions)
|
||||||
|
free(part);
|
||||||
delete in_index;
|
|
||||||
delete out_path;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
#ifndef _RESAMPLER_H_
|
#ifndef _RESAMPLER_H_
|
||||||
#define _RESAMPLER_H_
|
#define _RESAMPLER_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <complex>
|
||||||
|
|
||||||
class Resampler {
|
class Resampler {
|
||||||
public:
|
public:
|
||||||
/* Constructor for rational sample rate conversion
|
/* Constructor for rational sample rate conversion
|
||||||
@@ -63,14 +66,11 @@ private:
|
|||||||
size_t p;
|
size_t p;
|
||||||
size_t q;
|
size_t q;
|
||||||
size_t filt_len;
|
size_t filt_len;
|
||||||
size_t *in_index;
|
std::vector<size_t> in_index;
|
||||||
size_t *out_path;
|
std::vector<size_t> out_path;
|
||||||
|
std::vector<std::complex<float> *> partitions;
|
||||||
|
|
||||||
float **partitions;
|
void initFilters(float bw);
|
||||||
|
|
||||||
bool initFilters(float bw);
|
|
||||||
void releaseFilters();
|
|
||||||
void computePath();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _RESAMPLER_H_ */
|
#endif /* _RESAMPLER_H_ */
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un
|
|||||||
burst = generateDummyBurst(sps, n);
|
burst = generateDummyBurst(sps, n);
|
||||||
break;
|
break;
|
||||||
case Transceiver::FILLER_NORM_RAND:
|
case Transceiver::FILLER_NORM_RAND:
|
||||||
burst = genRandNormalBurst(rtsc, sps, n, mPrbs);
|
burst = genRandNormalBurst(rtsc, sps, n);
|
||||||
break;
|
break;
|
||||||
case Transceiver::FILLER_EDGE_RAND:
|
case Transceiver::FILLER_EDGE_RAND:
|
||||||
burst = generateEdgeBurst(rtsc);
|
burst = generateEdgeBurst(rtsc);
|
||||||
@@ -112,16 +112,17 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un
|
|||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::Transceiver(int wBasePort,
|
Transceiver::Transceiver(int wBasePort,
|
||||||
const char *wTRXAddress,
|
const char *TRXAddress,
|
||||||
|
const char *GSMcoreAddress,
|
||||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface,
|
RadioInterface *wRadioInterface,
|
||||||
double wRssiOffset)
|
double wRssiOffset)
|
||||||
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
|
||||||
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
|
||||||
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||||
rssiOffset(wRssiOffset),
|
rssiOffset(wRssiOffset),
|
||||||
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false),
|
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
|
||||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
||||||
mWriteBurstToDiskMask(0)
|
mWriteBurstToDiskMask(0)
|
||||||
{
|
{
|
||||||
@@ -197,8 +198,8 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
|
|||||||
d_srcport = mBasePort + 2 * i + 2;
|
d_srcport = mBasePort + 2 * i + 2;
|
||||||
d_dstport = mBasePort + 2 * i + 102;
|
d_dstport = mBasePort + 2 * i + 102;
|
||||||
|
|
||||||
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
|
mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
|
||||||
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Randomize the central clock */
|
/* Randomize the central clock */
|
||||||
@@ -273,7 +274,7 @@ bool Transceiver::start()
|
|||||||
TxUpperLoopAdapter, (void*) chan);
|
TxUpperLoopAdapter, (void*) chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeClockInterface();
|
mForceClockInterface = true;
|
||||||
mOn = true;
|
mOn = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -297,6 +298,10 @@ void Transceiver::stop()
|
|||||||
LOG(NOTICE) << "Stopping the transceiver";
|
LOG(NOTICE) << "Stopping the transceiver";
|
||||||
mTxLowerLoopThread->cancel();
|
mTxLowerLoopThread->cancel();
|
||||||
mRxLowerLoopThread->cancel();
|
mRxLowerLoopThread->cancel();
|
||||||
|
mTxLowerLoopThread->join();
|
||||||
|
mRxLowerLoopThread->join();
|
||||||
|
delete mTxLowerLoopThread;
|
||||||
|
delete mRxLowerLoopThread;
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
mRxServiceLoopThreads[i]->cancel();
|
mRxServiceLoopThreads[i]->cancel();
|
||||||
@@ -315,11 +320,6 @@ void Transceiver::stop()
|
|||||||
mTxPriorityQueues[i].clear();
|
mTxPriorityQueues[i].clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
mTxLowerLoopThread->join();
|
|
||||||
mRxLowerLoopThread->join();
|
|
||||||
delete mTxLowerLoopThread;
|
|
||||||
delete mRxLowerLoopThread;
|
|
||||||
|
|
||||||
mOn = false;
|
mOn = false;
|
||||||
LOG(NOTICE) << "Transceiver stopped";
|
LOG(NOTICE) << "Transceiver stopped";
|
||||||
}
|
}
|
||||||
@@ -607,7 +607,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
|||||||
avg = sqrt(avg / radio_burst->chans());
|
avg = sqrt(avg / radio_burst->chans());
|
||||||
|
|
||||||
wTime = time;
|
wTime = time;
|
||||||
RSSI = 20.0 * log10(avg / rxFullScale);
|
RSSI = 20.0 * log10(rxFullScale / avg);
|
||||||
|
|
||||||
/* RSSI estimation are valid */
|
/* RSSI estimation are valid */
|
||||||
isRssiValid = true;
|
isRssiValid = true;
|
||||||
@@ -678,9 +678,6 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
|
|
||||||
sscanf(buffer,"%3s %s",cmdcheck,command);
|
sscanf(buffer,"%3s %s",cmdcheck,command);
|
||||||
|
|
||||||
if (!chan)
|
|
||||||
writeClockInterface();
|
|
||||||
|
|
||||||
if (strcmp(cmdcheck,"CMD")!=0) {
|
if (strcmp(cmdcheck,"CMD")!=0) {
|
||||||
LOG(WARNING) << "bogus message on control interface";
|
LOG(WARNING) << "bogus message on control interface";
|
||||||
return;
|
return;
|
||||||
@@ -874,8 +871,8 @@ void Transceiver::driveReceiveRadio()
|
|||||||
{
|
{
|
||||||
if (!mRadioInterface->driveReceiveRadio()) {
|
if (!mRadioInterface->driveReceiveRadio()) {
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
} else {
|
} else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
|
||||||
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
mForceClockInterface = false;
|
||||||
writeClockInterface();
|
writeClockInterface();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,9 +84,6 @@ struct TransceiverState {
|
|||||||
|
|
||||||
/* Shadowed downlink attenuation */
|
/* Shadowed downlink attenuation */
|
||||||
int mPower;
|
int mPower;
|
||||||
|
|
||||||
/* Pseudorandom bit sequence */
|
|
||||||
PRBS9 mPrbs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The Transceiver class, responsible for physical layer of basestation */
|
/** The Transceiver class, responsible for physical layer of basestation */
|
||||||
@@ -94,13 +91,15 @@ class Transceiver {
|
|||||||
public:
|
public:
|
||||||
/** Transceiver constructor
|
/** Transceiver constructor
|
||||||
@param wBasePort base port number of UDP sockets
|
@param wBasePort base port number of UDP sockets
|
||||||
@param TRXAddress IP address of the TRX manager, as a string
|
@param TRXAddress IP address of the TRX, as a string
|
||||||
|
@param GSMcoreAddress IP address of the GSM core, as a string
|
||||||
@param wSPS number of samples per GSM symbol
|
@param wSPS number of samples per GSM symbol
|
||||||
@param wTransmitLatency initial setting of transmit latency
|
@param wTransmitLatency initial setting of transmit latency
|
||||||
@param radioInterface associated radioInterface object
|
@param radioInterface associated radioInterface object
|
||||||
*/
|
*/
|
||||||
Transceiver(int wBasePort,
|
Transceiver(int wBasePort,
|
||||||
const char *TRXAddress,
|
const char *TRXAddress,
|
||||||
|
const char *GSMcoreAddress,
|
||||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface,
|
RadioInterface *wRadioInterface,
|
||||||
@@ -155,7 +154,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
int mBasePort;
|
int mBasePort;
|
||||||
std::string mAddr;
|
std::string mLocalAddr;
|
||||||
|
std::string mRemoteAddr;
|
||||||
|
|
||||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
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
|
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||||
@@ -211,6 +211,7 @@ private:
|
|||||||
|
|
||||||
bool mEdge;
|
bool mEdge;
|
||||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
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
|
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||||
double mTxFreq; ///< the transmit frequency
|
double mTxFreq; ///< the transmit frequency
|
||||||
double mRxFreq; ///< the receive frequency
|
double mRxFreq; ///< the receive frequency
|
||||||
@@ -278,4 +279,3 @@ void *ControlServiceLoopAdapter(TransceiverChannel *);
|
|||||||
|
|
||||||
/** transmit queueing thread loop */
|
/** transmit queueing thread loop */
|
||||||
void *TxUpperLoopAdapter(TransceiverChannel *);
|
void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
* See the COPYING file in the main directory for details.
|
* See the COPYING file in the main directory for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include "radioDevice.h"
|
#include "radioDevice.h"
|
||||||
#include "Threads.h"
|
#include "Threads.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
@@ -37,12 +38,6 @@
|
|||||||
#include <uhd/utils/msg.hpp>
|
#include <uhd/utils/msg.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define B2XX_CLK_RT 26e6
|
|
||||||
#define B2XX_MCBTS_CLK_RT 51.2e6
|
|
||||||
#define E1XX_CLK_RT 52e6
|
|
||||||
#define LIMESDR_CLK_RT (GSMRATE*32)
|
|
||||||
#define B100_BASE_RT 400000
|
|
||||||
#define USRP2_BASE_RT 390625
|
|
||||||
#define USRP_TX_AMPL 0.3
|
#define USRP_TX_AMPL 0.3
|
||||||
#define UMTRX_TX_AMPL 0.7
|
#define UMTRX_TX_AMPL 0.7
|
||||||
#define LIMESDR_TX_AMPL 0.3
|
#define LIMESDR_TX_AMPL 0.3
|
||||||
@@ -73,15 +68,6 @@ enum uhd_dev_type {
|
|||||||
X3XX,
|
X3XX,
|
||||||
UMTRX,
|
UMTRX,
|
||||||
LIMESDR,
|
LIMESDR,
|
||||||
NUM_USRP_TYPES,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct uhd_dev_offset {
|
|
||||||
enum uhd_dev_type type;
|
|
||||||
size_t tx_sps;
|
|
||||||
size_t rx_sps;
|
|
||||||
double offset;
|
|
||||||
const std::string desc;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -109,76 +95,44 @@ struct uhd_dev_offset {
|
|||||||
* Notes:
|
* Notes:
|
||||||
* USRP1 with timestamps is not supported by UHD.
|
* USRP1 with timestamps is not supported by UHD.
|
||||||
*/
|
*/
|
||||||
static struct uhd_dev_offset uhd_offsets[] = {
|
|
||||||
{ USRP1, 1, 1, 0.0, "USRP1 not supported" },
|
/* Device Type, Tx-SPS, Rx-SPS */
|
||||||
{ USRP1, 4, 1, 0.0, "USRP1 not supported"},
|
typedef std::tuple<uhd_dev_type, int, int> dev_key;
|
||||||
{ USRP2, 1, 1, 1.2184e-4, "N2XX 1 SPS" },
|
|
||||||
{ USRP2, 4, 1, 7.6547e-5, "N2XX 4/1 SPS" },
|
/* Device parameter descriptor */
|
||||||
{ B100, 1, 1, 1.2104e-4, "B100 1 SPS" },
|
struct dev_desc {
|
||||||
{ B100, 4, 1, 7.9307e-5, "B100 4 SPS" },
|
unsigned channels;
|
||||||
{ B200, 1, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
|
double mcr;
|
||||||
{ B200, 4, 1, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" },
|
double rate;
|
||||||
{ B210, 1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
|
double offset;
|
||||||
{ B210, 4, 1, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" },
|
std::string str;
|
||||||
{ B2XX_MCBTS, 4, 4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" },
|
|
||||||
{ E1XX, 1, 1, 9.5192e-5, "E1XX 1 SPS" },
|
|
||||||
{ E1XX, 4, 1, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" },
|
|
||||||
{ E3XX, 1, 1, 1.84616e-4, "E3XX 1 SPS" },
|
|
||||||
{ E3XX, 4, 1, 1.29231e-4, "E3XX 4/1 Tx/Rx SPS" },
|
|
||||||
{ X3XX, 1, 1, 1.5360e-4, "X3XX 1 SPS"},
|
|
||||||
{ X3XX, 4, 1, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS"},
|
|
||||||
{ UMTRX, 1, 1, 9.9692e-5, "UmTRX 1 SPS" },
|
|
||||||
{ UMTRX, 4, 1, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS" },
|
|
||||||
{ USRP2, 4, 4, 4.6080e-5, "N2XX 4 SPS" },
|
|
||||||
{ B200, 4, 4, B2XX_TIMING_4_4SPS, "B200 4 SPS" },
|
|
||||||
{ B210, 4, 4, B2XX_TIMING_4_4SPS, "B210 4 SPS" },
|
|
||||||
{ X3XX, 4, 4, 5.6567e-5, "X3XX 4 SPS"},
|
|
||||||
{ UMTRX, 4, 4, 5.1503e-5, "UmTRX 4 SPS" },
|
|
||||||
{ LIMESDR, 4, 4, 16.5/GSMRATE, "STREAM/LimeSDR (4 SPS TX/RX)" },
|
|
||||||
};
|
};
|
||||||
#define NUM_UHD_OFFSETS (sizeof(uhd_offsets)/sizeof(uhd_offsets[0]))
|
|
||||||
|
|
||||||
/*
|
static const std::map<dev_key, dev_desc> dev_param_map {
|
||||||
* Select sample rate based on device type and requested samples-per-symbol.
|
{ std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
|
||||||
* The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
|
{ std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
|
||||||
* usable channel spacing of 400 kHz.
|
{ std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
|
||||||
*/
|
{ std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } },
|
||||||
static double select_rate(uhd_dev_type type, int sps,
|
{ std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } },
|
||||||
RadioDevice::InterfaceType iface)
|
{ std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
|
||||||
{
|
{ std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
|
||||||
if ((sps != 4) && (sps != 1))
|
{ std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
|
||||||
return -9999.99;
|
{ std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } },
|
||||||
|
{ std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
|
||||||
if (iface == RadioDevice::MULTI_ARFCN) {
|
{ std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
|
||||||
switch (type) {
|
{ std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } },
|
||||||
case B2XX_MCBTS:
|
{ std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } },
|
||||||
return 4 * MCBTS_SPACING;
|
{ std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } },
|
||||||
default:
|
{ std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } },
|
||||||
LOG(ALERT) << "Invalid device combination";
|
{ std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } },
|
||||||
return -9999.99;
|
{ std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } },
|
||||||
}
|
{ std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } },
|
||||||
}
|
{ 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"} },
|
||||||
switch (type) {
|
{ std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
|
||||||
case USRP2:
|
{ std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
|
||||||
case X3XX:
|
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
|
||||||
return USRP2_BASE_RT * sps;
|
};
|
||||||
case B100:
|
|
||||||
return B100_BASE_RT * sps;
|
|
||||||
case B200:
|
|
||||||
case B210:
|
|
||||||
case E1XX:
|
|
||||||
case E3XX:
|
|
||||||
case UMTRX:
|
|
||||||
case LIMESDR:
|
|
||||||
return GSMRATE * sps;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(ALERT) << "Unknown device type " << type;
|
|
||||||
return -9999.99;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Sample Buffer - Allows reading and writing of timed samples using osmo-trx
|
Sample Buffer - Allows reading and writing of timed samples using osmo-trx
|
||||||
@@ -339,9 +293,8 @@ private:
|
|||||||
std::vector<smpl_buf *> rx_buffers;
|
std::vector<smpl_buf *> rx_buffers;
|
||||||
|
|
||||||
void init_gains();
|
void init_gains();
|
||||||
double get_dev_offset();
|
void set_channels(bool swap);
|
||||||
int set_master_clk(double rate);
|
void set_rates();
|
||||||
int set_rates(double tx_rate, double rx_rate);
|
|
||||||
bool parse_dev_type();
|
bool parse_dev_type();
|
||||||
bool flush_recv(size_t num_pkts);
|
bool flush_recv(size_t num_pkts);
|
||||||
int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
|
int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
|
||||||
@@ -470,105 +423,22 @@ void uhd_device::init_gains()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double uhd_device::get_dev_offset()
|
void uhd_device::set_rates()
|
||||||
{
|
{
|
||||||
struct uhd_dev_offset *offset = NULL;
|
dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
|
||||||
|
if (desc.mcr != 0.0)
|
||||||
|
usrp_dev->set_master_clock_rate(desc.mcr);
|
||||||
|
|
||||||
/* Reject USRP1 */
|
tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
|
||||||
if (dev_type == USRP1) {
|
rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
|
||||||
LOG(ERR) << "Invalid device type";
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search for matching offset value */
|
|
||||||
for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
|
|
||||||
if ((dev_type == uhd_offsets[i].type) &&
|
|
||||||
(tx_sps == uhd_offsets[i].tx_sps) &&
|
|
||||||
(rx_sps == uhd_offsets[i].rx_sps)) {
|
|
||||||
offset = &uhd_offsets[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!offset) {
|
|
||||||
LOG(ERR) << "Invalid device configuration";
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "-- Setting " << offset->desc << std::endl;
|
|
||||||
|
|
||||||
return offset->offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int uhd_device::set_master_clk(double clk_rate)
|
|
||||||
{
|
|
||||||
double actual, offset, limit = 1.0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
usrp_dev->set_master_clock_rate(clk_rate);
|
|
||||||
} catch (const std::exception &ex) {
|
|
||||||
LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
|
|
||||||
LOG(ALERT) << ex.what();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
actual = usrp_dev->get_master_clock_rate();
|
|
||||||
offset = fabs(clk_rate - actual);
|
|
||||||
|
|
||||||
if (offset > limit) {
|
|
||||||
LOG(ALERT) << "Failed to set master clock rate";
|
|
||||||
LOG(ALERT) << "Requested clock rate " << clk_rate;
|
|
||||||
LOG(ALERT) << "Actual clock rate " << actual;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int uhd_device::set_rates(double tx_rate, double rx_rate)
|
|
||||||
{
|
|
||||||
double offset_limit = 1.0;
|
|
||||||
double tx_offset, rx_offset;
|
|
||||||
|
|
||||||
/* B2XX and E1xx are the only device where we set FPGA clocking */
|
|
||||||
if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
|
|
||||||
if (set_master_clk(B2XX_CLK_RT) < 0)
|
|
||||||
return -1;
|
|
||||||
} else if (dev_type == E1XX) {
|
|
||||||
if (set_master_clk(E1XX_CLK_RT) < 0)
|
|
||||||
return -1;
|
|
||||||
} else if (dev_type == B2XX_MCBTS) {
|
|
||||||
if (set_master_clk(B2XX_MCBTS_CLK_RT) < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (dev_type == LIMESDR) {
|
|
||||||
if (set_master_clk(LIMESDR_CLK_RT) < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Set sample rates
|
|
||||||
try {
|
|
||||||
usrp_dev->set_tx_rate(tx_rate);
|
usrp_dev->set_tx_rate(tx_rate);
|
||||||
usrp_dev->set_rx_rate(rx_rate);
|
usrp_dev->set_rx_rate(rx_rate);
|
||||||
} catch (const std::exception &ex) {
|
tx_rate = usrp_dev->get_tx_rate();
|
||||||
LOG(ALERT) << "UHD rate setting failed";
|
rx_rate = usrp_dev->get_rx_rate();
|
||||||
LOG(ALERT) << ex.what();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
this->tx_rate = usrp_dev->get_tx_rate();
|
|
||||||
this->rx_rate = usrp_dev->get_rx_rate();
|
|
||||||
|
|
||||||
tx_offset = fabs(this->tx_rate - tx_rate);
|
ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
|
||||||
rx_offset = fabs(this->rx_rate - rx_rate);
|
LOG(INFO) << "Rates configured for " << desc.str;
|
||||||
if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
|
|
||||||
LOG(ALERT) << "Actual sample rate differs from desired rate";
|
|
||||||
LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
|
|
||||||
<< this->rx_rate << ")";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double uhd_device::setTxGain(double db, size_t chan)
|
double uhd_device::setTxGain(double db, size_t chan)
|
||||||
@@ -641,87 +511,41 @@ double uhd_device::getRxGain(size_t chan)
|
|||||||
*/
|
*/
|
||||||
bool uhd_device::parse_dev_type()
|
bool uhd_device::parse_dev_type()
|
||||||
{
|
{
|
||||||
std::string mboard_str, dev_str;
|
uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
|
||||||
uhd::property_tree::sptr prop_tree;
|
std::string devString = prop_tree->access<std::string>("/name").get();
|
||||||
size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str, e3xx_str,
|
std::string mboardString = usrp_dev->get_mboard_name();
|
||||||
b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str, limesdr_str;
|
|
||||||
|
|
||||||
prop_tree = usrp_dev->get_device()->get_tree();
|
const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
|
||||||
dev_str = prop_tree->access<std::string>("/name").get();
|
{ "B100", { B100, TX_WINDOW_USRP1 } },
|
||||||
mboard_str = usrp_dev->get_mboard_name();
|
{ "B200", { B200, TX_WINDOW_USRP1 } },
|
||||||
|
{ "B200mini", { B200, TX_WINDOW_USRP1 } },
|
||||||
usrp1_str = dev_str.find("USRP1");
|
{ "B210", { B210, TX_WINDOW_USRP1 } },
|
||||||
usrp2_str = dev_str.find("USRP2");
|
{ "E100", { E1XX, TX_WINDOW_FIXED } },
|
||||||
b100_str = mboard_str.find("B100");
|
{ "E110", { E1XX, TX_WINDOW_FIXED } },
|
||||||
b200_str = mboard_str.find("B200");
|
{ "E310", { E3XX, TX_WINDOW_FIXED } },
|
||||||
b210_str = mboard_str.find("B210");
|
{ "E3XX", { E3XX, TX_WINDOW_FIXED } },
|
||||||
e100_str = mboard_str.find("E100");
|
{ "X300", { X3XX, TX_WINDOW_FIXED } },
|
||||||
e110_str = mboard_str.find("E110");
|
{ "X310", { X3XX, TX_WINDOW_FIXED } },
|
||||||
e310_str = mboard_str.find("E310");
|
{ "USRP2", { USRP2, TX_WINDOW_FIXED } },
|
||||||
e3xx_str = mboard_str.find("E3XX");
|
{ "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
|
||||||
x300_str = mboard_str.find("X300");
|
{ "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
|
||||||
x310_str = mboard_str.find("X310");
|
};
|
||||||
umtrx_str = dev_str.find("UmTRX");
|
|
||||||
// LimeSDR is based on STREAM board, so it's advertized as such
|
|
||||||
limesdr_str = dev_str.find("STREAM");
|
|
||||||
|
|
||||||
if (usrp1_str != std::string::npos) {
|
|
||||||
LOG(ALERT) << "USRP1 is not supported using the UHD driver";
|
|
||||||
LOG(ALERT) << "Please compile with GNU Radio libusrp support";
|
|
||||||
dev_type = USRP1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b100_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_USRP1;
|
|
||||||
dev_type = B100;
|
|
||||||
} else if (b200_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_USRP1;
|
|
||||||
dev_type = B200;
|
|
||||||
} else if (b210_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_USRP1;
|
|
||||||
dev_type = B210;
|
|
||||||
} else if (e100_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_FIXED;
|
|
||||||
dev_type = E1XX;
|
|
||||||
} else if (e110_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_FIXED;
|
|
||||||
dev_type = E1XX;
|
|
||||||
} else if (usrp2_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_FIXED;
|
|
||||||
dev_type = USRP2;
|
|
||||||
} else if ((e310_str != std::string::npos) ||
|
|
||||||
(e3xx_str != std::string::npos)) {
|
|
||||||
tx_window = TX_WINDOW_FIXED;
|
|
||||||
dev_type = E3XX;
|
|
||||||
} else if (x300_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_FIXED;
|
|
||||||
dev_type = X3XX;
|
|
||||||
} else if (x310_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_FIXED;
|
|
||||||
dev_type = X3XX;
|
|
||||||
} else if (umtrx_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_FIXED;
|
|
||||||
dev_type = UMTRX;
|
|
||||||
} else if (limesdr_str != std::string::npos) {
|
|
||||||
tx_window = TX_WINDOW_USRP1;
|
|
||||||
dev_type = LIMESDR;
|
|
||||||
} else {
|
|
||||||
LOG(ALERT) << "Unknown UHD device type "
|
|
||||||
<< dev_str << " " << mboard_str;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tx_window == TX_WINDOW_USRP1) {
|
|
||||||
LOG(INFO) << "Using USRP1 type transmit window for "
|
|
||||||
<< dev_str << " " << mboard_str;
|
|
||||||
} else {
|
|
||||||
LOG(INFO) << "Using fixed transmit window for "
|
|
||||||
<< dev_str << " " << mboard_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
mapIter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(ALERT) << "Unsupported device " << devString;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for UHD version > 3.9.0 for E3XX support
|
* Check for UHD version > 3.9.0 for E3XX support
|
||||||
@@ -743,6 +567,45 @@ static bool uhd_e3xx_version_chk()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uhd_device::set_channels(bool swap)
|
||||||
|
{
|
||||||
|
if (iface == MULTI_ARFCN) {
|
||||||
|
if (dev_type != B200 && dev_type != B210)
|
||||||
|
throw std::invalid_argument("Device does not support MCBTS");
|
||||||
|
dev_type = B2XX_MCBTS;
|
||||||
|
chans = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
|
||||||
|
throw std::invalid_argument("Device does not support number of requested channels");
|
||||||
|
|
||||||
|
std::string subdev_string;
|
||||||
|
switch (dev_type) {
|
||||||
|
case B210:
|
||||||
|
case E3XX:
|
||||||
|
if (chans == 1)
|
||||||
|
subdev_string = swap ? "A:B" : "A:A";
|
||||||
|
else if (chans == 2)
|
||||||
|
subdev_string = swap ? "A:B A:A" : "A:A A:B";
|
||||||
|
break;
|
||||||
|
case X3XX:
|
||||||
|
case UMTRX:
|
||||||
|
if (chans == 1)
|
||||||
|
subdev_string = swap ? "B:0" : "A:0";
|
||||||
|
else if (chans == 2)
|
||||||
|
subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subdev_string.empty()) {
|
||||||
|
uhd::usrp::subdev_spec_t spec(subdev_string);
|
||||||
|
usrp_dev->set_tx_subdev_spec(spec);
|
||||||
|
usrp_dev->set_rx_subdev_spec(spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||||
{
|
{
|
||||||
const char *refstr;
|
const char *refstr;
|
||||||
@@ -773,27 +636,10 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify and set channels
|
try {
|
||||||
if (iface == MULTI_ARFCN) {
|
set_channels(swap_channels);
|
||||||
if ((dev_type != B200) && (dev_type != B210)) {
|
} catch (const std::exception &e) {
|
||||||
LOG(ALERT) << "Unsupported device configuration";
|
LOG(ALERT) << "Channel setting failed - " << e.what();
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_type = B2XX_MCBTS;
|
|
||||||
chans = 1;
|
|
||||||
} else if (chans == 2) {
|
|
||||||
if (dev_type == B210) {
|
|
||||||
} else if (dev_type == UMTRX) {
|
|
||||||
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
|
|
||||||
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
|
||||||
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
|
||||||
} else {
|
|
||||||
LOG(ALERT) << "Invalid device configuration";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else if (chans != 1) {
|
|
||||||
LOG(ALERT) << "Invalid channel combination for device";
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,14 +666,12 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
|||||||
|
|
||||||
usrp_dev->set_clock_source(refstr);
|
usrp_dev->set_clock_source(refstr);
|
||||||
|
|
||||||
// Set rates
|
try {
|
||||||
double _rx_rate = select_rate(dev_type, rx_sps, iface);
|
set_rates();
|
||||||
double _tx_rate = select_rate(dev_type, tx_sps, iface);
|
} catch (const std::exception &e) {
|
||||||
|
LOG(ALERT) << "UHD rate setting failed - " << e.what();
|
||||||
if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
|
|
||||||
return -1;
|
|
||||||
if (set_rates(_tx_rate, _rx_rate) < 0)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Set RF frontend bandwidth
|
// Set RF frontend bandwidth
|
||||||
if (dev_type == UMTRX) {
|
if (dev_type == UMTRX) {
|
||||||
@@ -860,17 +704,6 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
|||||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
|
rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
|
||||||
|
|
||||||
// Set receive chain sample offset. Trigger the EDGE offset
|
|
||||||
// table by checking for 4 SPS on the receive path. No other
|
|
||||||
// configuration supports using 4 SPS.
|
|
||||||
double offset = get_dev_offset();
|
|
||||||
if (offset == 0.0) {
|
|
||||||
LOG(ERR) << "Unsupported configuration, no correction applied";
|
|
||||||
ts_offset = 0;
|
|
||||||
} else {
|
|
||||||
ts_offset = (TIMESTAMP) (offset * rx_rate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize and shadow gain values
|
// Initialize and shadow gain values
|
||||||
init_gains();
|
init_gains();
|
||||||
|
|
||||||
@@ -1236,7 +1069,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
|||||||
|
|
||||||
/* Find center frequency between channels */
|
/* Find center frequency between channels */
|
||||||
rf_spread = fabs(freqs[!chan] - freq);
|
rf_spread = fabs(freqs[!chan] - freq);
|
||||||
if (rf_spread > B2XX_CLK_RT) {
|
if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
|
||||||
LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
|
LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
|
||||||
return treq;
|
return treq;
|
||||||
}
|
}
|
||||||
@@ -1352,7 +1185,7 @@ TIMESTAMP uhd_device::initialReadTimestamp()
|
|||||||
double uhd_device::fullScaleInputValue()
|
double uhd_device::fullScaleInputValue()
|
||||||
{
|
{
|
||||||
if (dev_type == LIMESDR)
|
if (dev_type == LIMESDR)
|
||||||
return (double) 2047 * LIMESDR_TX_AMPL;
|
return (double) SHRT_MAX * LIMESDR_TX_AMPL;
|
||||||
if (dev_type == UMTRX)
|
if (dev_type == UMTRX)
|
||||||
return (double) SHRT_MAX * UMTRX_TX_AMPL;
|
return (double) SHRT_MAX * UMTRX_TX_AMPL;
|
||||||
else
|
else
|
||||||
@@ -1361,7 +1194,6 @@ double uhd_device::fullScaleInputValue()
|
|||||||
|
|
||||||
double uhd_device::fullScaleOutputValue()
|
double uhd_device::fullScaleOutputValue()
|
||||||
{
|
{
|
||||||
if (dev_type == LIMESDR) return (double) 2047;
|
|
||||||
return (double) SHRT_MAX;
|
return (double) SHRT_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,520 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iomanip>
|
|
||||||
|
|
||||||
#include "Logger.h"
|
|
||||||
#include "sigProcLib.h"
|
|
||||||
#include "signalVector.h"
|
|
||||||
#include "Transceiver.h"
|
|
||||||
#include "Configuration.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "convolve.h"
|
|
||||||
#include "convert.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEFAULT_RX_SPS 1
|
|
||||||
#define DEFAULT_SEARCH_WINDOW 30
|
|
||||||
|
|
||||||
// Tail + data + stealing + midamble + guard (without the last 0.25)
|
|
||||||
#define BURST_LEN_FULL 156
|
|
||||||
// Tail + data + stealing + midamble
|
|
||||||
#define BURST_LEN_ACTIVE 148
|
|
||||||
// Tail + data + stealing + midamble - 2*0.5
|
|
||||||
#define BURST_LEN_USEFUL 147
|
|
||||||
|
|
||||||
// Size of a sample in bytes as stores in a file
|
|
||||||
#define SAMPLE_SIZE_BYTES (2 * sizeof(float))
|
|
||||||
// Burst length in bytes as stored in a file
|
|
||||||
#define BURST_LEN_BYTES (BURST_LEN_FULL * SAMPLE_SIZE_BYTES)
|
|
||||||
|
|
||||||
ConfigurationTable gConfig;
|
|
||||||
|
|
||||||
struct trx_config {
|
|
||||||
std::string log_level;
|
|
||||||
unsigned sps;
|
|
||||||
unsigned tsc;
|
|
||||||
unsigned max_expected_delay_nb;
|
|
||||||
unsigned max_expected_delay_ab;
|
|
||||||
double full_scale;
|
|
||||||
bool edge;
|
|
||||||
CorrType type;
|
|
||||||
std::string filename;
|
|
||||||
unsigned ber_burst_avg; ///< Average BER over this many bursts.
|
|
||||||
///< Set to 0 to average for the whole duration.
|
|
||||||
};
|
|
||||||
|
|
||||||
class NormalBurstSoftbitMask {
|
|
||||||
public:
|
|
||||||
NormalBurstSoftbitMask(SoftVector &softBits)
|
|
||||||
: mSoftBits(softBits)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SoftVector &bits() { return mSoftBits; }
|
|
||||||
SoftVector tailBitsL() { return mSoftBits.segment(0,3); }
|
|
||||||
SoftVector dataBitsL() { return mSoftBits.segment(3,57); }
|
|
||||||
SoftVector stealingBitsL() { return mSoftBits.segment(60, 1); }
|
|
||||||
SoftVector midambleBits() { return mSoftBits.segment(61, 26); }
|
|
||||||
SoftVector stealingBitsR() { return mSoftBits.segment(87, 1); }
|
|
||||||
SoftVector dataBitsR() { return mSoftBits.segment(88,57); }
|
|
||||||
SoftVector tailBitsR() { return mSoftBits.segment(145,3); }
|
|
||||||
SoftVector guardBits() { return mSoftBits.segment(148,8); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
SoftVector &mSoftBits;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SoftBurst {
|
|
||||||
public:
|
|
||||||
SoftBurst(SoftVector *softBits, double toa=0)
|
|
||||||
: mSoftBits(softBits), mTOA(toa)
|
|
||||||
{
|
|
||||||
assert(mSoftBits != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
~SoftBurst()
|
|
||||||
{
|
|
||||||
delete mSoftBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TOA(double TOA) { mTOA = TOA; }
|
|
||||||
double TOA() { return mTOA; }
|
|
||||||
|
|
||||||
NormalBurstSoftbitMask normalBurstMask() { return NormalBurstSoftbitMask(*mSoftBits); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
SoftVector *mSoftBits;
|
|
||||||
double mTOA;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BEREstimator {
|
|
||||||
public:
|
|
||||||
BEREstimator(const PRBS& prbs)
|
|
||||||
: mPRBS(prbs), mTotalBits(0), mErrorBits(0), mSynchronized(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
unsigned synchronize(const BitVector &bits)
|
|
||||||
{
|
|
||||||
for (unsigned i=0; i<mPRBS.size(); i++) {
|
|
||||||
mPRBS.processBit(bits[i]);
|
|
||||||
}
|
|
||||||
mSynchronized = true;
|
|
||||||
return mPRBS.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void process(const BitVector &bits, size_t start_from = 0)
|
|
||||||
{
|
|
||||||
for (size_t i=start_from; i<bits.size(); i++) {
|
|
||||||
mTotalBits++;
|
|
||||||
if (mPRBS.generateBit() != bits.bit(i)) {
|
|
||||||
mErrorBits++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sync_and_process(const BitVector &bits)
|
|
||||||
{
|
|
||||||
unsigned skip = 0;
|
|
||||||
if (!mSynchronized) {
|
|
||||||
skip = synchronize(bits);
|
|
||||||
}
|
|
||||||
|
|
||||||
process(bits, skip);
|
|
||||||
}
|
|
||||||
|
|
||||||
void skip(size_t num)
|
|
||||||
{
|
|
||||||
for (size_t i=0; i<num; i++) {
|
|
||||||
mTotalBits++;
|
|
||||||
mErrorBits++;
|
|
||||||
mPRBS.generateBit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
mTotalBits = 0;
|
|
||||||
mErrorBits = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned totalBits() const { return mTotalBits; }
|
|
||||||
unsigned errorBits() const { return mErrorBits; }
|
|
||||||
double BER() const { return mErrorBits/(double)mTotalBits; }
|
|
||||||
|
|
||||||
bool isSynchronized() const {return mSynchronized; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
PRBS mPRBS;
|
|
||||||
unsigned mTotalBits;
|
|
||||||
unsigned mErrorBits;
|
|
||||||
bool mSynchronized;
|
|
||||||
};
|
|
||||||
|
|
||||||
double getBurstRSSI(const signalVector &burst, unsigned sps, double full_scale)
|
|
||||||
{
|
|
||||||
/* Calculate average power of the burst */
|
|
||||||
float avg = energyDetect(burst, 20 * sps);
|
|
||||||
return 20.0 * log10(sqrt(avg) / full_scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printDetectionResult(int rc)
|
|
||||||
{
|
|
||||||
if (rc > 0) {
|
|
||||||
std::cout << "Detected correlation type: " << (CorrType)rc << std::endl;
|
|
||||||
} else {
|
|
||||||
if (rc == -SIGERR_CLIP) {
|
|
||||||
std::cout << "Clipping detected on received RACH or Normal Burst" << std::endl;
|
|
||||||
} else if (rc != SIGERR_NONE) {
|
|
||||||
std::cout << "Unhandled RACH or Normal Burst detection error" << std::endl;
|
|
||||||
} else {
|
|
||||||
// std::cout << "No burst detected" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SoftVector *demodulateBurst(const signalVector &burst,
|
|
||||||
CorrType expected_type,
|
|
||||||
unsigned sps, unsigned tsc,
|
|
||||||
unsigned max_expected_delay,
|
|
||||||
double &timingOffset)
|
|
||||||
{
|
|
||||||
complex amp;
|
|
||||||
float toa;
|
|
||||||
int rc;
|
|
||||||
CorrType detected_type;
|
|
||||||
|
|
||||||
/* Detect normal or RACH bursts */
|
|
||||||
rc = detectAnyBurst(burst, tsc, BURST_THRESH, sps, expected_type, amp, toa,
|
|
||||||
max_expected_delay);
|
|
||||||
printDetectionResult(rc);
|
|
||||||
if (rc <= 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert samples to symbols
|
|
||||||
timingOffset = toa / sps;
|
|
||||||
// rc > 0 means it's a detected CorrType
|
|
||||||
detected_type = (CorrType)rc;
|
|
||||||
|
|
||||||
return demodAnyBurst(burst, sps, amp, toa, detected_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool processBurst(const trx_config &config, signalVector &burst,
|
|
||||||
unsigned max_expected_delay,
|
|
||||||
double &RSSI,
|
|
||||||
double &timingOffset,
|
|
||||||
BEREstimator &berEstimator)
|
|
||||||
{
|
|
||||||
RSSI = getBurstRSSI(burst, config.sps, config.full_scale);
|
|
||||||
SoftVector *softBits = demodulateBurst(burst, config.type, config.sps,config.tsc,
|
|
||||||
max_expected_delay, timingOffset);
|
|
||||||
|
|
||||||
/* Print burst information and content */
|
|
||||||
if (softBits == NULL) {
|
|
||||||
std::cout << "Skipped frame" << std::endl;
|
|
||||||
// TODO: This is different for EDGE
|
|
||||||
berEstimator.skip(57*2);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SoftBurst softBurst(softBits, timingOffset);
|
|
||||||
NormalBurstSoftbitMask nb = softBurst.normalBurstMask();
|
|
||||||
|
|
||||||
berEstimator.sync_and_process(nb.dataBitsL().sliced());
|
|
||||||
berEstimator.sync_and_process(nb.dataBitsR().sliced());
|
|
||||||
|
|
||||||
std::cout << "TOA: " << softBurst.TOA() << " symbols" << std::endl;
|
|
||||||
// Exclude tail and guard bits from the energy calculation
|
|
||||||
std::cout << "Energy: " << softBits->segment(3,142).getEnergy() << std::endl;
|
|
||||||
//std::cout << "Demodulated burst: " << *softBits << std::endl;
|
|
||||||
std::cout << " tail|--------------------------data---------------------------|f|--------midamble----------|f|--------------------------data---------------------------|tai|-guard--" << std::endl;
|
|
||||||
// " 000 010001011011110011101001100100000001010001011000100100010 0 11101111000100101110111100 0 011010111011101010011010111000101100001110101011011001011 000 1''..---"
|
|
||||||
std::cout << "Demodulated burst:"
|
|
||||||
<< " " << nb.tailBitsL()
|
|
||||||
<< " " << nb.dataBitsL()
|
|
||||||
<< " " << nb.stealingBitsL()
|
|
||||||
<< " " << nb.midambleBits()
|
|
||||||
<< " " << nb.stealingBitsR()
|
|
||||||
<< " " << nb.dataBitsR()
|
|
||||||
<< " " << nb.tailBitsR()
|
|
||||||
<< " " << nb.guardBits()
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup configuration values
|
|
||||||
static void print_config(struct trx_config *config)
|
|
||||||
{
|
|
||||||
std::ostringstream ost("");
|
|
||||||
ost << "Config Settings" << std::endl;
|
|
||||||
ost << " Source file name............. " << config->filename << std::endl;
|
|
||||||
ost << " Log Level.................... " << config->log_level << std::endl;
|
|
||||||
ost << " Rx Samples-per-Symbol........ " << config->sps << std::endl;
|
|
||||||
ost << " EDGE support................. " << (config->edge ? "Enabled" : "Disabled") << std::endl;
|
|
||||||
ost << " Burst type................... " << config->type << std::endl;
|
|
||||||
ost << " Burst TSC.................... " << config->tsc << std::endl;
|
|
||||||
ost << " Normal Burst search window... " << config->max_expected_delay_nb << std::endl;
|
|
||||||
ost << " Access Burst search window... " << config->max_expected_delay_ab << std::endl;
|
|
||||||
ost << " Signal full scale............ " << config->full_scale << std::endl;
|
|
||||||
ost << " BER average window (bursts).. " << config->ber_burst_avg << std::endl;
|
|
||||||
std::cout << ost << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_help()
|
|
||||||
{
|
|
||||||
fprintf(stdout, "Options:\n"
|
|
||||||
" -h This text\n"
|
|
||||||
" -l LEVEL Logging level (%s)\n"
|
|
||||||
" -e Enable EDGE receiver\n"
|
|
||||||
" -s SPS Samples-per-symbol (1 or 4, default: %d)\n"
|
|
||||||
" -t TSC Burst training sequence (0 to 7, default: 0)\n"
|
|
||||||
" -f FILE File to read\n"
|
|
||||||
" -w SYMBOLS Normal Burst search window (0 to 156, default: %d)\n"
|
|
||||||
" -W SYMBOLS Access Burst search window (0 to 156, default: %d)\n"
|
|
||||||
" -b BURSTS BER average window. Set to 0 to average over the whole file (default: 1)\n",
|
|
||||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG",
|
|
||||||
DEFAULT_RX_SPS,
|
|
||||||
DEFAULT_SEARCH_WINDOW, DEFAULT_SEARCH_WINDOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool handle_options(int argc, char **argv, struct trx_config *config)
|
|
||||||
{
|
|
||||||
int option;
|
|
||||||
|
|
||||||
config->log_level = "NOTICE";
|
|
||||||
config->sps = DEFAULT_RX_SPS;
|
|
||||||
config->tsc = 0;
|
|
||||||
config->max_expected_delay_nb = DEFAULT_SEARCH_WINDOW;
|
|
||||||
config->max_expected_delay_ab = DEFAULT_SEARCH_WINDOW;
|
|
||||||
config->full_scale = SHRT_MAX;
|
|
||||||
config->edge = false;
|
|
||||||
config->type = TSC;
|
|
||||||
config->ber_burst_avg = 1;
|
|
||||||
|
|
||||||
while ((option = getopt(argc, argv, "ls:et:f:w:W:b:h")) != -1) {
|
|
||||||
switch (option) {
|
|
||||||
case 'l':
|
|
||||||
config->log_level = optarg;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
config->sps = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'e':
|
|
||||||
config->edge = true;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
config->tsc = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
config->filename = optarg;
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
config->max_expected_delay_nb = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'W':
|
|
||||||
config->max_expected_delay_ab = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
config->ber_burst_avg = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
default:
|
|
||||||
print_help();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((config->sps != 1) && (config->sps != 4)) {
|
|
||||||
printf("ERROR: Unsupported samples-per-symbol %i\n\n", config->sps);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->edge && (config->sps != 4)) {
|
|
||||||
printf("ERROR: EDGE only supported at 4 samples per symbol\n\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->tsc > 7) {
|
|
||||||
printf("ERROR: Invalid training sequence %i\n\n", config->tsc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->filename.length() == 0) {
|
|
||||||
printf("ERROR: No input file specified\n\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->max_expected_delay_nb > 156 || config->max_expected_delay_nb < 0 ||
|
|
||||||
config->max_expected_delay_ab > 156 || config->max_expected_delay_ab < 0) {
|
|
||||||
printf("ERROR: Invalid search window size, must be withit [1..156] range\n\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
struct trx_config config;
|
|
||||||
|
|
||||||
#ifdef HAVE_SSE3
|
|
||||||
printf("Info: SSE3 support compiled in");
|
|
||||||
if (__builtin_cpu_supports("sse3"))
|
|
||||||
printf(" and supported by CPU\n");
|
|
||||||
else
|
|
||||||
printf(", but not supported by CPU\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SSE4_1
|
|
||||||
printf("Info: SSE4.1 support compiled in");
|
|
||||||
if (__builtin_cpu_supports("sse4.1"))
|
|
||||||
printf(" and supported by CPU\n");
|
|
||||||
else
|
|
||||||
printf(", but not supported by CPU\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
convolve_init();
|
|
||||||
convert_init();
|
|
||||||
|
|
||||||
// Process command line options and print config to screen
|
|
||||||
if (!handle_options(argc, argv, &config)) {
|
|
||||||
print_help();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
print_config(&config);
|
|
||||||
|
|
||||||
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
|
|
||||||
|
|
||||||
if (!sigProcLibSetup()) {
|
|
||||||
LOG(ALERT) << "Failed to initialize signal processing library";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
double RSSI;
|
|
||||||
double timingOffset, timingOffsetPrev = 0.0;
|
|
||||||
signalVector burst(2*BURST_LEN_FULL);
|
|
||||||
GSM::Time gsmTime;
|
|
||||||
bool syncedTo157bits = false; // We should syncronize to 156-157 frame structure only once
|
|
||||||
bool burst156_157 = false; // Set to true to enable 156-156-156-157 frame
|
|
||||||
int bitsReadExtra = 0; // set to 1 every 4 bursts and when TOA>1.0
|
|
||||||
int bitsToSkip = 0; // set to 1 when TOA<0.0
|
|
||||||
unsigned berBurstsAveraged = 0;
|
|
||||||
PRBS9 prbs;
|
|
||||||
BEREstimator berEstimator(prbs);
|
|
||||||
|
|
||||||
// Configure output stream
|
|
||||||
std::cout << std::fixed;
|
|
||||||
std::cout << std::setprecision(2);
|
|
||||||
|
|
||||||
std::ifstream file (config.filename.c_str(), std::ifstream::binary);
|
|
||||||
|
|
||||||
// Read the first burst, but do not process it, because we need at least two bursts
|
|
||||||
// worth of data for reliable initial detection.
|
|
||||||
file.read((char*)burst.begin(), config.sps * BURST_LEN_BYTES);
|
|
||||||
{signalVector t = burst.segment(0, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* Distort signal */
|
|
||||||
{
|
|
||||||
signalVector burst_read = burst.segment(85,156);
|
|
||||||
std::ifstream file (config.filename.c_str(), std::ifstream::binary);
|
|
||||||
file.read((char*)burst_read.begin(), burst_read.size() * 2 * sizeof(float));
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if 1
|
|
||||||
// Read more data and try burst detection until successful
|
|
||||||
while(file.read((char*)(burst.begin()+config.sps*BURST_LEN_FULL), config.sps*BURST_LEN_BYTES))
|
|
||||||
{
|
|
||||||
{signalVector t = burst.segment(BURST_LEN_FULL, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
|
|
||||||
bool found = processBurst(config, burst, BURST_LEN_FULL, RSSI, timingOffset, berEstimator);
|
|
||||||
std::cout << "RSSI: " << RSSI << " dBFS" << std::endl;
|
|
||||||
if (found) {
|
|
||||||
gsmTime.incTN();
|
|
||||||
berBurstsAveraged++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
burst.segmentMove(config.sps*BURST_LEN_FULL, 0, config.sps*BURST_LEN_FULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Align stream to burst
|
|
||||||
int offsetInt = (int)timingOffset;
|
|
||||||
burst.segmentMove(config.sps*(BURST_LEN_FULL+offsetInt), 0, config.sps*(BURST_LEN_FULL-offsetInt));
|
|
||||||
{signalVector t = burst.segment(0, BURST_LEN_FULL-offsetInt); scaleVector(t, complex(1.0/SHRT_MAX)); }
|
|
||||||
file.read((char*)(burst.begin()+config.sps*(BURST_LEN_FULL-offsetInt)), config.sps*offsetInt*SAMPLE_SIZE_BYTES);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Resize burst vector to hold only one burst, because demodulation code
|
|
||||||
// always decode the full vector size.
|
|
||||||
burst.shrink(BURST_LEN_FULL+1);
|
|
||||||
|
|
||||||
// Process the rest of the stream
|
|
||||||
do {
|
|
||||||
{signalVector t = burst.segment(0, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
|
|
||||||
processBurst(config, burst, (config.type==RACH)?config.max_expected_delay_ab:config.max_expected_delay_ab,
|
|
||||||
RSSI, timingOffset, berEstimator);
|
|
||||||
if (burst156_157 && !syncedTo157bits && timingOffset - timingOffsetPrev > .75) {
|
|
||||||
std::cout << "TOA adjust: Found a 157-bit burst, reset TN to mark it" << std::endl;
|
|
||||||
gsmTime.TN(2);
|
|
||||||
timingOffset -= 1.0;
|
|
||||||
// Make sure we do this adjustment only once.
|
|
||||||
syncedTo157bits = true;
|
|
||||||
} else {
|
|
||||||
gsmTime.incTN();
|
|
||||||
}
|
|
||||||
bitsToSkip = 0;
|
|
||||||
bitsReadExtra = 0;
|
|
||||||
if (timingOffset < 0.0) {
|
|
||||||
std::cout << "TOA adjust: skip a bit" << std::endl;
|
|
||||||
burst[0] = 0;
|
|
||||||
bitsToSkip = 1;
|
|
||||||
bitsReadExtra--;
|
|
||||||
}
|
|
||||||
bitsReadExtra += (gsmTime.TN()%4 == 0);
|
|
||||||
if (timingOffset > 1.1) {
|
|
||||||
std::cout << "TOA adjust: add extra bit" << std::endl;
|
|
||||||
bitsReadExtra++;
|
|
||||||
}
|
|
||||||
std::cout << "Clock: " << gsmTime;
|
|
||||||
std::cout << " RSSI: " << RSSI << " dBFS";
|
|
||||||
std::cout << " Error bits: " << berEstimator.errorBits() << " Total bits: " << berEstimator.totalBits()
|
|
||||||
<< " BER: " << 100.0*berEstimator.errorBits() / berEstimator.totalBits() << "%" << std::endl;
|
|
||||||
berBurstsAveraged++;
|
|
||||||
// Never reset if config.ber_burst_avg is 0
|
|
||||||
if (config.ber_burst_avg > 0 && berBurstsAveraged >= config.ber_burst_avg) {
|
|
||||||
berBurstsAveraged = 0;
|
|
||||||
berEstimator.reset();
|
|
||||||
}
|
|
||||||
std::cout << "bitsReadExtra: " << bitsReadExtra << " bitsToSkip: " << bitsToSkip << std::endl;
|
|
||||||
timingOffsetPrev = timingOffset;
|
|
||||||
} while(file.read((char*)(burst.begin()+bitsToSkip), config.sps*(BURST_LEN_BYTES+SAMPLE_SIZE_BYTES*bitsReadExtra)));
|
|
||||||
|
|
||||||
std::cout << "End of file reached" << std::endl;
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,334 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <endian.h> // for byte order manipulation
|
|
||||||
|
|
||||||
#include "Logger.h"
|
|
||||||
#include "sigProcLib.h"
|
|
||||||
#include "GSMCommon.h"
|
|
||||||
#include "BitVector.h"
|
|
||||||
#include "Configuration.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "convolve.h"
|
|
||||||
#include "convert.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEFAULT_SPS 4
|
|
||||||
#define DEFAULT_SEARCH_WINDOW 30
|
|
||||||
|
|
||||||
// Tail + data + stealing + midamble + guard (without the last 0.25)
|
|
||||||
#define BURST_LEN_FULL 156
|
|
||||||
// Tail + data + stealing + midamble
|
|
||||||
#define BURST_LEN_ACTIVE 148
|
|
||||||
// Tail + data + stealing + midamble - 2*0.5
|
|
||||||
#define BURST_LEN_USEFUL 147
|
|
||||||
|
|
||||||
// Size of a sample in bytes as stores in a file
|
|
||||||
#define SAMPLE_SIZE_BYTES (2 * sizeof(float))
|
|
||||||
// Burst length in bytes as stored in a file
|
|
||||||
#define BURST_LEN_BYTES (BURST_LEN_FULL * SAMPLE_SIZE_BYTES)
|
|
||||||
|
|
||||||
ConfigurationTable gConfig;
|
|
||||||
|
|
||||||
enum FileType {
|
|
||||||
FLOAT_NORM_LE, ///< Float -1..+1 Little Endian
|
|
||||||
FLOAT16_LE, ///< Float -32767..+32767 Little Endian
|
|
||||||
SIGNED16_LE, ///< Integer -32767..+32767 Little Endian
|
|
||||||
SIGNED16_BE, ///< Integer -32767..+32767 Big Endian (Keysight waveform format)
|
|
||||||
};
|
|
||||||
|
|
||||||
struct trx_config {
|
|
||||||
std::string log_level;
|
|
||||||
unsigned sps;
|
|
||||||
unsigned tsc;
|
|
||||||
double full_scale;
|
|
||||||
bool edge;
|
|
||||||
CorrType type;
|
|
||||||
std::string filename;
|
|
||||||
FileType file_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, FileType ftype)
|
|
||||||
{
|
|
||||||
switch(ftype)
|
|
||||||
{
|
|
||||||
case FLOAT_NORM_LE:
|
|
||||||
os << "float";
|
|
||||||
break;
|
|
||||||
case FLOAT16_LE:
|
|
||||||
os << "float16";
|
|
||||||
break;
|
|
||||||
case SIGNED16_LE:
|
|
||||||
os << "signed16";
|
|
||||||
break;
|
|
||||||
case SIGNED16_BE:
|
|
||||||
os << "signed16be";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(!"unknown file type");
|
|
||||||
}
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void writeBurstFloatNorm(std::ofstream& os, const signalVector& v)
|
|
||||||
{
|
|
||||||
os.write((char*)v.begin(), v.size() * 2 * sizeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeBurstFloat16LE(std::ofstream& os, const signalVector& v)
|
|
||||||
{
|
|
||||||
const complex *c = v.begin();
|
|
||||||
for (size_t i=0; i<v.size(); i++, c++) {
|
|
||||||
float iq[2];
|
|
||||||
iq[0] = c->real()*SHRT_MAX;
|
|
||||||
iq[1] = c->imag()*SHRT_MAX;
|
|
||||||
os.write((char*)&iq, 2*sizeof(float));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeBurstSigned16LE(std::ofstream& os, const signalVector& v)
|
|
||||||
{
|
|
||||||
const complex *c = v.begin();
|
|
||||||
for (size_t i=0; i<v.size(); i++, c++) {
|
|
||||||
int16_t iq[2];
|
|
||||||
iq[0] = c->real()*SHRT_MAX;
|
|
||||||
iq[1] = c->imag()*SHRT_MAX;
|
|
||||||
iq[0] = htole16(iq[0]);
|
|
||||||
iq[1] = htole16(iq[1]);
|
|
||||||
os.write((char*)&iq, 2*sizeof(int16_t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeBurstSigned16BE(std::ofstream& os, const signalVector& v)
|
|
||||||
{
|
|
||||||
const complex *c = v.begin();
|
|
||||||
for (size_t i=0; i<v.size(); i++, c++) {
|
|
||||||
int16_t iq[2];
|
|
||||||
iq[0] = c->real()*SHRT_MAX;
|
|
||||||
iq[1] = c->imag()*SHRT_MAX;
|
|
||||||
iq[0] = htobe16(iq[0]);
|
|
||||||
iq[1] = htobe16(iq[1]);
|
|
||||||
os.write((char*)&iq, 2*sizeof(int16_t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeBurst(std::ofstream& os, const signalVector& v, FileType ftype)
|
|
||||||
{
|
|
||||||
switch(ftype)
|
|
||||||
{
|
|
||||||
case FLOAT_NORM_LE:
|
|
||||||
writeBurstFloatNorm(os, v);
|
|
||||||
break;
|
|
||||||
case FLOAT16_LE:
|
|
||||||
writeBurstFloat16LE(os, v);
|
|
||||||
break;
|
|
||||||
case SIGNED16_LE:
|
|
||||||
writeBurstSigned16LE(os, v);
|
|
||||||
break;
|
|
||||||
case SIGNED16_BE:
|
|
||||||
writeBurstSigned16BE(os, v);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(!"unknown file type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup configuration values
|
|
||||||
static void print_config(struct trx_config *config)
|
|
||||||
{
|
|
||||||
std::ostringstream ost("");
|
|
||||||
ost << "Config Settings" << std::endl;
|
|
||||||
ost << " Destination file name........ " << config->filename << std::endl;
|
|
||||||
ost << " Destination file type........ " << config->file_type << std::endl;
|
|
||||||
ost << " Log Level.................... " << config->log_level << std::endl;
|
|
||||||
ost << " Tx Samples-per-Symbol........ " << config->sps << std::endl;
|
|
||||||
ost << " EDGE support................. " << (config->edge ? "Enabled" : "Disabled") << std::endl;
|
|
||||||
ost << " Burst type................... " << config->type << std::endl;
|
|
||||||
ost << " Burst TSC.................... " << config->tsc << std::endl;
|
|
||||||
ost << " Signal full scale............ " << config->full_scale << std::endl;
|
|
||||||
std::cout << ost << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_help()
|
|
||||||
{
|
|
||||||
fprintf(stdout,
|
|
||||||
"This utility generates waveform files aka IQ binary files in a number of formats"
|
|
||||||
"to use them as input to osmo-trx-dec or load them into signal generators.\n"
|
|
||||||
"\n"
|
|
||||||
"Options:\n"
|
|
||||||
" -h This text\n"
|
|
||||||
" -l LEVEL Logging level (%s)\n"
|
|
||||||
" -e Enable EDGE receiver\n"
|
|
||||||
" -s SPS Samples-per-symbol (1 or 4, default: %d)\n"
|
|
||||||
" -t TSC Burst training sequence (0 to 7, default: 0)\n"
|
|
||||||
" -f FILE File to write generated bursts to\n"
|
|
||||||
" -F FILETYPE Format of the file - float, float16, signed16, signed16be (default: f16)\n"
|
|
||||||
" Note: Keysight waveform format is signed16be. osmo-trx-dec accepts float16.\n",
|
|
||||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG",
|
|
||||||
DEFAULT_SPS);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileType option_to_file_type(const std::string &optarg)
|
|
||||||
{
|
|
||||||
if (optarg == "float") {
|
|
||||||
return FLOAT_NORM_LE;
|
|
||||||
} else if (optarg == "float16") {
|
|
||||||
return FLOAT16_LE;
|
|
||||||
} else if (optarg == "signed16") {
|
|
||||||
return SIGNED16_LE;
|
|
||||||
} else if (optarg == "signed16be") {
|
|
||||||
return SIGNED16_BE;
|
|
||||||
} else {
|
|
||||||
return (FileType)-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool handle_options(int argc, char **argv, struct trx_config *config)
|
|
||||||
{
|
|
||||||
int option;
|
|
||||||
|
|
||||||
config->log_level = "NOTICE";
|
|
||||||
config->sps = DEFAULT_SPS;
|
|
||||||
config->tsc = 0;
|
|
||||||
config->full_scale = SHRT_MAX;
|
|
||||||
config->edge = false;
|
|
||||||
config->type = TSC;
|
|
||||||
config->file_type = FLOAT16_LE;
|
|
||||||
|
|
||||||
while ((option = getopt(argc, argv, "ls:et:f:F:h")) != -1) {
|
|
||||||
switch (option) {
|
|
||||||
case 'l':
|
|
||||||
config->log_level = optarg;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
config->sps = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'e':
|
|
||||||
config->edge = true;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
config->tsc = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
config->filename = optarg;
|
|
||||||
break;
|
|
||||||
case 'F':
|
|
||||||
config->file_type = option_to_file_type(optarg);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
default:
|
|
||||||
print_help();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((config->sps != 1) && (config->sps != 4)) {
|
|
||||||
printf("ERROR: Unsupported samples-per-symbol %i\n\n", config->sps);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->edge && (config->sps != 4)) {
|
|
||||||
printf("ERROR: EDGE only supported at 4 samples per symbol\n\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->tsc > 7) {
|
|
||||||
printf("ERROR: Invalid training sequence %i\n\n", config->tsc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->filename.length() == 0) {
|
|
||||||
printf("ERROR: No output file name specified\n\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->file_type < 0) {
|
|
||||||
printf("ERROR: Wrong output file format\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
struct trx_config config;
|
|
||||||
|
|
||||||
#ifdef HAVE_SSE3
|
|
||||||
printf("Info: SSE3 support compiled in");
|
|
||||||
if (__builtin_cpu_supports("sse3"))
|
|
||||||
printf(" and supported by CPU\n");
|
|
||||||
else
|
|
||||||
printf(", but not supported by CPU\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SSE4_1
|
|
||||||
printf("Info: SSE4.1 support compiled in");
|
|
||||||
if (__builtin_cpu_supports("sse4.1"))
|
|
||||||
printf(" and supported by CPU\n");
|
|
||||||
else
|
|
||||||
printf(", but not supported by CPU\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
convolve_init();
|
|
||||||
convert_init();
|
|
||||||
|
|
||||||
// Process command line options and print config to screen
|
|
||||||
if (!handle_options(argc, argv, &config)) {
|
|
||||||
print_help();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
print_config(&config);
|
|
||||||
|
|
||||||
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
|
|
||||||
|
|
||||||
if (!sigProcLibSetup()) {
|
|
||||||
LOG(ALERT) << "Failed to initialize signal processing library";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
signalVector burst(2*BURST_LEN_FULL);
|
|
||||||
GSM::Time gsmTime;
|
|
||||||
PRBS9 prbs;
|
|
||||||
|
|
||||||
// Configure output stream
|
|
||||||
std::cout << std::fixed;
|
|
||||||
std::cout << std::setprecision(2);
|
|
||||||
|
|
||||||
std::ofstream file (config.filename.c_str(), std::ifstream::binary);
|
|
||||||
|
|
||||||
for (int i=0; i<511; i++) {
|
|
||||||
signalVector *signal = genRandNormalBurst(config.tsc, config.sps, gsmTime.TN(), prbs);
|
|
||||||
writeBurst(file, *signal, config.file_type);
|
|
||||||
gsmTime.incTN();
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
std::cout << "Done!" << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
|
||||||
#include <GSMCommon.h>
|
#include <GSMCommon.h>
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
@@ -61,7 +62,8 @@ extern "C" {
|
|||||||
|
|
||||||
struct trx_config {
|
struct trx_config {
|
||||||
std::string log_level;
|
std::string log_level;
|
||||||
std::string addr;
|
std::string local_addr;
|
||||||
|
std::string remote_addr;
|
||||||
std::string dev_args;
|
std::string dev_args;
|
||||||
unsigned port;
|
unsigned port;
|
||||||
unsigned tx_sps;
|
unsigned tx_sps;
|
||||||
@@ -77,6 +79,7 @@ struct trx_config {
|
|||||||
double rssi_offset;
|
double rssi_offset;
|
||||||
bool swap_channels;
|
bool swap_channels;
|
||||||
bool edge;
|
bool edge;
|
||||||
|
int sched_rr;
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigurationTable gConfig;
|
ConfigurationTable gConfig;
|
||||||
@@ -132,7 +135,8 @@ bool trx_setup_config(struct trx_config *config)
|
|||||||
ost << " Log Level............... " << config->log_level << std::endl;
|
ost << " Log Level............... " << config->log_level << std::endl;
|
||||||
ost << " Device args............. " << config->dev_args << std::endl;
|
ost << " Device args............. " << config->dev_args << std::endl;
|
||||||
ost << " TRX Base Port........... " << config->port << std::endl;
|
ost << " TRX Base Port........... " << config->port << std::endl;
|
||||||
ost << " TRX Address............. " << config->addr << std::endl;
|
ost << " TRX Address............. " << config->local_addr << std::endl;
|
||||||
|
ost << " GSM Core Address........." << config->remote_addr << std::endl;
|
||||||
ost << " Channels................ " << config->chans << std::endl;
|
ost << " Channels................ " << config->chans << std::endl;
|
||||||
ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
|
ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
|
||||||
ost << " Rx Samples-per-Symbol... " << config->rx_sps << std::endl;
|
ost << " Rx Samples-per-Symbol... " << config->rx_sps << std::endl;
|
||||||
@@ -198,9 +202,10 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
|
|||||||
Transceiver *trx;
|
Transceiver *trx;
|
||||||
VectorFIFO *fifo;
|
VectorFIFO *fifo;
|
||||||
|
|
||||||
trx = new Transceiver(config->port, config->addr.c_str(),
|
trx = new Transceiver(config->port, config->local_addr.c_str(),
|
||||||
config->tx_sps, config->rx_sps, config->chans,
|
config->remote_addr.c_str(), config->tx_sps,
|
||||||
GSM::Time(3,0), radio, config->rssi_offset);
|
config->rx_sps, config->chans, GSM::Time(3,0),
|
||||||
|
radio, config->rssi_offset);
|
||||||
if (!trx->init(config->filler, config->rtsc,
|
if (!trx->init(config->filler, config->rtsc,
|
||||||
config->rach_delay, config->edge)) {
|
config->rach_delay, config->edge)) {
|
||||||
LOG(ALERT) << "Failed to initialize transceiver";
|
LOG(ALERT) << "Failed to initialize transceiver";
|
||||||
@@ -246,6 +251,7 @@ static void print_help()
|
|||||||
" -a UHD device args\n"
|
" -a UHD device args\n"
|
||||||
" -l Logging level (%s)\n"
|
" -l Logging level (%s)\n"
|
||||||
" -i IP address of GSM core\n"
|
" -i IP address of GSM core\n"
|
||||||
|
" -j IP address of osmo-trx\n"
|
||||||
" -p Base port number\n"
|
" -p Base port number\n"
|
||||||
" -e Enable EDGE receiver\n"
|
" -e Enable EDGE receiver\n"
|
||||||
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
|
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
|
||||||
@@ -256,11 +262,11 @@ static void print_help()
|
|||||||
" -c Number of ARFCN channels (default=1)\n"
|
" -c Number of ARFCN channels (default=1)\n"
|
||||||
" -f Enable C0 filler table\n"
|
" -f Enable C0 filler table\n"
|
||||||
" -o Set baseband frequency offset (default=auto)\n"
|
" -o Set baseband frequency offset (default=auto)\n"
|
||||||
" -r Random GMSK Normal Burst test mode with given TSC\n"
|
" -r Random Normal Burst test mode with TSC\n"
|
||||||
" -E Random 8-PSK Normal Burst test mode with given TSC\n"
|
|
||||||
" -A Random Access Burst test mode with delay\n"
|
" -A Random Access Burst test mode with delay\n"
|
||||||
" -R RSSI to dBm offset in dB (default=0)\n"
|
" -R RSSI to dBm offset in dB (default=0)\n"
|
||||||
" -S Swap channels (UmTRX only)\n",
|
" -S Swap channels (UmTRX only)\n"
|
||||||
|
" -t SCHED_RR real-time priority (1..32)\n",
|
||||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +275,8 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
int option;
|
int option;
|
||||||
|
|
||||||
config->log_level = "NOTICE";
|
config->log_level = "NOTICE";
|
||||||
config->addr = DEFAULT_TRX_IP;
|
config->local_addr = DEFAULT_TRX_IP;
|
||||||
|
config->remote_addr = DEFAULT_TRX_IP;
|
||||||
config->port = DEFAULT_TRX_PORT;
|
config->port = DEFAULT_TRX_PORT;
|
||||||
config->tx_sps = DEFAULT_TX_SPS;
|
config->tx_sps = DEFAULT_TX_SPS;
|
||||||
config->rx_sps = DEFAULT_RX_SPS;
|
config->rx_sps = DEFAULT_RX_SPS;
|
||||||
@@ -284,8 +291,9 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
config->rssi_offset = 0.0;
|
config->rssi_offset = 0.0;
|
||||||
config->swap_channels = false;
|
config->swap_channels = false;
|
||||||
config->edge = false;
|
config->edge = false;
|
||||||
|
config->sched_rr = -1;
|
||||||
|
|
||||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dmxgfo:s:b:r:E:A:R:Se")) != -1) {
|
while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:")) != -1) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case 'h':
|
case 'h':
|
||||||
print_help();
|
print_help();
|
||||||
@@ -298,7 +306,10 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
config->log_level = optarg;
|
config->log_level = optarg;
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
config->addr = optarg;
|
config->remote_addr = optarg;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
config->local_addr = optarg;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
config->port = atoi(optarg);
|
config->port = atoi(optarg);
|
||||||
@@ -331,10 +342,6 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
config->rtsc = atoi(optarg);
|
config->rtsc = atoi(optarg);
|
||||||
config->filler = Transceiver::FILLER_NORM_RAND;
|
config->filler = Transceiver::FILLER_NORM_RAND;
|
||||||
break;
|
break;
|
||||||
case 'E':
|
|
||||||
config->rtsc = atoi(optarg);
|
|
||||||
config->filler = Transceiver::FILLER_EDGE_RAND;
|
|
||||||
break;
|
|
||||||
case 'A':
|
case 'A':
|
||||||
config->rach_delay = atoi(optarg);
|
config->rach_delay = atoi(optarg);
|
||||||
config->filler = Transceiver::FILLER_ACCESS_RAND;
|
config->filler = Transceiver::FILLER_ACCESS_RAND;
|
||||||
@@ -348,6 +355,9 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
case 'e':
|
case 'e':
|
||||||
config->edge = true;
|
config->edge = true;
|
||||||
break;
|
break;
|
||||||
|
case 't':
|
||||||
|
config->sched_rr = atoi(optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print_help();
|
print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -365,10 +375,8 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
goto bad_config;
|
goto bad_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->edge && (config->filler == Transceiver::FILLER_EDGE_RAND)) {
|
if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND))
|
||||||
printf("Can't enable EDGE filler when EDGE mode is disabled\n\n");
|
config->filler = Transceiver::FILLER_EDGE_RAND;
|
||||||
goto bad_config;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((config->tx_sps != 1) && (config->tx_sps != 4) &&
|
if ((config->tx_sps != 1) && (config->tx_sps != 4) &&
|
||||||
(config->rx_sps != 1) && (config->rx_sps != 4)) {
|
(config->rx_sps != 1) && (config->rx_sps != 4)) {
|
||||||
@@ -393,6 +401,21 @@ bad_config:
|
|||||||
exit(0);
|
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 main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int type, chans, ref;
|
int type, chans, ref;
|
||||||
@@ -404,18 +427,26 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
#ifdef HAVE_SSE3
|
#ifdef HAVE_SSE3
|
||||||
printf("Info: SSE3 support compiled in");
|
printf("Info: SSE3 support compiled in");
|
||||||
|
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||||
if (__builtin_cpu_supports("sse3"))
|
if (__builtin_cpu_supports("sse3"))
|
||||||
printf(" and supported by CPU\n");
|
printf(" and supported by CPU\n");
|
||||||
else
|
else
|
||||||
printf(", but not supported by CPU\n");
|
printf(", but not supported by CPU\n");
|
||||||
|
#else
|
||||||
|
printf(", but runtime SIMD detection disabled\n");
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SSE4_1
|
#ifdef HAVE_SSE4_1
|
||||||
printf("Info: SSE4.1 support compiled in");
|
printf("Info: SSE4.1 support compiled in");
|
||||||
|
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||||
if (__builtin_cpu_supports("sse4.1"))
|
if (__builtin_cpu_supports("sse4.1"))
|
||||||
printf(" and supported by CPU\n");
|
printf(" and supported by CPU\n");
|
||||||
else
|
else
|
||||||
printf(", but not supported by CPU\n");
|
printf(", but not supported by CPU\n");
|
||||||
|
#else
|
||||||
|
printf(", but runtime SIMD detection disabled\n");
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
convolve_init();
|
convolve_init();
|
||||||
@@ -423,6 +454,11 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
handle_options(argc, argv, &config);
|
handle_options(argc, argv, &config);
|
||||||
|
|
||||||
|
if (config.sched_rr != -1) {
|
||||||
|
if (set_sched_rr(config.sched_rr) < 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
setup_signal_handlers();
|
setup_signal_handlers();
|
||||||
|
|
||||||
/* Check database sanity */
|
/* Check database sanity */
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "radioInterface.h"
|
#include "radioInterface.h"
|
||||||
#include "Resampler.h"
|
#include "Resampler.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
#include <PRBS.h>
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "convert.h"
|
#include "convert.h"
|
||||||
|
|||||||
@@ -46,14 +46,10 @@ using namespace GSM;
|
|||||||
#define CLIP_THRESH 30000.0f
|
#define CLIP_THRESH 30000.0f
|
||||||
|
|
||||||
/** Lookup tables for trigonometric approximation */
|
/** Lookup tables for trigonometric approximation */
|
||||||
float cosTable[TABLESIZE+1]; // add 1 element for wrap around
|
static float sincTable[TABLESIZE+1]; // add 1 element for wrap around
|
||||||
float sinTable[TABLESIZE+1];
|
|
||||||
float sincTable[TABLESIZE+1];
|
|
||||||
|
|
||||||
/** Constants */
|
/** Constants */
|
||||||
static const float M_PI_F = (float)M_PI;
|
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 */
|
/* Precomputed rotation vectors */
|
||||||
static signalVector *GMSKRotation4 = NULL;
|
static signalVector *GMSKRotation4 = NULL;
|
||||||
@@ -64,7 +60,7 @@ static signalVector *GMSKReverseRotation1 = NULL;
|
|||||||
/* Precomputed fractional delay filters */
|
/* Precomputed fractional delay filters */
|
||||||
static signalVector *delayFilters[DELAYFILTS];
|
static signalVector *delayFilters[DELAYFILTS];
|
||||||
|
|
||||||
static Complex<float> psk8_table[8] = {
|
static const Complex<float> psk8_table[8] = {
|
||||||
Complex<float>(-0.70710678, 0.70710678),
|
Complex<float>(-0.70710678, 0.70710678),
|
||||||
Complex<float>( 0.0, -1.0),
|
Complex<float>( 0.0, -1.0),
|
||||||
Complex<float>( 0.0, 1.0),
|
Complex<float>( 0.0, 1.0),
|
||||||
@@ -172,67 +168,7 @@ void sigProcLibDestroy()
|
|||||||
GSMPulse4 = NULL;
|
GSMPulse4 = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dB relative to 1.0.
|
static float vectorNorm2(const signalVector &x)
|
||||||
// if > 1.0, then return 0 dB
|
|
||||||
float dB(float x) {
|
|
||||||
|
|
||||||
float arg = 1.0F;
|
|
||||||
float dB = 0.0F;
|
|
||||||
|
|
||||||
if (x >= 1.0F) return 0.0F;
|
|
||||||
if (x <= 0.0F) return -200.0F;
|
|
||||||
|
|
||||||
float prevArg = arg;
|
|
||||||
float prevdB = dB;
|
|
||||||
float stepSize = 16.0F;
|
|
||||||
float dBstepSize = 12.0F;
|
|
||||||
while (stepSize > 1.0F) {
|
|
||||||
do {
|
|
||||||
prevArg = arg;
|
|
||||||
prevdB = dB;
|
|
||||||
arg /= stepSize;
|
|
||||||
dB -= dBstepSize;
|
|
||||||
} while (arg > x);
|
|
||||||
arg = prevArg;
|
|
||||||
dB = prevdB;
|
|
||||||
stepSize *= 0.5F;
|
|
||||||
dBstepSize -= 3.0F;
|
|
||||||
}
|
|
||||||
return ((arg-x)*(dB-3.0F) + (x-arg*0.5F)*dB)/(arg - arg*0.5F);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10^(-dB/10), inverse of dB func.
|
|
||||||
float dBinv(float x) {
|
|
||||||
|
|
||||||
float arg = 1.0F;
|
|
||||||
float dB = 0.0F;
|
|
||||||
|
|
||||||
if (x >= 0.0F) return 1.0F;
|
|
||||||
if (x <= -200.0F) return 0.0F;
|
|
||||||
|
|
||||||
float prevArg = arg;
|
|
||||||
float prevdB = dB;
|
|
||||||
float stepSize = 16.0F;
|
|
||||||
float dBstepSize = 12.0F;
|
|
||||||
while (stepSize > 1.0F) {
|
|
||||||
do {
|
|
||||||
prevArg = arg;
|
|
||||||
prevdB = dB;
|
|
||||||
arg /= stepSize;
|
|
||||||
dB -= dBstepSize;
|
|
||||||
} while (dB > x);
|
|
||||||
arg = prevArg;
|
|
||||||
dB = prevdB;
|
|
||||||
stepSize *= 0.5F;
|
|
||||||
dBstepSize -= 3.0F;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((dB-x)*(arg*0.5F)+(x-(dB-3.0F))*(arg))/3.0F;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
float vectorNorm2(const signalVector &x)
|
|
||||||
{
|
{
|
||||||
signalVector::const_iterator xPtr = x.begin();
|
signalVector::const_iterator xPtr = x.begin();
|
||||||
float Energy = 0.0;
|
float Energy = 0.0;
|
||||||
@@ -242,64 +178,6 @@ float vectorNorm2(const signalVector &x)
|
|||||||
return Energy;
|
return Energy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float vectorPower(const signalVector &x)
|
|
||||||
{
|
|
||||||
return vectorNorm2(x)/x.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** compute cosine via lookup table */
|
|
||||||
float cosLookup(const 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 iDelta*cosTable[argI] + delta*cosTable[argI+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** compute sine via lookup table */
|
|
||||||
float sinLookup(const 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 iDelta*sinTable[argI] + delta*sinTable[argI+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** 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
|
* Initialize 4 sps and 1 sps rotation tables
|
||||||
*/
|
*/
|
||||||
@@ -311,11 +189,11 @@ static void initGMSKRotationTables()
|
|||||||
GMSKReverseRotation4 = new signalVector(len4);
|
GMSKReverseRotation4 = new signalVector(len4);
|
||||||
signalVector::iterator rotPtr = GMSKRotation4->begin();
|
signalVector::iterator rotPtr = GMSKRotation4->begin();
|
||||||
signalVector::iterator revPtr = GMSKReverseRotation4->begin();
|
signalVector::iterator revPtr = GMSKReverseRotation4->begin();
|
||||||
float phase = 0.0;
|
auto phase = 0.0;
|
||||||
while (rotPtr != GMSKRotation4->end()) {
|
while (rotPtr != GMSKRotation4->end()) {
|
||||||
*rotPtr++ = expjLookup(phase);
|
*rotPtr++ = complex(cos(phase), sin(phase));
|
||||||
*revPtr++ = expjLookup(-phase);
|
*revPtr++ = complex(cos(-phase), sin(-phase));
|
||||||
phase += M_PI_F / 2.0F / 4.0;
|
phase += M_PI / 2.0 / 4.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GMSKRotation1 = new signalVector(len1);
|
GMSKRotation1 = new signalVector(len1);
|
||||||
@@ -324,9 +202,9 @@ static void initGMSKRotationTables()
|
|||||||
revPtr = GMSKReverseRotation1->begin();
|
revPtr = GMSKReverseRotation1->begin();
|
||||||
phase = 0.0;
|
phase = 0.0;
|
||||||
while (rotPtr != GMSKRotation1->end()) {
|
while (rotPtr != GMSKRotation1->end()) {
|
||||||
*rotPtr++ = expjLookup(phase);
|
*rotPtr++ = complex(cos(phase), sin(phase));
|
||||||
*revPtr++ = expjLookup(-phase);
|
*revPtr++ = complex(cos(-phase), sin(-phase));
|
||||||
phase += M_PI_F / 2.0F;
|
phase += M_PI / 2.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,11 +279,18 @@ static bool GMSKReverseRotate(signalVector &x, int sps)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
signalVector *convolve(const signalVector *x,
|
/** Convolution type indicator */
|
||||||
const signalVector *h,
|
enum ConvType {
|
||||||
signalVector *y,
|
START_ONLY,
|
||||||
ConvType spanType, size_t start,
|
NO_DELAY,
|
||||||
size_t len, size_t step, int offset)
|
CUSTOM,
|
||||||
|
UNDEFINED,
|
||||||
|
};
|
||||||
|
|
||||||
|
static signalVector *convolve(const signalVector *x, const signalVector *h,
|
||||||
|
signalVector *y, ConvType spanType,
|
||||||
|
size_t start = 0, size_t len = 0,
|
||||||
|
size_t step = 1, int offset = 0)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
size_t head = 0, tail = 0;
|
size_t head = 0, tail = 0;
|
||||||
@@ -654,29 +539,6 @@ static PulseSequence *generateGSMPulse(int sps)
|
|||||||
return pulse;
|
return pulse;
|
||||||
}
|
}
|
||||||
|
|
||||||
signalVector* reverseConjugate(signalVector *b)
|
|
||||||
{
|
|
||||||
signalVector *tmp = new signalVector(b->size());
|
|
||||||
tmp->isReal(b->isReal());
|
|
||||||
signalVector::iterator bP = b->begin();
|
|
||||||
signalVector::iterator bPEnd = b->end();
|
|
||||||
signalVector::iterator tmpP = tmp->end()-1;
|
|
||||||
if (!b->isReal()) {
|
|
||||||
while (bP < bPEnd) {
|
|
||||||
*tmpP-- = bP->conj();
|
|
||||||
bP++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
while (bP < bPEnd) {
|
|
||||||
*tmpP-- = bP->real();
|
|
||||||
bP++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool vectorSlicer(SoftVector *x)
|
bool vectorSlicer(SoftVector *x)
|
||||||
{
|
{
|
||||||
SoftVector::iterator xP = x->begin();
|
SoftVector::iterator xP = x->begin();
|
||||||
@@ -696,7 +558,7 @@ static signalVector *rotateBurst(const BitVector &wBurst,
|
|||||||
int guardPeriodLength, int sps)
|
int guardPeriodLength, int sps)
|
||||||
{
|
{
|
||||||
int burst_len;
|
int burst_len;
|
||||||
signalVector *pulse, rotated, *shaped;
|
signalVector *pulse, rotated;
|
||||||
signalVector::iterator itr;
|
signalVector::iterator itr;
|
||||||
|
|
||||||
pulse = GSMPulse1->empty;
|
pulse = GSMPulse1->empty;
|
||||||
@@ -713,11 +575,7 @@ static signalVector *rotateBurst(const BitVector &wBurst,
|
|||||||
rotated.isReal(false);
|
rotated.isReal(false);
|
||||||
|
|
||||||
/* Dummy filter operation */
|
/* Dummy filter operation */
|
||||||
shaped = convolve(&rotated, pulse, NULL, START_ONLY);
|
return convolve(&rotated, pulse, NULL, START_ONLY);
|
||||||
if (!shaped)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return shaped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rotateBurst2(signalVector &burst, double phase)
|
static void rotateBurst2(signalVector &burst, double phase)
|
||||||
@@ -737,8 +595,7 @@ static signalVector *modulateBurstLaurent(const BitVector &bits)
|
|||||||
{
|
{
|
||||||
int burst_len, sps = 4;
|
int burst_len, sps = 4;
|
||||||
float phase;
|
float phase;
|
||||||
signalVector *c0_pulse, *c1_pulse, *c0_burst;
|
signalVector *c0_pulse, *c1_pulse, *c0_shaped, *c1_shaped;
|
||||||
signalVector *c1_burst, *c0_shaped, *c1_shaped;
|
|
||||||
signalVector::iterator c0_itr, c1_itr;
|
signalVector::iterator c0_itr, c1_itr;
|
||||||
|
|
||||||
c0_pulse = GSMPulse4->c0;
|
c0_pulse = GSMPulse4->c0;
|
||||||
@@ -749,13 +606,12 @@ static signalVector *modulateBurstLaurent(const BitVector &bits)
|
|||||||
|
|
||||||
burst_len = 625;
|
burst_len = 625;
|
||||||
|
|
||||||
c0_burst = new signalVector(burst_len, c0_pulse->size());
|
signalVector c0_burst(burst_len, c0_pulse->size());
|
||||||
c0_burst->isReal(true);
|
c0_burst.isReal(true);
|
||||||
c0_itr = c0_burst->begin();
|
c0_itr = c0_burst.begin();
|
||||||
|
|
||||||
c1_burst = new signalVector(burst_len, c1_pulse->size());
|
signalVector c1_burst(burst_len, c1_pulse->size());
|
||||||
c1_burst->isReal(true);
|
c1_itr = c1_burst.begin();
|
||||||
c1_itr = c1_burst->begin();
|
|
||||||
|
|
||||||
/* Padded differential tail bits */
|
/* Padded differential tail bits */
|
||||||
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
||||||
@@ -771,10 +627,10 @@ static signalVector *modulateBurstLaurent(const BitVector &bits)
|
|||||||
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
||||||
|
|
||||||
/* Generate C0 phase coefficients */
|
/* Generate C0 phase coefficients */
|
||||||
GMSKRotate(*c0_burst, sps);
|
GMSKRotate(c0_burst, sps);
|
||||||
c0_burst->isReal(false);
|
c0_burst.isReal(false);
|
||||||
|
|
||||||
c0_itr = c0_burst->begin();
|
c0_itr = c0_burst.begin();
|
||||||
c0_itr += sps * 2;
|
c0_itr += sps * 2;
|
||||||
c1_itr += sps * 2;
|
c1_itr += sps * 2;
|
||||||
|
|
||||||
@@ -799,8 +655,8 @@ static signalVector *modulateBurstLaurent(const BitVector &bits)
|
|||||||
*c1_itr = *c0_itr * Complex<float>(0, phase);
|
*c1_itr = *c0_itr * Complex<float>(0, phase);
|
||||||
|
|
||||||
/* Primary (C0) and secondary (C1) pulse shaping */
|
/* Primary (C0) and secondary (C1) pulse shaping */
|
||||||
c0_shaped = convolve(c0_burst, c0_pulse, NULL, START_ONLY);
|
c0_shaped = convolve(&c0_burst, c0_pulse, NULL, START_ONLY);
|
||||||
c1_shaped = convolve(c1_burst, c1_pulse, NULL, START_ONLY);
|
c1_shaped = convolve(&c1_burst, c1_pulse, NULL, START_ONLY);
|
||||||
|
|
||||||
/* Sum shaped outputs into C0 */
|
/* Sum shaped outputs into C0 */
|
||||||
c0_itr = c0_shaped->begin();
|
c0_itr = c0_shaped->begin();
|
||||||
@@ -808,10 +664,7 @@ static signalVector *modulateBurstLaurent(const BitVector &bits)
|
|||||||
for (unsigned i = 0; i < c0_shaped->size(); i++ )
|
for (unsigned i = 0; i < c0_shaped->size(); i++ )
|
||||||
*c0_itr++ += *c1_itr++;
|
*c0_itr++ += *c1_itr++;
|
||||||
|
|
||||||
delete c0_burst;
|
|
||||||
delete c1_burst;
|
|
||||||
delete c1_shaped;
|
delete c1_shaped;
|
||||||
|
|
||||||
return c0_shaped;
|
return c0_shaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -885,7 +738,6 @@ static signalVector *mapEdgeSymbols(const BitVector &bits)
|
|||||||
static signalVector *shapeEdgeBurst(const signalVector &symbols)
|
static signalVector *shapeEdgeBurst(const signalVector &symbols)
|
||||||
{
|
{
|
||||||
size_t nsyms, nsamps = 625, sps = 4;
|
size_t nsyms, nsamps = 625, sps = 4;
|
||||||
signalVector *burst, *shape;
|
|
||||||
signalVector::iterator burst_itr;
|
signalVector::iterator burst_itr;
|
||||||
|
|
||||||
nsyms = symbols.size();
|
nsyms = symbols.size();
|
||||||
@@ -893,10 +745,10 @@ static signalVector *shapeEdgeBurst(const signalVector &symbols)
|
|||||||
if (nsyms * sps > nsamps)
|
if (nsyms * sps > nsamps)
|
||||||
nsyms = 156;
|
nsyms = 156;
|
||||||
|
|
||||||
burst = new signalVector(nsamps, GSMPulse4->c0->size());
|
signalVector burst(nsamps, GSMPulse4->c0->size());
|
||||||
|
|
||||||
/* Delay burst by 1 symbol */
|
/* Delay burst by 1 symbol */
|
||||||
burst_itr = burst->begin() + sps;
|
burst_itr = burst.begin() + sps;
|
||||||
for (size_t i = 0; i < nsyms; i++) {
|
for (size_t i = 0; i < nsyms; i++) {
|
||||||
float phase = i * 3.0f * M_PI / 8.0f;
|
float phase = i * 3.0f * M_PI / 8.0f;
|
||||||
Complex<float> rot = Complex<float>(cos(phase), sin(phase));
|
Complex<float> rot = Complex<float>(cos(phase), sin(phase));
|
||||||
@@ -906,16 +758,13 @@ static signalVector *shapeEdgeBurst(const signalVector &symbols)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Single Gaussian pulse approximation shaping */
|
/* Single Gaussian pulse approximation shaping */
|
||||||
shape = convolve(burst, GSMPulse4->c0, NULL, START_ONLY);
|
return convolve(&burst, GSMPulse4->c0, NULL, START_ONLY);
|
||||||
delete burst;
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate a random GSM normal burst.
|
* Generate a random GSM normal burst.
|
||||||
*/
|
*/
|
||||||
signalVector *genRandNormalBurst(int tsc, int sps, int tn, PRBS &prbs)
|
signalVector *genRandNormalBurst(int tsc, int sps, int tn)
|
||||||
{
|
{
|
||||||
if ((tsc < 0) || (tsc > 7) || (tn < 0) || (tn > 7))
|
if ((tsc < 0) || (tsc > 7) || (tn < 0) || (tn > 7))
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -923,40 +772,36 @@ signalVector *genRandNormalBurst(int tsc, int sps, int tn, PRBS &prbs)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
BitVector *bits = new BitVector(148);
|
BitVector bits(148);
|
||||||
signalVector *burst;
|
|
||||||
|
|
||||||
/* Tail bits */
|
/* Tail bits */
|
||||||
for (; i < 3; i++)
|
for (; i < 3; i++)
|
||||||
(*bits)[i] = 0;
|
bits[i] = 0;
|
||||||
|
|
||||||
/* Random bits */
|
/* Random bits */
|
||||||
for (; i < 60; i++)
|
for (; i < 60; i++)
|
||||||
(*bits)[i] = prbs.generateBit();
|
bits[i] = rand() % 2;
|
||||||
|
|
||||||
/* Stealing bit */
|
/* Stealing bit */
|
||||||
(*bits)[i++] = 0;
|
bits[i++] = 0;
|
||||||
|
|
||||||
/* Training sequence */
|
/* Training sequence */
|
||||||
for (int n = 0; i < 87; i++, n++)
|
for (int n = 0; i < 87; i++, n++)
|
||||||
(*bits)[i] = gTrainingSequence[tsc][n];
|
bits[i] = gTrainingSequence[tsc][n];
|
||||||
|
|
||||||
/* Stealing bit */
|
/* Stealing bit */
|
||||||
(*bits)[i++] = 0;
|
bits[i++] = 0;
|
||||||
|
|
||||||
/* Random bits */
|
/* Random bits */
|
||||||
for (; i < 145; i++)
|
for (; i < 145; i++)
|
||||||
(*bits)[i] = prbs.generateBit();
|
bits[i] = rand() % 2;
|
||||||
|
|
||||||
/* Tail bits */
|
/* Tail bits */
|
||||||
for (; i < 148; i++)
|
for (; i < 148; i++)
|
||||||
(*bits)[i] = 0;
|
bits[i] = 0;
|
||||||
|
|
||||||
int guard = 8 + !(tn % 4);
|
int guard = 8 + !(tn % 4);
|
||||||
burst = modulateBurst(*bits, guard, sps);
|
return modulateBurst(bits, guard, sps);
|
||||||
delete bits;
|
|
||||||
|
|
||||||
return burst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -972,30 +817,26 @@ signalVector *genRandAccessBurst(int delay, int sps, int tn)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
BitVector *bits = new BitVector(88+delay);
|
BitVector bits(88 + delay);
|
||||||
signalVector *burst;
|
|
||||||
|
|
||||||
/* delay */
|
/* delay */
|
||||||
for (; i < delay; i++)
|
for (; i < delay; i++)
|
||||||
(*bits)[i] = 0;
|
bits[i] = 0;
|
||||||
|
|
||||||
/* head and synch bits */
|
/* head and synch bits */
|
||||||
for (int n = 0; i < 49+delay; i++, n++)
|
for (int n = 0; i < 49+delay; i++, n++)
|
||||||
(*bits)[i] = gRACHBurst[n];
|
bits[i] = gRACHBurst[n];
|
||||||
|
|
||||||
/* Random bits */
|
/* Random bits */
|
||||||
for (; i < 85+delay; i++)
|
for (; i < 85+delay; i++)
|
||||||
(*bits)[i] = rand() % 2;
|
bits[i] = rand() % 2;
|
||||||
|
|
||||||
/* Tail bits */
|
/* Tail bits */
|
||||||
for (; i < 88+delay; i++)
|
for (; i < 88+delay; i++)
|
||||||
(*bits)[i] = 0;
|
bits[i] = 0;
|
||||||
|
|
||||||
int guard = 68-delay + !(tn % 4);
|
int guard = 68-delay + !(tn % 4);
|
||||||
burst = modulateBurst(*bits, guard, sps);
|
return modulateBurst(bits, guard, sps);
|
||||||
delete bits;
|
|
||||||
|
|
||||||
return burst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
signalVector *generateEmptyBurst(int sps, int tn)
|
signalVector *generateEmptyBurst(int sps, int tn)
|
||||||
@@ -1032,17 +873,17 @@ signalVector *generateEdgeBurst(int tsc)
|
|||||||
if ((tsc < 0) || (tsc > 7))
|
if ((tsc < 0) || (tsc > 7))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
signalVector *shape, *burst = new signalVector(148);
|
signalVector burst(148);
|
||||||
const BitVector *midamble = &gEdgeTrainingSequence[tsc];
|
const BitVector *midamble = &gEdgeTrainingSequence[tsc];
|
||||||
|
|
||||||
/* Tail */
|
/* Tail */
|
||||||
int n, i = 0;
|
int n, i = 0;
|
||||||
for (; i < tail; i++)
|
for (; i < tail; i++)
|
||||||
(*burst)[i] = psk8_table[7];
|
burst[i] = psk8_table[7];
|
||||||
|
|
||||||
/* Body */
|
/* Body */
|
||||||
for (; i < tail + data; i++)
|
for (; i < tail + data; i++)
|
||||||
(*burst)[i] = psk8_table[rand() % 8];
|
burst[i] = psk8_table[rand() % 8];
|
||||||
|
|
||||||
/* TSC */
|
/* TSC */
|
||||||
for (n = 0; i < tail + data + train; i++, n++) {
|
for (n = 0; i < tail + data + train; i++, n++) {
|
||||||
@@ -1050,21 +891,18 @@ signalVector *generateEdgeBurst(int tsc)
|
|||||||
(((unsigned) (*midamble)[3 * n + 1] & 0x01) << 1) |
|
(((unsigned) (*midamble)[3 * n + 1] & 0x01) << 1) |
|
||||||
(((unsigned) (*midamble)[3 * n + 2] & 0x01) << 2);
|
(((unsigned) (*midamble)[3 * n + 2] & 0x01) << 2);
|
||||||
|
|
||||||
(*burst)[i] = psk8_table[index];
|
burst[i] = psk8_table[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Body */
|
/* Body */
|
||||||
for (; i < tail + data + train + data; i++)
|
for (; i < tail + data + train + data; i++)
|
||||||
(*burst)[i] = psk8_table[rand() % 8];
|
burst[i] = psk8_table[rand() % 8];
|
||||||
|
|
||||||
/* Tail */
|
/* Tail */
|
||||||
for (; i < tail + data + train + data + tail; i++)
|
for (; i < tail + data + train + data + tail; i++)
|
||||||
(*burst)[i] = psk8_table[7];
|
burst[i] = psk8_table[7];
|
||||||
|
|
||||||
shape = shapeEdgeBurst(*burst);
|
return shapeEdgeBurst(burst);
|
||||||
delete burst;
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1100,7 +938,7 @@ static signalVector *modulateBurstBasic(const BitVector &bits,
|
|||||||
int guard_len, int sps)
|
int guard_len, int sps)
|
||||||
{
|
{
|
||||||
int burst_len;
|
int burst_len;
|
||||||
signalVector *pulse, *burst, *shaped;
|
signalVector *pulse;
|
||||||
signalVector::iterator burst_itr;
|
signalVector::iterator burst_itr;
|
||||||
|
|
||||||
if (sps == 1)
|
if (sps == 1)
|
||||||
@@ -1110,9 +948,9 @@ static signalVector *modulateBurstBasic(const BitVector &bits,
|
|||||||
|
|
||||||
burst_len = sps * (bits.size() + guard_len);
|
burst_len = sps * (bits.size() + guard_len);
|
||||||
|
|
||||||
burst = new signalVector(burst_len, pulse->size());
|
signalVector burst(burst_len, pulse->size());
|
||||||
burst->isReal(true);
|
burst.isReal(true);
|
||||||
burst_itr = burst->begin();
|
burst_itr = burst.begin();
|
||||||
|
|
||||||
/* Raw bits are not differentially encoded */
|
/* Raw bits are not differentially encoded */
|
||||||
for (unsigned i = 0; i < bits.size(); i++) {
|
for (unsigned i = 0; i < bits.size(); i++) {
|
||||||
@@ -1120,15 +958,11 @@ static signalVector *modulateBurstBasic(const BitVector &bits,
|
|||||||
burst_itr += sps;
|
burst_itr += sps;
|
||||||
}
|
}
|
||||||
|
|
||||||
GMSKRotate(*burst, sps);
|
GMSKRotate(burst, sps);
|
||||||
burst->isReal(false);
|
burst.isReal(false);
|
||||||
|
|
||||||
/* Single Gaussian pulse approximation shaping */
|
/* Single Gaussian pulse approximation shaping */
|
||||||
shaped = convolve(burst, pulse, NULL, START_ONLY);
|
return convolve(&burst, pulse, NULL, START_ONLY);
|
||||||
|
|
||||||
delete burst;
|
|
||||||
|
|
||||||
return shaped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assume input bits are not differentially encoded */
|
/* Assume input bits are not differentially encoded */
|
||||||
@@ -1145,20 +979,14 @@ signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength,
|
|||||||
|
|
||||||
static void generateSincTable()
|
static void generateSincTable()
|
||||||
{
|
{
|
||||||
float x;
|
|
||||||
|
|
||||||
for (int i = 0; i < TABLESIZE; i++) {
|
for (int i = 0; i < TABLESIZE; i++) {
|
||||||
x = (float) i / TABLESIZE * 8 * M_PI;
|
auto x = (double) i / TABLESIZE * 8 * M_PI;
|
||||||
if (fabs(x) < 0.01) {
|
auto y = sin(x) / x;
|
||||||
sincTable[i] = 1.0f;
|
sincTable[i] = std::isnan(y) ? 1.0 : y;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sincTable[i] = sinf(x) / x;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float sinc(float x)
|
static float sinc(float x)
|
||||||
{
|
{
|
||||||
if (fabs(x) >= 8 * M_PI)
|
if (fabs(x) >= 8 * M_PI)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
@@ -1173,7 +1001,7 @@ float sinc(float x)
|
|||||||
* sinc function generator. The number of filters generated is specified
|
* sinc function generator. The number of filters generated is specified
|
||||||
* by the DELAYFILTS value.
|
* by the DELAYFILTS value.
|
||||||
*/
|
*/
|
||||||
void generateDelayFilters()
|
static void generateDelayFilters()
|
||||||
{
|
{
|
||||||
int h_len = 20;
|
int h_len = 20;
|
||||||
complex *data;
|
complex *data;
|
||||||
@@ -1268,31 +1096,8 @@ signalVector *delayVector(const signalVector *in, signalVector *out, float delay
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
signalVector *gaussianNoise(int length,
|
static complex interpolatePoint(const signalVector &inSig, float ix)
|
||||||
float variance,
|
|
||||||
complex mean)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
signalVector *noise = new signalVector(length);
|
|
||||||
signalVector::iterator nPtr = noise->begin();
|
|
||||||
float stddev = sqrtf(variance);
|
|
||||||
while (nPtr < noise->end()) {
|
|
||||||
float u1 = (float) rand()/ (float) RAND_MAX;
|
|
||||||
while (u1==0.0)
|
|
||||||
u1 = (float) rand()/ (float) RAND_MAX;
|
|
||||||
float u2 = (float) rand()/ (float) RAND_MAX;
|
|
||||||
float arg = 2.0*M_PI*u2;
|
|
||||||
*nPtr = mean + stddev*complex(cos(arg),sin(arg))*sqrtf(-2.0*log(u1));
|
|
||||||
nPtr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return noise;
|
|
||||||
}
|
|
||||||
|
|
||||||
complex interpolatePoint(const signalVector &inSig,
|
|
||||||
float ix)
|
|
||||||
{
|
|
||||||
|
|
||||||
int start = (int) (floor(ix) - 10);
|
int start = (int) (floor(ix) - 10);
|
||||||
if (start < 0) start = 0;
|
if (start < 0) start = 0;
|
||||||
int end = (int) (floor(ix) + 11);
|
int end = (int) (floor(ix) + 11);
|
||||||
@@ -1332,12 +1137,9 @@ static complex fastPeakDetect(const signalVector &rxBurst, float *index)
|
|||||||
return amp;
|
return amp;
|
||||||
}
|
}
|
||||||
|
|
||||||
complex peakDetect(const signalVector &rxBurst,
|
static complex peakDetect(const signalVector &rxBurst,
|
||||||
float *peakIndex,
|
float *peakIndex, float *avgPwr)
|
||||||
float *avgPwr)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
complex maxVal = 0.0;
|
complex maxVal = 0.0;
|
||||||
float maxIndex = -1;
|
float maxIndex = -1;
|
||||||
float sumPower = 0.0;
|
float sumPower = 0.0;
|
||||||
@@ -1410,7 +1212,7 @@ void scaleVector(signalVector &x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** in-place conjugation */
|
/** in-place conjugation */
|
||||||
void conjugateVector(signalVector &x)
|
static void conjugateVector(signalVector &x)
|
||||||
{
|
{
|
||||||
if (x.isReal()) return;
|
if (x.isReal()) return;
|
||||||
signalVector::iterator xP = x.begin();
|
signalVector::iterator xP = x.begin();
|
||||||
@@ -1421,37 +1223,6 @@ void conjugateVector(signalVector &x)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// in-place addition!!
|
|
||||||
bool addVector(signalVector &x,
|
|
||||||
signalVector &y)
|
|
||||||
{
|
|
||||||
signalVector::iterator xP = x.begin();
|
|
||||||
signalVector::iterator yP = y.begin();
|
|
||||||
signalVector::iterator xPEnd = x.end();
|
|
||||||
signalVector::iterator yPEnd = y.end();
|
|
||||||
while ((xP < xPEnd) && (yP < yPEnd)) {
|
|
||||||
*xP = *xP + *yP;
|
|
||||||
xP++; yP++;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// in-place multiplication!!
|
|
||||||
bool multVector(signalVector &x,
|
|
||||||
signalVector &y)
|
|
||||||
{
|
|
||||||
signalVector::iterator xP = x.begin();
|
|
||||||
signalVector::iterator yP = y.begin();
|
|
||||||
signalVector::iterator xPEnd = x.end();
|
|
||||||
signalVector::iterator yPEnd = y.end();
|
|
||||||
while ((xP < xPEnd) && (yP < yPEnd)) {
|
|
||||||
*xP = (*xP) * (*yP);
|
|
||||||
xP++; yP++;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool generateMidamble(int sps, int tsc)
|
static bool generateMidamble(int sps, int tsc)
|
||||||
{
|
{
|
||||||
bool status = true;
|
bool status = true;
|
||||||
@@ -1528,7 +1299,7 @@ release:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
CorrelationSequence *generateEdgeMidamble(int tsc)
|
static CorrelationSequence *generateEdgeMidamble(int tsc)
|
||||||
{
|
{
|
||||||
complex *data = NULL;
|
complex *data = NULL;
|
||||||
signalVector *midamble = NULL, *_midamble = NULL;
|
signalVector *midamble = NULL, *_midamble = NULL;
|
||||||
@@ -1682,6 +1453,21 @@ float energyDetect(const signalVector &rxBurst, unsigned windowLength)
|
|||||||
return energy/windowLength;
|
return energy/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));
|
||||||
|
|
||||||
|
if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
|
||||||
|
(float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
|
||||||
|
delete out;
|
||||||
|
out = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detect a burst based on correlation and peak-to-average ratio
|
* Detect a burst based on correlation and peak-to-average ratio
|
||||||
*
|
*
|
||||||
@@ -1771,7 +1557,6 @@ static int detectGeneralBurst(const signalVector &rxBurst,
|
|||||||
{
|
{
|
||||||
int rc, start, len;
|
int rc, start, len;
|
||||||
bool clipping = false;
|
bool clipping = false;
|
||||||
signalVector *corr;
|
|
||||||
|
|
||||||
if ((sps != 1) && (sps != 4))
|
if ((sps != 1) && (sps != 4))
|
||||||
return -SIGERR_UNSUPPORTED;
|
return -SIGERR_UNSUPPORTED;
|
||||||
@@ -1787,12 +1572,10 @@ static int detectGeneralBurst(const signalVector &rxBurst,
|
|||||||
|
|
||||||
start = target - head - 1;
|
start = target - head - 1;
|
||||||
len = head + tail;
|
len = head + tail;
|
||||||
corr = new signalVector(len);
|
signalVector corr(len);
|
||||||
|
|
||||||
rc = detectBurst(rxBurst, *corr, sync,
|
rc = detectBurst(rxBurst, corr, sync,
|
||||||
thresh, sps, &, &toa, start, len);
|
thresh, sps, &, &toa, start, len);
|
||||||
delete corr;
|
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
return -SIGERR_INTERNAL;
|
return -SIGERR_INTERNAL;
|
||||||
} else if (!rc) {
|
} else if (!rc) {
|
||||||
@@ -1816,12 +1599,8 @@ static int detectGeneralBurst(const signalVector &rxBurst,
|
|||||||
* head: Search 8 symbols before target
|
* head: Search 8 symbols before target
|
||||||
* tail: Search 8 symbols + maximum expected delay
|
* tail: Search 8 symbols + maximum expected delay
|
||||||
*/
|
*/
|
||||||
int detectRACHBurst(const signalVector &burst,
|
static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
|
||||||
float threshold,
|
complex &litude, float &toa, unsigned max_toa)
|
||||||
int sps,
|
|
||||||
complex &litude,
|
|
||||||
float &toa,
|
|
||||||
unsigned max_toa)
|
|
||||||
{
|
{
|
||||||
int rc, target, head, tail;
|
int rc, target, head, tail;
|
||||||
CorrelationSequence *sync;
|
CorrelationSequence *sync;
|
||||||
@@ -1845,7 +1624,7 @@ int detectRACHBurst(const signalVector &burst,
|
|||||||
* head: Search 6 symbols before target
|
* head: Search 6 symbols before target
|
||||||
* tail: Search 6 symbols + maximum expected delay
|
* tail: Search 6 symbols + maximum expected delay
|
||||||
*/
|
*/
|
||||||
int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold,
|
static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold,
|
||||||
int sps, complex &litude, float &toa, unsigned max_toa)
|
int sps, complex &litude, float &toa, unsigned max_toa)
|
||||||
{
|
{
|
||||||
int rc, target, head, tail;
|
int rc, target, head, tail;
|
||||||
@@ -1855,8 +1634,8 @@ int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold
|
|||||||
return -SIGERR_UNSUPPORTED;
|
return -SIGERR_UNSUPPORTED;
|
||||||
|
|
||||||
target = 3 + 58 + 16 + 5;
|
target = 3 + 58 + 16 + 5;
|
||||||
head = 3;
|
head = 6;
|
||||||
tail = 3 + max_toa;
|
tail = 6 + max_toa;
|
||||||
sync = gMidambles[tsc];
|
sync = gMidambles[tsc];
|
||||||
|
|
||||||
rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
|
rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
|
||||||
@@ -1864,7 +1643,7 @@ int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int detectEdgeBurst(const signalVector &burst, unsigned tsc, float threshold,
|
static int detectEdgeBurst(const signalVector &burst, unsigned tsc, float threshold,
|
||||||
int sps, complex &litude, float &toa, unsigned max_toa)
|
int sps, complex &litude, float &toa, unsigned max_toa)
|
||||||
{
|
{
|
||||||
int rc, target, head, tail;
|
int rc, target, head, tail;
|
||||||
@@ -1915,41 +1694,6 @@ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
signalVector *downsampleBurst(const signalVector &burst)
|
|
||||||
{
|
|
||||||
signalVector *in, *out;
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
signalVector *decimateVector(signalVector &wVector, size_t factor)
|
|
||||||
{
|
|
||||||
signalVector *dec;
|
|
||||||
|
|
||||||
if (factor <= 1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
dec = new signalVector(wVector.size() / factor);
|
|
||||||
dec->isReal(wVector.isReal());
|
|
||||||
|
|
||||||
signalVector::iterator itr = dec->begin();
|
|
||||||
for (size_t i = 0; i < wVector.size(); i += factor)
|
|
||||||
*itr++ = wVector[i];
|
|
||||||
|
|
||||||
return dec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Soft 8-PSK decoding using Manhattan distance metric
|
* Soft 8-PSK decoding using Manhattan distance metric
|
||||||
*/
|
*/
|
||||||
@@ -2046,8 +1790,8 @@ static signalVector *demodCommon(const signalVector &burst, int sps,
|
|||||||
* 4 SPS (if activated) to minimize distortion through the fractional
|
* 4 SPS (if activated) to minimize distortion through the fractional
|
||||||
* delay filters. Symbol rotation and after always operates at 1 SPS.
|
* delay filters. Symbol rotation and after always operates at 1 SPS.
|
||||||
*/
|
*/
|
||||||
SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps,
|
static SoftVector *demodGmskBurst(const signalVector &rxBurst,
|
||||||
complex channel, float TOA)
|
int sps, complex channel, float TOA)
|
||||||
{
|
{
|
||||||
SoftVector *bits;
|
SoftVector *bits;
|
||||||
signalVector *dec;
|
signalVector *dec;
|
||||||
@@ -2075,8 +1819,8 @@ SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps,
|
|||||||
* through the fractional delay filters at 1 SPS renders signal
|
* through the fractional delay filters at 1 SPS renders signal
|
||||||
* nearly unrecoverable.
|
* nearly unrecoverable.
|
||||||
*/
|
*/
|
||||||
SoftVector *demodEdgeBurst(const signalVector &burst, int sps,
|
static SoftVector *demodEdgeBurst(const signalVector &burst,
|
||||||
complex chan, float toa)
|
int sps, complex chan, float toa)
|
||||||
{
|
{
|
||||||
SoftVector *bits;
|
SoftVector *bits;
|
||||||
signalVector *dec, *rot, *eq;
|
signalVector *dec, *rot, *eq;
|
||||||
@@ -2110,7 +1854,6 @@ SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp,
|
|||||||
|
|
||||||
bool sigProcLibSetup()
|
bool sigProcLibSetup()
|
||||||
{
|
{
|
||||||
initTrigTables();
|
|
||||||
generateSincTable();
|
generateSincTable();
|
||||||
initGMSKRotationTables();
|
initGMSKRotationTables();
|
||||||
|
|
||||||
@@ -2137,26 +1880,3 @@ fail:
|
|||||||
sigProcLibDestroy();
|
sigProcLibDestroy();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string corrTypeToString(CorrType corr) {
|
|
||||||
switch (corr) {
|
|
||||||
case OFF:
|
|
||||||
return "OFF";
|
|
||||||
case TSC:
|
|
||||||
return "TSC";
|
|
||||||
case RACH:
|
|
||||||
return "RACH";
|
|
||||||
case EDGE:
|
|
||||||
return "EDGE";
|
|
||||||
case IDLE:
|
|
||||||
return "IDLE";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, CorrType corr)
|
|
||||||
{
|
|
||||||
os << corrTypeToString(corr);
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#include "Vector.h"
|
#include "Vector.h"
|
||||||
#include "Complex.h"
|
#include "Complex.h"
|
||||||
#include "BitVector.h"
|
#include "BitVector.h"
|
||||||
#include "PRBS.h"
|
|
||||||
#include "signalVector.h"
|
#include "signalVector.h"
|
||||||
|
|
||||||
/* Burst lengths */
|
/* Burst lengths */
|
||||||
@@ -26,14 +25,6 @@
|
|||||||
#define EDGE_BURST_NBITS 444
|
#define EDGE_BURST_NBITS 444
|
||||||
#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
|
#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
|
||||||
|
|
||||||
/** Convolution type indicator */
|
|
||||||
enum ConvType {
|
|
||||||
START_ONLY,
|
|
||||||
NO_DELAY,
|
|
||||||
CUSTOM,
|
|
||||||
UNDEFINED,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Codes for burst types of received bursts*/
|
/** Codes for burst types of received bursts*/
|
||||||
enum CorrType{
|
enum CorrType{
|
||||||
OFF, ///< timeslot is off
|
OFF, ///< timeslot is off
|
||||||
@@ -42,8 +33,6 @@ enum CorrType{
|
|||||||
EDGE, ///< timeslot should contain an EDGE burst
|
EDGE, ///< timeslot should contain an EDGE burst
|
||||||
IDLE ///< timeslot is an idle (or dummy) burst
|
IDLE ///< timeslot is an idle (or dummy) burst
|
||||||
};
|
};
|
||||||
std::string corrTypeToString(CorrType corr);
|
|
||||||
std::ostream& operator<<(std::ostream& os, CorrType corr);
|
|
||||||
|
|
||||||
enum SignalError {
|
enum SignalError {
|
||||||
SIGERR_NONE,
|
SIGERR_NONE,
|
||||||
@@ -62,66 +51,12 @@ enum SignalError {
|
|||||||
*/
|
*/
|
||||||
#define BURST_THRESH 4.0
|
#define BURST_THRESH 4.0
|
||||||
|
|
||||||
/** Convert a linear number to a dB value */
|
|
||||||
float dB(float x);
|
|
||||||
|
|
||||||
/** Convert a dB value into a linear value */
|
|
||||||
float dBinv(float x);
|
|
||||||
|
|
||||||
/** Compute the energy of a vector */
|
|
||||||
float vectorNorm2(const signalVector &x);
|
|
||||||
|
|
||||||
/** Compute the average power of a vector */
|
|
||||||
float vectorPower(const signalVector &x);
|
|
||||||
|
|
||||||
/** Setup the signal processing library */
|
/** Setup the signal processing library */
|
||||||
bool sigProcLibSetup();
|
bool sigProcLibSetup();
|
||||||
|
|
||||||
/** Destroy the signal processing library */
|
/** Destroy the signal processing library */
|
||||||
void sigProcLibDestroy(void);
|
void sigProcLibDestroy(void);
|
||||||
|
|
||||||
/**
|
|
||||||
Convolve two vectors.
|
|
||||||
@param a,b The vectors to be convolved.
|
|
||||||
@param c, A preallocated vector to hold the convolution result.
|
|
||||||
@param spanType The type/span of the convolution.
|
|
||||||
@return The convolution result or NULL on error.
|
|
||||||
*/
|
|
||||||
signalVector *convolve(const signalVector *a, const signalVector *b,
|
|
||||||
signalVector *c, ConvType spanType,
|
|
||||||
size_t start = 0, size_t len = 0,
|
|
||||||
size_t step = 1, int offset = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Frequency shift a vector.
|
|
||||||
@param y The frequency shifted vector.
|
|
||||||
@param x The vector to-be-shifted.
|
|
||||||
@param freq The digital frequency shift
|
|
||||||
@param startPhase The starting phase of the oscillator
|
|
||||||
@param finalPhase The final phase of the oscillator
|
|
||||||
@return The frequency shifted vector.
|
|
||||||
*/
|
|
||||||
signalVector* frequencyShift(signalVector *y,
|
|
||||||
signalVector *x,
|
|
||||||
float freq = 0.0,
|
|
||||||
float startPhase = 0.0,
|
|
||||||
float *finalPhase=NULL);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Correlate two vectors.
|
|
||||||
@param a,b The vectors to be correlated.
|
|
||||||
@param c, A preallocated vector to hold the correlation result.
|
|
||||||
@param spanType The type/span of the correlation.
|
|
||||||
@return The correlation result.
|
|
||||||
*/
|
|
||||||
signalVector* correlate(signalVector *a,
|
|
||||||
signalVector *b,
|
|
||||||
signalVector *c,
|
|
||||||
ConvType spanType,
|
|
||||||
bool bReversedConjugated = false,
|
|
||||||
unsigned startIx = 0,
|
|
||||||
unsigned len = 0);
|
|
||||||
|
|
||||||
/** Operate soft slicer on a soft-bit vector */
|
/** Operate soft slicer on a soft-bit vector */
|
||||||
bool vectorSlicer(SoftVector *x);
|
bool vectorSlicer(SoftVector *x);
|
||||||
|
|
||||||
@@ -141,7 +76,7 @@ signalVector *generateEdgeBurst(int tsc);
|
|||||||
signalVector *generateEmptyBurst(int sps, int tn);
|
signalVector *generateEmptyBurst(int sps, int tn);
|
||||||
|
|
||||||
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
|
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
|
||||||
signalVector *genRandNormalBurst(int tsc, int sps, int tn, PRBS &prbs);
|
signalVector *genRandNormalBurst(int tsc, int sps, int tn);
|
||||||
|
|
||||||
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
|
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
|
||||||
signalVector *genRandAccessBurst(int delay, int sps, int tn);
|
signalVector *genRandAccessBurst(int delay, int sps, int tn);
|
||||||
@@ -149,45 +84,6 @@ signalVector *genRandAccessBurst(int delay, int sps, int tn);
|
|||||||
/** Generate a dummy GSM burst - 4 or 1 SPS */
|
/** Generate a dummy GSM burst - 4 or 1 SPS */
|
||||||
signalVector *generateDummyBurst(int sps, int tn);
|
signalVector *generateDummyBurst(int sps, int tn);
|
||||||
|
|
||||||
/** Sinc function */
|
|
||||||
float sinc(float x);
|
|
||||||
|
|
||||||
/** Delay a vector */
|
|
||||||
signalVector *delayVector(const signalVector *in, signalVector *out, float delay);
|
|
||||||
|
|
||||||
/** Add two vectors in-place */
|
|
||||||
bool addVector(signalVector &x,
|
|
||||||
signalVector &y);
|
|
||||||
|
|
||||||
/** Multiply two vectors in-place*/
|
|
||||||
bool multVector(signalVector &x,
|
|
||||||
signalVector &y);
|
|
||||||
|
|
||||||
/** Generate a vector of gaussian noise */
|
|
||||||
signalVector *gaussianNoise(int length,
|
|
||||||
float variance = 1.0,
|
|
||||||
complex mean = complex(0.0));
|
|
||||||
|
|
||||||
/**
|
|
||||||
Given a non-integer index, interpolate a sample.
|
|
||||||
@param inSig The signal from which to interpolate.
|
|
||||||
@param ix The index.
|
|
||||||
@return The interpolated signal value.
|
|
||||||
*/
|
|
||||||
complex interpolatePoint(const signalVector &inSig,
|
|
||||||
float ix);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Given a correlator output, locate the correlation peak.
|
|
||||||
@param rxBurst The correlator result.
|
|
||||||
@param peakIndex Pointer to value to receive interpolated peak index.
|
|
||||||
@param avgPower Power to value to receive mean power.
|
|
||||||
@return Peak value.
|
|
||||||
*/
|
|
||||||
complex peakDetect(const signalVector &rxBurst,
|
|
||||||
float *peakIndex,
|
|
||||||
float *avgPwr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Apply a scalar to a vector.
|
Apply a scalar to a vector.
|
||||||
@param x The vector of interest.
|
@param x The vector of interest.
|
||||||
@@ -204,68 +100,6 @@ void scaleVector(signalVector &x,
|
|||||||
*/
|
*/
|
||||||
float energyDetect(const signalVector &rxBurst,
|
float energyDetect(const signalVector &rxBurst,
|
||||||
unsigned windowLength);
|
unsigned windowLength);
|
||||||
|
|
||||||
/**
|
|
||||||
RACH aka Access Burst correlator/detector.
|
|
||||||
@param burst The received GSM burst of interest.
|
|
||||||
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
|
|
||||||
@param sps The number of samples per GSM symbol.
|
|
||||||
@param amplitude The estimated amplitude of received RACH burst.
|
|
||||||
@param toa The estimate time-of-arrival of received RACH burst.
|
|
||||||
@param max_toa The maximum expected time-of-arrival
|
|
||||||
@return 1 if threshold value is reached,
|
|
||||||
negative value (-SignalError) on error,
|
|
||||||
zero (SIGERR_NONE) if no burst is detected
|
|
||||||
*/
|
|
||||||
int detectRACHBurst(const signalVector &burst,
|
|
||||||
float threshold,
|
|
||||||
int sps,
|
|
||||||
complex &litude,
|
|
||||||
float &toa,
|
|
||||||
unsigned max_toa);
|
|
||||||
|
|
||||||
/**
|
|
||||||
GMSK Normal Burst correlator/detector.
|
|
||||||
@param rxBurst The received GSM burst of interest.
|
|
||||||
@param tsc Midamble type (0..7) also known as TSC
|
|
||||||
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
|
|
||||||
@param sps The number of samples per GSM symbol.
|
|
||||||
@param amplitude The estimated amplitude of received TSC burst.
|
|
||||||
@param toa The estimate time-of-arrival of received TSC burst.
|
|
||||||
@param max_toa The maximum expected time-of-arrival
|
|
||||||
@return 1 if threshold value is reached,
|
|
||||||
negative value (-SignalError) on error,
|
|
||||||
zero (SIGERR_NONE) if no burst is detected
|
|
||||||
*/
|
|
||||||
int analyzeTrafficBurst(const signalVector &burst,
|
|
||||||
unsigned tsc,
|
|
||||||
float threshold,
|
|
||||||
int sps,
|
|
||||||
complex &litude,
|
|
||||||
float &toa,
|
|
||||||
unsigned max_toa);
|
|
||||||
|
|
||||||
/**
|
|
||||||
EDGE/8-PSK Normal Burst correlator/detector
|
|
||||||
@param burst The received GSM burst of interest
|
|
||||||
@param tsc Midamble type (0..7) also known as TSC
|
|
||||||
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
|
|
||||||
@param sps The number of samples per GSM symbol.
|
|
||||||
@param amplitude The estimated amplitude of received TSC burst.
|
|
||||||
@param toa The estimate time-of-arrival of received TSC burst.
|
|
||||||
@param max_toa The maximum expected time-of-arrival
|
|
||||||
@return 1 if threshold value is reached,
|
|
||||||
negative value (-SignalError) on error,
|
|
||||||
zero (SIGERR_NONE) if no burst is detected
|
|
||||||
*/
|
|
||||||
int detectEdgeBurst(const signalVector &burst,
|
|
||||||
unsigned tsc,
|
|
||||||
float threshold,
|
|
||||||
int sps,
|
|
||||||
complex &litude,
|
|
||||||
float &toa,
|
|
||||||
unsigned max_toa);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
8-PSK/GMSK/RACH burst detector
|
8-PSK/GMSK/RACH burst detector
|
||||||
@param burst The received GSM burst of interest
|
@param burst The received GSM burst of interest
|
||||||
@@ -288,44 +122,6 @@ int detectAnyBurst(const signalVector &burst,
|
|||||||
float &toa,
|
float &toa,
|
||||||
unsigned max_toa);
|
unsigned max_toa);
|
||||||
|
|
||||||
/**
|
|
||||||
Downsample 4 SPS to 1 SPS using a polyphase filterbank
|
|
||||||
@param burst Input burst of at least 624 symbols
|
|
||||||
@return Decimated signal vector of 156 symbols
|
|
||||||
*/
|
|
||||||
signalVector *downsampleBurst(const signalVector &burst);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Decimate a vector.
|
|
||||||
@param wVector The vector of interest.
|
|
||||||
@param factor Decimation factor.
|
|
||||||
@return The decimated signal vector.
|
|
||||||
*/
|
|
||||||
signalVector *decimateVector(signalVector &wVector, size_t factor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Demodulates a GMSK burst using a soft-slicer.
|
|
||||||
@param rxBurst The burst to be demodulated.
|
|
||||||
@param gsmPulse The GSM pulse.
|
|
||||||
@param sps The number of samples per GSM symbol.
|
|
||||||
@param channel The amplitude estimate of the received burst.
|
|
||||||
@param TOA The time-of-arrival of the received burst.
|
|
||||||
@return The demodulated bit sequence.
|
|
||||||
*/
|
|
||||||
SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps,
|
|
||||||
complex channel, float TOA);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Demodulate 8-PSK EDGE burst with soft symbol ooutput
|
|
||||||
@param rxBurst The burst to be demodulated.
|
|
||||||
@param sps The number of samples per GSM symbol.
|
|
||||||
@param channel The amplitude estimate of the received burst.
|
|
||||||
@param TOA The time-of-arrival of the received burst.
|
|
||||||
@return The demodulated bit sequence.
|
|
||||||
*/
|
|
||||||
SoftVector *demodEdgeBurst(const signalVector &rxBurst, int sps,
|
|
||||||
complex channel, float TOA);
|
|
||||||
|
|
||||||
/** Demodulate burst basde on type and output soft bits */
|
/** Demodulate burst basde on type and output soft bits */
|
||||||
SoftVector *demodAnyBurst(const signalVector &burst, int sps,
|
SoftVector *demodAnyBurst(const signalVector &burst, int sps,
|
||||||
complex amp, float toa, CorrType type);
|
complex amp, float toa, CorrType type);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ void convert_init(void)
|
|||||||
c.convert_si16_ps_16n = base_convert_short_float;
|
c.convert_si16_ps_16n = base_convert_short_float;
|
||||||
c.convert_si16_ps = base_convert_short_float;
|
c.convert_si16_ps = base_convert_short_float;
|
||||||
|
|
||||||
|
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||||
#ifdef HAVE_SSE4_1
|
#ifdef HAVE_SSE4_1
|
||||||
if (__builtin_cpu_supports("sse4.1")) {
|
if (__builtin_cpu_supports("sse4.1")) {
|
||||||
c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n;
|
c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n;
|
||||||
@@ -60,6 +61,7 @@ void convert_init(void)
|
|||||||
c.convert_scale_ps_si16 = _sse_convert_scale_ps_si16;
|
c.convert_scale_ps_si16 = _sse_convert_scale_ps_si16;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void convert_float_short(short *out, const float *in, float scale, int len)
|
void convert_float_short(short *out, const float *in, float scale, int len)
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ void convolve_init(void)
|
|||||||
c.conv_real4n = (void *)_base_convolve_real;
|
c.conv_real4n = (void *)_base_convolve_real;
|
||||||
c.conv_real = (void *)_base_convolve_real;
|
c.conv_real = (void *)_base_convolve_real;
|
||||||
|
|
||||||
#ifdef HAVE_SSE3
|
#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
|
||||||
if (__builtin_cpu_supports("sse3")) {
|
if (__builtin_cpu_supports("sse3")) {
|
||||||
c.conv_cmplx_4n = sse_conv_cmplx_4n;
|
c.conv_cmplx_4n = sse_conv_cmplx_4n;
|
||||||
c.conv_cmplx_8n = sse_conv_cmplx_8n;
|
c.conv_cmplx_8n = sse_conv_cmplx_8n;
|
||||||
|
|||||||
982
config/ax_cxx_compile_stdcxx.m4
Normal file
982
config/ax_cxx_compile_stdcxx.m4
Normal file
@@ -0,0 +1,982 @@
|
|||||||
|
# ===========================================================================
|
||||||
|
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||||
|
# ===========================================================================
|
||||||
|
#
|
||||||
|
# SYNOPSIS
|
||||||
|
#
|
||||||
|
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
|
||||||
|
#
|
||||||
|
# DESCRIPTION
|
||||||
|
#
|
||||||
|
# Check for baseline language coverage in the compiler for the specified
|
||||||
|
# version of the C++ standard. If necessary, add switches to CXX and
|
||||||
|
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
|
||||||
|
# or '14' (for the C++14 standard).
|
||||||
|
#
|
||||||
|
# The second argument, if specified, indicates whether you insist on an
|
||||||
|
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||||
|
# -std=c++11). If neither is specified, you get whatever works, with
|
||||||
|
# preference for an extended mode.
|
||||||
|
#
|
||||||
|
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||||
|
# indicates that baseline support for the specified C++ standard is
|
||||||
|
# required and that the macro should error out if no mode with that
|
||||||
|
# support is found. If specified 'optional', then configuration proceeds
|
||||||
|
# regardless, after defining HAVE_CXX${VERSION} if and only if a
|
||||||
|
# supporting mode is found.
|
||||||
|
#
|
||||||
|
# LICENSE
|
||||||
|
#
|
||||||
|
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||||
|
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||||
|
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||||
|
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||||
|
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||||
|
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||||
|
# Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
|
||||||
|
#
|
||||||
|
# Copying and distribution of this file, with or without modification, are
|
||||||
|
# permitted in any medium without royalty provided the copyright notice
|
||||||
|
# and this notice are preserved. This file is offered as-is, without any
|
||||||
|
# warranty.
|
||||||
|
|
||||||
|
#serial 7
|
||||||
|
|
||||||
|
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||||
|
dnl (serial version number 13).
|
||||||
|
|
||||||
|
AX_REQUIRE_DEFINED([AC_MSG_WARN])
|
||||||
|
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||||
|
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
|
||||||
|
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
|
||||||
|
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
|
||||||
|
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||||
|
m4_if([$2], [], [],
|
||||||
|
[$2], [ext], [],
|
||||||
|
[$2], [noext], [],
|
||||||
|
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||||
|
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
|
||||||
|
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
|
||||||
|
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
|
||||||
|
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||||
|
AC_LANG_PUSH([C++])dnl
|
||||||
|
ac_success=no
|
||||||
|
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||||
|
ax_cv_cxx_compile_cxx$1,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||||
|
[ax_cv_cxx_compile_cxx$1=yes],
|
||||||
|
[ax_cv_cxx_compile_cxx$1=no])])
|
||||||
|
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||||
|
ac_success=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
m4_if([$2], [noext], [], [dnl
|
||||||
|
if test x$ac_success = xno; then
|
||||||
|
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||||
|
switch="-std=gnu++${alternative}"
|
||||||
|
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||||
|
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||||
|
$cachevar,
|
||||||
|
[ac_save_CXX="$CXX"
|
||||||
|
CXX="$CXX $switch"
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||||
|
[eval $cachevar=yes],
|
||||||
|
[eval $cachevar=no])
|
||||||
|
CXX="$ac_save_CXX"])
|
||||||
|
if eval test x\$$cachevar = xyes; then
|
||||||
|
CXX="$CXX $switch"
|
||||||
|
if test -n "$CXXCPP" ; then
|
||||||
|
CXXCPP="$CXXCPP $switch"
|
||||||
|
fi
|
||||||
|
ac_success=yes
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi])
|
||||||
|
|
||||||
|
m4_if([$2], [ext], [], [dnl
|
||||||
|
if test x$ac_success = xno; then
|
||||||
|
dnl HP's aCC needs +std=c++11 according to:
|
||||||
|
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||||
|
dnl Cray's crayCC needs "-h std=c++11"
|
||||||
|
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||||
|
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
|
||||||
|
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||||
|
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||||
|
$cachevar,
|
||||||
|
[ac_save_CXX="$CXX"
|
||||||
|
CXX="$CXX $switch"
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||||
|
[eval $cachevar=yes],
|
||||||
|
[eval $cachevar=no])
|
||||||
|
CXX="$ac_save_CXX"])
|
||||||
|
if eval test x\$$cachevar = xyes; then
|
||||||
|
CXX="$CXX $switch"
|
||||||
|
if test -n "$CXXCPP" ; then
|
||||||
|
CXXCPP="$CXXCPP $switch"
|
||||||
|
fi
|
||||||
|
ac_success=yes
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if test x$ac_success = xyes; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi])
|
||||||
|
AC_LANG_POP([C++])
|
||||||
|
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
|
||||||
|
if test x$ac_success = xno; then
|
||||||
|
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test x$ac_success = xno; then
|
||||||
|
HAVE_CXX$1=0
|
||||||
|
AC_MSG_NOTICE([No compiler with C++$1 support was found])
|
||||||
|
else
|
||||||
|
HAVE_CXX$1=1
|
||||||
|
AC_DEFINE(HAVE_CXX$1,1,
|
||||||
|
[define if the compiler supports basic C++$1 syntax])
|
||||||
|
fi
|
||||||
|
AC_SUBST(HAVE_CXX$1)
|
||||||
|
m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])])
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
dnl Test body for checking C++11 support
|
||||||
|
|
||||||
|
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||||
|
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
dnl Test body for checking C++14 support
|
||||||
|
|
||||||
|
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||||
|
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||||
|
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||||
|
)
|
||||||
|
|
||||||
|
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
|
||||||
|
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||||
|
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||||
|
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||||
|
)
|
||||||
|
|
||||||
|
dnl Tests for new features in C++11
|
||||||
|
|
||||||
|
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||||
|
|
||||||
|
// If the compiler admits that it is not ready for C++11, why torture it?
|
||||||
|
// Hopefully, this will speed up the test.
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
|
||||||
|
#error "This is not a C++ compiler"
|
||||||
|
|
||||||
|
#elif __cplusplus < 201103L
|
||||||
|
|
||||||
|
#error "This is not a C++11 compiler"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
namespace cxx11
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace test_static_assert
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct check
|
||||||
|
{
|
||||||
|
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_final_override
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Base
|
||||||
|
{
|
||||||
|
virtual void f() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Derived : public Base
|
||||||
|
{
|
||||||
|
virtual void f() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_double_right_angle_brackets
|
||||||
|
{
|
||||||
|
|
||||||
|
template < typename T >
|
||||||
|
struct check {};
|
||||||
|
|
||||||
|
typedef check<void> single_type;
|
||||||
|
typedef check<check<void>> double_type;
|
||||||
|
typedef check<check<check<void>>> triple_type;
|
||||||
|
typedef check<check<check<check<void>>>> quadruple_type;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_decltype
|
||||||
|
{
|
||||||
|
|
||||||
|
int
|
||||||
|
f()
|
||||||
|
{
|
||||||
|
int a = 1;
|
||||||
|
decltype(a) b = 2;
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_type_deduction
|
||||||
|
{
|
||||||
|
|
||||||
|
template < typename T1, typename T2 >
|
||||||
|
struct is_same
|
||||||
|
{
|
||||||
|
static const bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename T >
|
||||||
|
struct is_same<T, T>
|
||||||
|
{
|
||||||
|
static const bool value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename T1, typename T2 >
|
||||||
|
auto
|
||||||
|
add(T1 a1, T2 a2) -> decltype(a1 + a2)
|
||||||
|
{
|
||||||
|
return a1 + a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
test(const int c, volatile int v)
|
||||||
|
{
|
||||||
|
static_assert(is_same<int, decltype(0)>::value == true, "");
|
||||||
|
static_assert(is_same<int, decltype(c)>::value == false, "");
|
||||||
|
static_assert(is_same<int, decltype(v)>::value == false, "");
|
||||||
|
auto ac = c;
|
||||||
|
auto av = v;
|
||||||
|
auto sumi = ac + av + 'x';
|
||||||
|
auto sumf = ac + av + 1.0;
|
||||||
|
static_assert(is_same<int, decltype(ac)>::value == true, "");
|
||||||
|
static_assert(is_same<int, decltype(av)>::value == true, "");
|
||||||
|
static_assert(is_same<int, decltype(sumi)>::value == true, "");
|
||||||
|
static_assert(is_same<int, decltype(sumf)>::value == false, "");
|
||||||
|
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
|
||||||
|
return (sumf > 0.0) ? sumi : add(c, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_noexcept
|
||||||
|
{
|
||||||
|
|
||||||
|
int f() { return 0; }
|
||||||
|
int g() noexcept { return 0; }
|
||||||
|
|
||||||
|
static_assert(noexcept(f()) == false, "");
|
||||||
|
static_assert(noexcept(g()) == true, "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_constexpr
|
||||||
|
{
|
||||||
|
|
||||||
|
template < typename CharT >
|
||||||
|
unsigned long constexpr
|
||||||
|
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
|
||||||
|
{
|
||||||
|
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename CharT >
|
||||||
|
unsigned long constexpr
|
||||||
|
strlen_c(const CharT *const s) noexcept
|
||||||
|
{
|
||||||
|
return strlen_c_r(s, 0UL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(strlen_c("") == 0UL, "");
|
||||||
|
static_assert(strlen_c("1") == 1UL, "");
|
||||||
|
static_assert(strlen_c("example") == 7UL, "");
|
||||||
|
static_assert(strlen_c("another\0example") == 7UL, "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_rvalue_references
|
||||||
|
{
|
||||||
|
|
||||||
|
template < int N >
|
||||||
|
struct answer
|
||||||
|
{
|
||||||
|
static constexpr int value = N;
|
||||||
|
};
|
||||||
|
|
||||||
|
answer<1> f(int&) { return answer<1>(); }
|
||||||
|
answer<2> f(const int&) { return answer<2>(); }
|
||||||
|
answer<3> f(int&&) { return answer<3>(); }
|
||||||
|
|
||||||
|
void
|
||||||
|
test()
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
const int c = 0;
|
||||||
|
static_assert(decltype(f(i))::value == 1, "");
|
||||||
|
static_assert(decltype(f(c))::value == 2, "");
|
||||||
|
static_assert(decltype(f(0))::value == 3, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_uniform_initialization
|
||||||
|
{
|
||||||
|
|
||||||
|
struct test
|
||||||
|
{
|
||||||
|
static const int zero {};
|
||||||
|
static const int one {1};
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(test::zero == 0, "");
|
||||||
|
static_assert(test::one == 1, "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_lambdas
|
||||||
|
{
|
||||||
|
|
||||||
|
void
|
||||||
|
test1()
|
||||||
|
{
|
||||||
|
auto lambda1 = [](){};
|
||||||
|
auto lambda2 = lambda1;
|
||||||
|
lambda1();
|
||||||
|
lambda2();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
test2()
|
||||||
|
{
|
||||||
|
auto a = [](int i, int j){ return i + j; }(1, 2);
|
||||||
|
auto b = []() -> int { return '0'; }();
|
||||||
|
auto c = [=](){ return a + b; }();
|
||||||
|
auto d = [&](){ return c; }();
|
||||||
|
auto e = [a, &b](int x) mutable {
|
||||||
|
const auto identity = [](int y){ return y; };
|
||||||
|
for (auto i = 0; i < a; ++i)
|
||||||
|
a += b--;
|
||||||
|
return x + identity(a + b);
|
||||||
|
}(0);
|
||||||
|
return a + b + c + d + e;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
test3()
|
||||||
|
{
|
||||||
|
const auto nullary = [](){ return 0; };
|
||||||
|
const auto unary = [](int x){ return x; };
|
||||||
|
using nullary_t = decltype(nullary);
|
||||||
|
using unary_t = decltype(unary);
|
||||||
|
const auto higher1st = [](nullary_t f){ return f(); };
|
||||||
|
const auto higher2nd = [unary](nullary_t f1){
|
||||||
|
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
|
||||||
|
};
|
||||||
|
return higher1st(nullary) + higher2nd(nullary)(unary);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_variadic_templates
|
||||||
|
{
|
||||||
|
|
||||||
|
template <int...>
|
||||||
|
struct sum;
|
||||||
|
|
||||||
|
template <int N0, int... N1toN>
|
||||||
|
struct sum<N0, N1toN...>
|
||||||
|
{
|
||||||
|
static constexpr auto value = N0 + sum<N1toN...>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct sum<>
|
||||||
|
{
|
||||||
|
static constexpr auto value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sum<>::value == 0, "");
|
||||||
|
static_assert(sum<1>::value == 1, "");
|
||||||
|
static_assert(sum<23>::value == 23, "");
|
||||||
|
static_assert(sum<1, 2>::value == 3, "");
|
||||||
|
static_assert(sum<5, 5, 11>::value == 21, "");
|
||||||
|
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
|
||||||
|
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
|
||||||
|
// because of this.
|
||||||
|
namespace test_template_alias_sfinae
|
||||||
|
{
|
||||||
|
|
||||||
|
struct foo {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using member = typename T::member_type;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void func(...) {}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void func(member<T>*) {}
|
||||||
|
|
||||||
|
void test();
|
||||||
|
|
||||||
|
void test() { func<foo>(0); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cxx11
|
||||||
|
|
||||||
|
#endif // __cplusplus >= 201103L
|
||||||
|
|
||||||
|
]])
|
||||||
|
|
||||||
|
|
||||||
|
dnl Tests for new features in C++14
|
||||||
|
|
||||||
|
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
|
||||||
|
|
||||||
|
// If the compiler admits that it is not ready for C++14, why torture it?
|
||||||
|
// Hopefully, this will speed up the test.
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
|
||||||
|
#error "This is not a C++ compiler"
|
||||||
|
|
||||||
|
#elif __cplusplus < 201402L
|
||||||
|
|
||||||
|
#error "This is not a C++14 compiler"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
namespace cxx14
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace test_polymorphic_lambdas
|
||||||
|
{
|
||||||
|
|
||||||
|
int
|
||||||
|
test()
|
||||||
|
{
|
||||||
|
const auto lambda = [](auto&&... args){
|
||||||
|
const auto istiny = [](auto x){
|
||||||
|
return (sizeof(x) == 1UL) ? 1 : 0;
|
||||||
|
};
|
||||||
|
const int aretiny[] = { istiny(args)... };
|
||||||
|
return aretiny[0];
|
||||||
|
};
|
||||||
|
return lambda(1, 1L, 1.0f, '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_binary_literals
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr auto ivii = 0b0000000000101010;
|
||||||
|
static_assert(ivii == 42, "wrong value");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_generalized_constexpr
|
||||||
|
{
|
||||||
|
|
||||||
|
template < typename CharT >
|
||||||
|
constexpr unsigned long
|
||||||
|
strlen_c(const CharT *const s) noexcept
|
||||||
|
{
|
||||||
|
auto length = 0UL;
|
||||||
|
for (auto p = s; *p; ++p)
|
||||||
|
++length;
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(strlen_c("") == 0UL, "");
|
||||||
|
static_assert(strlen_c("x") == 1UL, "");
|
||||||
|
static_assert(strlen_c("test") == 4UL, "");
|
||||||
|
static_assert(strlen_c("another\0test") == 7UL, "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_lambda_init_capture
|
||||||
|
{
|
||||||
|
|
||||||
|
int
|
||||||
|
test()
|
||||||
|
{
|
||||||
|
auto x = 0;
|
||||||
|
const auto lambda1 = [a = x](int b){ return a + b; };
|
||||||
|
const auto lambda2 = [a = lambda1(x)](){ return a; };
|
||||||
|
return lambda2();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_digit_separators
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr auto ten_million = 100'000'000;
|
||||||
|
static_assert(ten_million == 100000000, "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_return_type_deduction
|
||||||
|
{
|
||||||
|
|
||||||
|
auto f(int& x) { return x; }
|
||||||
|
decltype(auto) g(int& x) { return x; }
|
||||||
|
|
||||||
|
template < typename T1, typename T2 >
|
||||||
|
struct is_same
|
||||||
|
{
|
||||||
|
static constexpr auto value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename T >
|
||||||
|
struct is_same<T, T>
|
||||||
|
{
|
||||||
|
static constexpr auto value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
test()
|
||||||
|
{
|
||||||
|
auto x = 0;
|
||||||
|
static_assert(is_same<int, decltype(f(x))>::value, "");
|
||||||
|
static_assert(is_same<int&, decltype(g(x))>::value, "");
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cxx14
|
||||||
|
|
||||||
|
#endif // __cplusplus >= 201402L
|
||||||
|
|
||||||
|
]])
|
||||||
|
|
||||||
|
|
||||||
|
dnl Tests for new features in C++17
|
||||||
|
|
||||||
|
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
|
||||||
|
|
||||||
|
// If the compiler admits that it is not ready for C++17, why torture it?
|
||||||
|
// Hopefully, this will speed up the test.
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
|
||||||
|
#error "This is not a C++ compiler"
|
||||||
|
|
||||||
|
#elif __cplusplus <= 201402L
|
||||||
|
|
||||||
|
#error "This is not a C++17 compiler"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define REALLY_CLANG
|
||||||
|
#else
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define REALLY_GCC
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <utility>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace cxx17
|
||||||
|
{
|
||||||
|
|
||||||
|
#if !defined(REALLY_CLANG)
|
||||||
|
namespace test_constexpr_lambdas
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO: test it with clang++ from git
|
||||||
|
|
||||||
|
constexpr int foo = [](){return 42;}();
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // !defined(REALLY_CLANG)
|
||||||
|
|
||||||
|
namespace test::nested_namespace::definitions
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_fold_expression
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
int multiply(Args... args)
|
||||||
|
{
|
||||||
|
return (args * ... * 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
bool all(Args... args)
|
||||||
|
{
|
||||||
|
return (args && ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_extended_static_assert
|
||||||
|
{
|
||||||
|
|
||||||
|
static_assert (true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_auto_brace_init_list
|
||||||
|
{
|
||||||
|
|
||||||
|
auto foo = {5};
|
||||||
|
auto bar {5};
|
||||||
|
|
||||||
|
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
|
||||||
|
static_assert(std::is_same<int, decltype(bar)>::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_typename_in_template_template_parameter
|
||||||
|
{
|
||||||
|
|
||||||
|
template<template<typename> typename X> struct D;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_fallthrough_nodiscard_maybe_unused_attributes
|
||||||
|
{
|
||||||
|
|
||||||
|
int f1()
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int f2()
|
||||||
|
{
|
||||||
|
[[maybe_unused]] auto unused = f1();
|
||||||
|
|
||||||
|
switch (f1())
|
||||||
|
{
|
||||||
|
case 17:
|
||||||
|
f1();
|
||||||
|
[[fallthrough]];
|
||||||
|
case 42:
|
||||||
|
f1();
|
||||||
|
}
|
||||||
|
return f1();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_extended_aggregate_initialization
|
||||||
|
{
|
||||||
|
|
||||||
|
struct base1
|
||||||
|
{
|
||||||
|
int b1, b2 = 42;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct base2
|
||||||
|
{
|
||||||
|
base2() {
|
||||||
|
b3 = 42;
|
||||||
|
}
|
||||||
|
int b3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct derived : base1, base2
|
||||||
|
{
|
||||||
|
int d;
|
||||||
|
};
|
||||||
|
|
||||||
|
derived d1 {{1, 2}, {}, 4}; // full initialization
|
||||||
|
derived d2 {{}, {}, 4}; // value-initialized bases
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_general_range_based_for_loop
|
||||||
|
{
|
||||||
|
|
||||||
|
struct iter
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
int& operator* ()
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int& operator* () const
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter& operator++()
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sentinel
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator== (const iter& i, const sentinel& s)
|
||||||
|
{
|
||||||
|
return i.i == s.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!= (const iter& i, const sentinel& s)
|
||||||
|
{
|
||||||
|
return !(i == s);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct range
|
||||||
|
{
|
||||||
|
iter begin() const
|
||||||
|
{
|
||||||
|
return {0};
|
||||||
|
}
|
||||||
|
|
||||||
|
sentinel end() const
|
||||||
|
{
|
||||||
|
return {5};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void f()
|
||||||
|
{
|
||||||
|
range r {};
|
||||||
|
|
||||||
|
for (auto i : r)
|
||||||
|
{
|
||||||
|
[[maybe_unused]] auto v = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_lambda_capture_asterisk_this_by_value
|
||||||
|
{
|
||||||
|
|
||||||
|
struct t
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int foo()
|
||||||
|
{
|
||||||
|
return [*this]()
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_enum_class_construction
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class byte : unsigned char
|
||||||
|
{};
|
||||||
|
|
||||||
|
byte foo {42};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_constexpr_if
|
||||||
|
{
|
||||||
|
|
||||||
|
template <bool cond>
|
||||||
|
int f ()
|
||||||
|
{
|
||||||
|
if constexpr(cond)
|
||||||
|
{
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_selection_statement_with_initializer
|
||||||
|
{
|
||||||
|
|
||||||
|
int f()
|
||||||
|
{
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f2()
|
||||||
|
{
|
||||||
|
if (auto i = f(); i > 0)
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (auto i = f(); i + 4)
|
||||||
|
{
|
||||||
|
case 17:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(REALLY_CLANG)
|
||||||
|
namespace test_template_argument_deduction_for_class_templates
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO: test it with clang++ from git
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
struct pair
|
||||||
|
{
|
||||||
|
pair (T1 p1, T2 p2)
|
||||||
|
: m1 {p1},
|
||||||
|
m2 {p2}
|
||||||
|
{}
|
||||||
|
|
||||||
|
T1 m1;
|
||||||
|
T2 m2;
|
||||||
|
};
|
||||||
|
|
||||||
|
void f()
|
||||||
|
{
|
||||||
|
[[maybe_unused]] auto p = pair{13, 42u};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // !defined(REALLY_CLANG)
|
||||||
|
|
||||||
|
namespace test_non_type_auto_template_parameters
|
||||||
|
{
|
||||||
|
|
||||||
|
template <auto n>
|
||||||
|
struct B
|
||||||
|
{};
|
||||||
|
|
||||||
|
B<5> b1;
|
||||||
|
B<'a'> b2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(REALLY_CLANG)
|
||||||
|
namespace test_structured_bindings
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO: test it with clang++ from git
|
||||||
|
|
||||||
|
int arr[2] = { 1, 2 };
|
||||||
|
std::pair<int, int> pr = { 1, 2 };
|
||||||
|
|
||||||
|
auto f1() -> int(&)[2]
|
||||||
|
{
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto f2() -> std::pair<int, int>&
|
||||||
|
{
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S
|
||||||
|
{
|
||||||
|
int x1 : 2;
|
||||||
|
volatile double y1;
|
||||||
|
};
|
||||||
|
|
||||||
|
S f3()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [ x1, y1 ] = f1();
|
||||||
|
auto& [ xr1, yr1 ] = f1();
|
||||||
|
auto [ x2, y2 ] = f2();
|
||||||
|
auto& [ xr2, yr2 ] = f2();
|
||||||
|
const auto [ x3, y3 ] = f3();
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // !defined(REALLY_CLANG)
|
||||||
|
|
||||||
|
#if !defined(REALLY_CLANG)
|
||||||
|
namespace test_exception_spec_type_system
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO: test it with clang++ from git
|
||||||
|
|
||||||
|
struct Good {};
|
||||||
|
struct Bad {};
|
||||||
|
|
||||||
|
void g1() noexcept;
|
||||||
|
void g2();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Bad
|
||||||
|
f(T*, T*);
|
||||||
|
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
Good
|
||||||
|
f(T1*, T2*);
|
||||||
|
|
||||||
|
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // !defined(REALLY_CLANG)
|
||||||
|
|
||||||
|
namespace test_inline_variables
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class T> void f(T)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<class T> inline T g(T)
|
||||||
|
{
|
||||||
|
return T{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline void f<>(int)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<> int g<>(int)
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cxx17
|
||||||
|
|
||||||
|
#endif // __cplusplus <= 201402L
|
||||||
|
|
||||||
|
]])
|
||||||
39
config/ax_cxx_compile_stdcxx_11.m4
Normal file
39
config/ax_cxx_compile_stdcxx_11.m4
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# SYNOPSIS
|
||||||
|
#
|
||||||
|
# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
|
||||||
|
#
|
||||||
|
# DESCRIPTION
|
||||||
|
#
|
||||||
|
# Check for baseline language coverage in the compiler for the C++11
|
||||||
|
# standard; if necessary, add switches to CXX and CXXCPP to enable
|
||||||
|
# support.
|
||||||
|
#
|
||||||
|
# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
|
||||||
|
# macro with the version set to C++11. The two optional arguments are
|
||||||
|
# forwarded literally as the second and third argument respectively.
|
||||||
|
# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
|
||||||
|
# more information. If you want to use this macro, you also need to
|
||||||
|
# download the ax_cxx_compile_stdcxx.m4 file.
|
||||||
|
#
|
||||||
|
# LICENSE
|
||||||
|
#
|
||||||
|
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||||
|
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||||
|
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||||
|
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||||
|
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||||
|
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||||
|
#
|
||||||
|
# Copying and distribution of this file, with or without modification, are
|
||||||
|
# permitted in any medium without royalty provided the copyright notice
|
||||||
|
# and this notice are preserved. This file is offered as-is, without any
|
||||||
|
# warranty.
|
||||||
|
|
||||||
|
#serial 18
|
||||||
|
|
||||||
|
AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
|
||||||
|
AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
# ===========================================================================
|
|
||||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_avx_xgetbv.html
|
|
||||||
# ===========================================================================
|
|
||||||
#
|
|
||||||
# SYNOPSIS
|
|
||||||
#
|
|
||||||
# AX_GCC_X86_AVX_XGETBV
|
|
||||||
#
|
|
||||||
# DESCRIPTION
|
|
||||||
#
|
|
||||||
# On later x86 processors with AVX SIMD support, with gcc or a compiler
|
|
||||||
# that has a compatible syntax for inline assembly instructions, run a
|
|
||||||
# small program that executes the xgetbv instruction with input OP. This
|
|
||||||
# can be used to detect if the OS supports AVX instruction usage.
|
|
||||||
#
|
|
||||||
# On output, the values of the eax and edx registers are stored as
|
|
||||||
# hexadecimal strings as "eax:edx" in the cache variable
|
|
||||||
# ax_cv_gcc_x86_avx_xgetbv.
|
|
||||||
#
|
|
||||||
# If the xgetbv instruction fails (because you are running a
|
|
||||||
# cross-compiler, or because you are not using gcc, or because you are on
|
|
||||||
# a processor that doesn't have this instruction),
|
|
||||||
# ax_cv_gcc_x86_avx_xgetbv_OP is set to the string "unknown".
|
|
||||||
#
|
|
||||||
# This macro mainly exists to be used in AX_EXT.
|
|
||||||
#
|
|
||||||
# LICENSE
|
|
||||||
#
|
|
||||||
# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
|
||||||
# gives unlimited permission to copy, distribute and modify the configure
|
|
||||||
# scripts that are the output of Autoconf when processing the Macro. You
|
|
||||||
# need not follow the terms of the GNU General Public License when using
|
|
||||||
# or distributing such scripts, even though portions of the text of the
|
|
||||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
|
||||||
# all other use of the material that constitutes the Autoconf Macro.
|
|
||||||
#
|
|
||||||
# This special exception to the GPL applies to versions of the Autoconf
|
|
||||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
|
||||||
# modified version of the Autoconf Macro, you may extend this special
|
|
||||||
# exception to the GPL to apply to your modified version as well.
|
|
||||||
|
|
||||||
#serial 1
|
|
||||||
|
|
||||||
AC_DEFUN([AX_GCC_X86_AVX_XGETBV],
|
|
||||||
[AC_REQUIRE([AC_PROG_CC])
|
|
||||||
AC_LANG_PUSH([C])
|
|
||||||
AC_CACHE_CHECK(for x86-AVX xgetbv $1 output, ax_cv_gcc_x86_avx_xgetbv_$1,
|
|
||||||
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
|
|
||||||
int op = $1, eax, edx;
|
|
||||||
FILE *f;
|
|
||||||
/* Opcodes for xgetbv */
|
|
||||||
__asm__(".byte 0x0f, 0x01, 0xd0"
|
|
||||||
: "=a" (eax), "=d" (edx)
|
|
||||||
: "c" (op));
|
|
||||||
f = fopen("conftest_xgetbv", "w"); if (!f) return 1;
|
|
||||||
fprintf(f, "%x:%x\n", eax, edx);
|
|
||||||
fclose(f);
|
|
||||||
return 0;
|
|
||||||
])],
|
|
||||||
[ax_cv_gcc_x86_avx_xgetbv_$1=`cat conftest_xgetbv`; rm -f conftest_xgetbv],
|
|
||||||
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown; rm -f conftest_xgetbv],
|
|
||||||
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown])])
|
|
||||||
AC_LANG_POP([C])
|
|
||||||
])
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
# ===========================================================================
|
|
||||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html
|
|
||||||
# ===========================================================================
|
|
||||||
#
|
|
||||||
# SYNOPSIS
|
|
||||||
#
|
|
||||||
# AX_GCC_X86_CPUID(OP)
|
|
||||||
#
|
|
||||||
# DESCRIPTION
|
|
||||||
#
|
|
||||||
# On Pentium and later x86 processors, with gcc or a compiler that has a
|
|
||||||
# compatible syntax for inline assembly instructions, run a small program
|
|
||||||
# that executes the cpuid instruction with input OP. This can be used to
|
|
||||||
# detect the CPU type.
|
|
||||||
#
|
|
||||||
# On output, the values of the eax, ebx, ecx, and edx registers are stored
|
|
||||||
# as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable
|
|
||||||
# ax_cv_gcc_x86_cpuid_OP.
|
|
||||||
#
|
|
||||||
# If the cpuid instruction fails (because you are running a
|
|
||||||
# cross-compiler, or because you are not using gcc, or because you are on
|
|
||||||
# a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP
|
|
||||||
# is set to the string "unknown".
|
|
||||||
#
|
|
||||||
# This macro mainly exists to be used in AX_GCC_ARCHFLAG.
|
|
||||||
#
|
|
||||||
# LICENSE
|
|
||||||
#
|
|
||||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
|
||||||
# Copyright (c) 2008 Matteo Frigo
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
|
||||||
# gives unlimited permission to copy, distribute and modify the configure
|
|
||||||
# scripts that are the output of Autoconf when processing the Macro. You
|
|
||||||
# need not follow the terms of the GNU General Public License when using
|
|
||||||
# or distributing such scripts, even though portions of the text of the
|
|
||||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
|
||||||
# all other use of the material that constitutes the Autoconf Macro.
|
|
||||||
#
|
|
||||||
# This special exception to the GPL applies to versions of the Autoconf
|
|
||||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
|
||||||
# modified version of the Autoconf Macro, you may extend this special
|
|
||||||
# exception to the GPL to apply to your modified version as well.
|
|
||||||
|
|
||||||
#serial 7
|
|
||||||
|
|
||||||
AC_DEFUN([AX_GCC_X86_CPUID],
|
|
||||||
[AC_REQUIRE([AC_PROG_CC])
|
|
||||||
AC_LANG_PUSH([C])
|
|
||||||
AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1,
|
|
||||||
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
|
|
||||||
int op = $1, eax, ebx, ecx, edx;
|
|
||||||
FILE *f;
|
|
||||||
__asm__("cpuid"
|
|
||||||
: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
|
|
||||||
: "a" (op));
|
|
||||||
f = fopen("conftest_cpuid", "w"); if (!f) return 1;
|
|
||||||
fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx);
|
|
||||||
fclose(f);
|
|
||||||
return 0;
|
|
||||||
])],
|
|
||||||
[ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid],
|
|
||||||
[ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid],
|
|
||||||
[ax_cv_gcc_x86_cpuid_$1=unknown])])
|
|
||||||
AC_LANG_POP([C])
|
|
||||||
])
|
|
||||||
@@ -4,14 +4,13 @@
|
|||||||
#
|
#
|
||||||
# SYNOPSIS
|
# SYNOPSIS
|
||||||
#
|
#
|
||||||
# AX_EXT
|
# AX_SSE
|
||||||
#
|
#
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
#
|
#
|
||||||
# Find supported SIMD extensions by requesting cpuid. When an SIMD
|
# Find SIMD extensions supported by compiler. The -m"simdextensionname" is
|
||||||
# extension is found, the -m"simdextensionname" is added to SIMD_FLAGS if
|
# added to SIMD_FLAGS if compiler supports it. For example, if "sse2" is
|
||||||
# compiler supports it. For example, if "sse2" is available, then "-msse2"
|
# available, then "-msse2" is added to SIMD_FLAGS.
|
||||||
# is added to SIMD_FLAGS.
|
|
||||||
#
|
#
|
||||||
# This macro calls:
|
# This macro calls:
|
||||||
#
|
#
|
||||||
@@ -19,7 +18,7 @@
|
|||||||
#
|
#
|
||||||
# And defines:
|
# And defines:
|
||||||
#
|
#
|
||||||
# HAVE_MMX / HAVE_SSE / HAVE_SSE2 / HAVE_SSE3 / HAVE_SSSE3 / HAVE_SSE4.1 / HAVE_SSE4.2 / HAVE_AVX
|
# HAVE_SSE3 / HAVE_SSE4.1
|
||||||
#
|
#
|
||||||
# LICENSE
|
# LICENSE
|
||||||
#
|
#
|
||||||
@@ -41,32 +40,29 @@ AC_DEFUN([AX_SSE],
|
|||||||
[
|
[
|
||||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||||
|
|
||||||
|
AM_CONDITIONAL(HAVE_SSE3, false)
|
||||||
|
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
||||||
|
|
||||||
case $host_cpu in
|
case $host_cpu in
|
||||||
i[[3456]]86*|x86_64*|amd64*)
|
i[[3456]]86*|x86_64*|amd64*)
|
||||||
|
|
||||||
AC_REQUIRE([AX_GCC_X86_CPUID])
|
|
||||||
AC_REQUIRE([AX_GCC_X86_AVX_XGETBV])
|
|
||||||
|
|
||||||
AX_GCC_X86_CPUID(0x00000001)
|
|
||||||
|
|
||||||
AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, [])
|
AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, [])
|
||||||
if test x"$ax_cv_support_sse3_ext" = x"yes"; then
|
if test x"$ax_cv_support_sse3_ext" = x"yes"; then
|
||||||
SIMD_FLAGS="$SIMD_FLAGS -msse3"
|
SIMD_FLAGS="$SIMD_FLAGS -msse3"
|
||||||
AC_DEFINE(HAVE_SSE3,,[Support SSE3 (Streaming SIMD Extensions 3) instructions])
|
AC_DEFINE(HAVE_SSE3,,
|
||||||
|
[Support SSE3 (Streaming SIMD Extensions 3) instructions])
|
||||||
AM_CONDITIONAL(HAVE_SSE3, true)
|
AM_CONDITIONAL(HAVE_SSE3, true)
|
||||||
else
|
else
|
||||||
AC_MSG_WARN([Your compiler does not support sse3 instructions, can you try another compiler?])
|
AC_MSG_WARN([Your compiler does not support SSE3 instructions])
|
||||||
AM_CONDITIONAL(HAVE_SSE3, false)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, [])
|
AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, [])
|
||||||
if test x"$ax_cv_support_sse41_ext" = x"yes"; then
|
if test x"$ax_cv_support_sse41_ext" = x"yes"; then
|
||||||
SIMD_FLAGS="$SIMD_FLAGS -msse4.1"
|
SIMD_FLAGS="$SIMD_FLAGS -msse4.1"
|
||||||
AC_DEFINE(HAVE_SSE4_1,,[Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions])
|
AC_DEFINE(HAVE_SSE4_1,,
|
||||||
|
[Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions])
|
||||||
AM_CONDITIONAL(HAVE_SSE4_1, true)
|
AM_CONDITIONAL(HAVE_SSE4_1, true)
|
||||||
else
|
else
|
||||||
AC_MSG_WARN([Your compiler does not support sse4.1 instructions, can you try another compiler?])
|
AC_MSG_WARN([Your compiler does not support SSE4.1])
|
||||||
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
36
configure.ac
36
configure.ac
@@ -18,7 +18,9 @@ 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 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
dnl
|
dnl
|
||||||
|
|
||||||
AC_INIT(openbts,P2.8TRUNK)
|
AC_INIT([osmo-trx],
|
||||||
|
m4_esyscmd([./git-version-gen .tarball-veresion]),
|
||||||
|
[openbsc@lists.osmocom.org])
|
||||||
AC_PREREQ(2.57)
|
AC_PREREQ(2.57)
|
||||||
AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am])
|
AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am])
|
||||||
AC_CONFIG_AUX_DIR([.])
|
AC_CONFIG_AUX_DIR([.])
|
||||||
@@ -34,8 +36,13 @@ AM_INIT_AUTOMAKE([subdir-objects])
|
|||||||
dnl Linux kernel KBuild style compile messages
|
dnl Linux kernel KBuild style compile messages
|
||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||||
|
|
||||||
|
dnl include release helper
|
||||||
|
RELMAKE='-include osmo-release.mk'
|
||||||
|
AC_SUBST([RELMAKE])
|
||||||
|
|
||||||
AM_PROG_AS
|
AM_PROG_AS
|
||||||
AC_PROG_CXX
|
AC_PROG_CXX
|
||||||
|
AX_CXX_COMPILE_STDCXX_11
|
||||||
AC_PROG_LN_S
|
AC_PROG_LN_S
|
||||||
AC_PROG_MAKE_SET
|
AC_PROG_MAKE_SET
|
||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
@@ -120,6 +127,33 @@ AS_IF([test "x$with_sse" != "xno"], [
|
|||||||
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
dnl Check if the compiler supports specified GCC's built-in function
|
||||||
|
AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
|
||||||
|
AC_CACHE_CHECK(
|
||||||
|
[whether ${CC} has $1 built-in],
|
||||||
|
[osmo_cv_cc_has_builtin], [
|
||||||
|
AC_LINK_IFELSE([
|
||||||
|
AC_LANG_PROGRAM([], [
|
||||||
|
__builtin_cpu_supports("sse");
|
||||||
|
])
|
||||||
|
],
|
||||||
|
[AS_VAR_SET([osmo_cv_cc_has_builtin], [yes])],
|
||||||
|
[AS_VAR_SET([osmo_cv_cc_has_builtin], [no])])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
AS_IF([test yes = AS_VAR_GET([osmo_cv_cc_has_builtin])], [
|
||||||
|
AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
|
||||||
|
[Define to 1 if compiler has the '$1' built-in function])
|
||||||
|
], [
|
||||||
|
AC_MSG_WARN($2)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl Check if the compiler supports runtime SIMD detection
|
||||||
|
CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
|
||||||
|
[Runtime SIMD detection will be disabled])
|
||||||
|
|
||||||
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
|
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
|
||||||
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
||||||
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
|
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
|
||||||
|
|||||||
167
debian/changelog
vendored
167
debian/changelog
vendored
@@ -1,3 +1,170 @@
|
|||||||
|
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
|
osmo-trx (0.1.9) trusty; urgency=medium
|
||||||
|
|
||||||
* Ask Ivan, really
|
* Ask Ivan, really
|
||||||
|
|||||||
3
debian/control
vendored
3
debian/control
vendored
@@ -5,7 +5,6 @@ Maintainer: Ivan Klyuchnikov <ivan.kluchnikov@fairwaves.ru>
|
|||||||
Build-Depends: debhelper (>= 9),
|
Build-Depends: debhelper (>= 9),
|
||||||
autotools-dev,
|
autotools-dev,
|
||||||
autoconf-archive,
|
autoconf-archive,
|
||||||
libdbd-sqlite3,
|
|
||||||
libsqlite3-dev,
|
libsqlite3-dev,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
dh-autoreconf,
|
dh-autoreconf,
|
||||||
@@ -20,7 +19,7 @@ Homepage: https://projects.osmocom.org/projects/osmotrx
|
|||||||
|
|
||||||
Package: osmo-trx
|
Package: osmo-trx
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3
|
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||||
Description: SDR transceiver that implements Layer 1 of a GSM BTS
|
Description: SDR transceiver that implements Layer 1 of a GSM BTS
|
||||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||||
physical layer of a BTS comprising the following 3GPP specifications:
|
physical layer of a BTS comprising the following 3GPP specifications:
|
||||||
|
|||||||
151
git-version-gen
Executable file
151
git-version-gen
Executable file
@@ -0,0 +1,151 @@
|
|||||||
|
#!/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