mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-01 04:33:33 +00:00
Compare commits
269 Commits
achemeris/
...
fairwaves/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2061843c3d | ||
|
|
63d0f2a496 | ||
|
|
6459ddc55c | ||
|
|
7a33c7221b | ||
|
|
dd62d4baa0 | ||
|
|
09130c81a7 | ||
|
|
0c4e24d197 | ||
|
|
15f9d95f5f | ||
|
|
73dbccda78 | ||
|
|
5e65b531e0 | ||
|
|
b992d0a515 | ||
|
|
d6ae8648ff | ||
|
|
e51a8f029e | ||
|
|
e8ae9fcf38 | ||
|
|
f5bf33b287 | ||
|
|
fe9769833f | ||
|
|
7e07cf2346 | ||
|
|
dfe0aef184 | ||
|
|
131f82bfac | ||
|
|
78b5627fa1 | ||
|
|
de116e90c0 | ||
|
|
15da7e1f7e | ||
|
|
6031734f44 | ||
|
|
5d2a36a113 | ||
|
|
2af14407a8 | ||
|
|
92bdfb86ac | ||
|
|
ae91f13ecb | ||
|
|
9d53ecf666 | ||
|
|
e0c12189d4 | ||
|
|
1470fcdb5a | ||
|
|
6e1dffd486 | ||
|
|
0229d22d2e | ||
|
|
f7717acd0c | ||
|
|
b34e60c105 | ||
|
|
4e6c938024 | ||
|
|
4aa548f0c2 | ||
|
|
f9e78beea5 | ||
|
|
f0189c47be | ||
|
|
c708816be1 | ||
|
|
e56bf3a0e5 | ||
|
|
38b69871ae | ||
|
|
7db522b6d9 | ||
|
|
ae09b04e26 | ||
|
|
b61c610cd9 | ||
|
|
132fb247b1 | ||
|
|
1c0b8b355c | ||
|
|
1dd05cf35a | ||
|
|
14d13b67dc | ||
|
|
89bca9b2de | ||
|
|
9270a5aa2e | ||
|
|
4793f4679b | ||
|
|
802b86502d | ||
|
|
a93f789e50 | ||
|
|
72bf762b42 | ||
|
|
2dee3e996e | ||
|
|
1f1cebb2e5 | ||
|
|
d1b28bd766 | ||
|
|
833e97e9ba | ||
|
|
e6d059f0c9 | ||
|
|
012a1b345b | ||
|
|
80cb08071b | ||
|
|
44c7f41d75 | ||
|
|
9436fbbf3c | ||
|
|
93ca09ea61 | ||
|
|
365bc38bee | ||
|
|
43242efc85 | ||
|
|
76b98cf236 | ||
|
|
aa15d62a8c | ||
|
|
2e5e2c537b | ||
|
|
8f0ccf618d | ||
|
|
06676ead63 | ||
|
|
4609f3285c | ||
|
|
7c741ec6a6 | ||
|
|
2f3e60bc1f | ||
|
|
cbfef6e40a | ||
|
|
b577ef014f | ||
|
|
c37594f3b9 | ||
|
|
ffee30d190 | ||
|
|
24575a6530 | ||
|
|
1e9801411b | ||
|
|
64464e6c34 | ||
|
|
e88710881b | ||
|
|
a84e162672 | ||
|
|
7676427816 | ||
|
|
35222296fe | ||
|
|
28670fb5da | ||
|
|
05c6feb71d | ||
|
|
2e4ed10722 | ||
|
|
c8c4eac55e | ||
|
|
37c52c79cf | ||
|
|
58e9591f9e | ||
|
|
19174f581b | ||
|
|
1ba69e7762 | ||
|
|
f931cf226b | ||
|
|
e476231deb | ||
|
|
e90c24c8d5 | ||
|
|
3b093bb13b | ||
|
|
3f4a13f049 | ||
|
|
0fe41a583c | ||
|
|
a5e0f1cdba | ||
|
|
2c650a6895 | ||
|
|
d4555f267e | ||
|
|
047956259b | ||
|
|
d2b070369d | ||
|
|
9664c3a6e7 | ||
|
|
1ab5e7f7bc | ||
|
|
5efe05021a | ||
|
|
78d1fc9a13 | ||
|
|
a8cf208616 | ||
|
|
f84232d30a | ||
|
|
9bd649ec73 | ||
|
|
871b87829f | ||
|
|
d17b189cbc | ||
|
|
7fec3030d4 | ||
|
|
af717b2d3c | ||
|
|
8ee2f38a87 | ||
|
|
4dfd64aa9e | ||
|
|
b0aefcbf47 | ||
|
|
d325343ecc | ||
|
|
5cd70dc4ec | ||
|
|
465694027b | ||
|
|
2079a3c664 | ||
|
|
99cf930f9a | ||
|
|
283b22dbce | ||
|
|
f147b17447 | ||
|
|
d4d3daa12e | ||
|
|
c312905f43 | ||
|
|
c4eab8795f | ||
|
|
cc6f79b1c0 | ||
|
|
5a0680655f | ||
|
|
3722920100 | ||
|
|
f3b9af65ed | ||
|
|
e692ce986c | ||
|
|
81c6873205 | ||
|
|
c052aa1d4c | ||
|
|
130a8007fa | ||
|
|
72e8619632 | ||
|
|
2beb1adcea | ||
|
|
2b542100a0 | ||
|
|
2268c8558c | ||
|
|
50747dc65d | ||
|
|
1e9b4d57da | ||
|
|
954b118bfa | ||
|
|
dbe26abcb9 | ||
|
|
e8905a03a5 | ||
|
|
909ffbfd23 | ||
|
|
351fd76706 | ||
|
|
6a2bf0d87b | ||
|
|
2966048b07 | ||
|
|
f5fd578d60 | ||
|
|
57ef87d061 | ||
|
|
5721920a08 | ||
|
|
194a9b1982 | ||
|
|
1fe5282133 | ||
|
|
4438a9fd8f | ||
|
|
64ad712daa | ||
|
|
5c7c178369 | ||
|
|
90f7a01d1d | ||
|
|
e171425b99 | ||
|
|
4d029d8965 | ||
|
|
6613331459 | ||
|
|
577cd020c1 | ||
|
|
88bbf1aafd | ||
|
|
2cc2ddda41 | ||
|
|
d7610cf0b8 | ||
|
|
722d4f70a4 | ||
|
|
93b7f37309 | ||
|
|
4ad9ea69ab | ||
|
|
eb54bddf47 | ||
|
|
a4d1a41244 | ||
|
|
b999759175 | ||
|
|
1ae25561fa | ||
|
|
187225cf33 | ||
|
|
ccb73e15f3 | ||
|
|
a5c83aeb56 | ||
|
|
c283916e41 | ||
|
|
cecfb2e8f9 | ||
|
|
8e17df7374 | ||
|
|
3ebc772dc2 | ||
|
|
18d3b833bc | ||
|
|
e788239fba | ||
|
|
635f1bf2af | ||
|
|
15d743efaf | ||
|
|
af506441b3 | ||
|
|
4de70be9a6 | ||
|
|
fb827d04ba | ||
|
|
2c79110969 | ||
|
|
85b179d125 | ||
|
|
2e622ff131 | ||
|
|
3f32ab5afa | ||
|
|
8c33679fa5 | ||
|
|
477b77c558 | ||
|
|
d3fccea05f | ||
|
|
cb269a32dd | ||
|
|
1882099d15 | ||
|
|
a0179e37f8 | ||
|
|
ef25dba4e7 | ||
|
|
2d0c00bd3d | ||
|
|
e90a42becc | ||
|
|
30421a7e25 | ||
|
|
34bbef754f | ||
|
|
2c1f85a10c | ||
|
|
a2fe91a688 | ||
|
|
e1ce92599a | ||
|
|
b075dd2f73 | ||
|
|
94edaaeee6 | ||
|
|
0e0e1f4363 | ||
|
|
d0f3ca3e94 | ||
|
|
f8bc7c351f | ||
|
|
a4cf48cf8b | ||
|
|
7940335586 | ||
|
|
f79c4d06ff | ||
|
|
20eb6d64fd | ||
|
|
e0fa2bfd93 | ||
|
|
6f4906e375 | ||
|
|
0a3dc4c210 | ||
|
|
acc22fa3ff | ||
|
|
7553aa973f | ||
|
|
7e4e536b1b | ||
|
|
204a9f135a | ||
|
|
0169b310de | ||
|
|
66e2dd2543 | ||
|
|
f078273a8a | ||
|
|
d647ec5dc1 | ||
|
|
c289d7a409 | ||
|
|
035bee5c32 | ||
|
|
cf910dcdda | ||
|
|
69762fd7c6 | ||
|
|
fffd987f22 | ||
|
|
17bbb9b755 | ||
|
|
a1a3ab4bab | ||
|
|
fa3a787ccb | ||
|
|
010fff783b | ||
|
|
61b4a6ad9f | ||
|
|
de1648ca6b | ||
|
|
3952d80d05 | ||
|
|
fe269fe31d | ||
|
|
c064124429 | ||
|
|
69d14c9a14 | ||
|
|
c1f7c42a33 | ||
|
|
2c282f5e12 | ||
|
|
92c16df875 | ||
|
|
d5a80c3dc6 | ||
|
|
dafb33700e | ||
|
|
0e44ab360e | ||
|
|
092f757ec2 | ||
|
|
a57bc8a3b9 | ||
|
|
84c60f6679 | ||
|
|
865bca42d6 | ||
|
|
c88d8d53f8 | ||
|
|
9ccd9f2c3c | ||
|
|
8181b0104a | ||
|
|
9471d7635a | ||
|
|
03e6ecf977 | ||
|
|
3eaae80c90 | ||
|
|
e57004d0c3 | ||
|
|
6aa1f18f41 | ||
|
|
e5dcfc4f80 | ||
|
|
83e0689e76 | ||
|
|
d24cc2cd96 | ||
|
|
ddd6defb43 | ||
|
|
96794cb746 | ||
|
|
7e06806ff0 | ||
|
|
cb69f08410 | ||
|
|
312e387630 | ||
|
|
f2293b8cfa | ||
|
|
e3e8814948 | ||
|
|
02d88d1380 | ||
|
|
b4cb4e23c0 |
48
.gitignore
vendored
Normal file
48
.gitignore
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# build results
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
Transceiver52M/osmo-trx
|
||||
Transceiver52M/osmo-trx-gen
|
||||
Transceiver52M/osmo-trx-dec
|
||||
|
||||
# tests
|
||||
CommonLibs/BitVectorTest
|
||||
CommonLibs/ConfigurationTest
|
||||
CommonLibs/F16Test
|
||||
CommonLibs/InterthreadTest
|
||||
CommonLibs/LogTest
|
||||
CommonLibs/RegexpTest
|
||||
CommonLibs/SocketsTest
|
||||
CommonLibs/TimevalTest
|
||||
CommonLibs/URLEncodeTest
|
||||
CommonLibs/VectorTest
|
||||
CommonLibs/PRBSTest
|
||||
|
||||
# automake/autoconf
|
||||
*.in
|
||||
.deps
|
||||
.libs
|
||||
.dirstamp
|
||||
*~
|
||||
Makefile
|
||||
config.log
|
||||
config.status
|
||||
config.h
|
||||
config.guess
|
||||
config.sub
|
||||
config/*
|
||||
configure
|
||||
compile
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
depcomp
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
stamp-h1
|
||||
INSTALL
|
||||
|
||||
# vim
|
||||
*.sw?
|
||||
3
.gitreview
Normal file
3
.gitreview
Normal file
@@ -0,0 +1,3 @@
|
||||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=osmo-trx
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <math.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -199,49 +200,6 @@ void BitVector::LSB8MSB()
|
||||
|
||||
|
||||
|
||||
uint64_t BitVector::syndrome(Generator& gen) const
|
||||
{
|
||||
gen.clear();
|
||||
const char *dp = mStart;
|
||||
while (dp<mEnd) gen.syndromeShift(*dp++);
|
||||
return gen.state();
|
||||
}
|
||||
|
||||
|
||||
uint64_t BitVector::parity(Generator& gen) const
|
||||
{
|
||||
gen.clear();
|
||||
const char *dp = mStart;
|
||||
while (dp<mEnd) gen.encoderShift(*dp++);
|
||||
return gen.state();
|
||||
}
|
||||
|
||||
|
||||
void BitVector::encode(const ViterbiR2O4& coder, BitVector& target)
|
||||
{
|
||||
size_t sz = size();
|
||||
assert(sz*coder.iRate() == target.size());
|
||||
|
||||
// Build a "history" array where each element contains the full history.
|
||||
uint32_t history[sz];
|
||||
uint32_t accum = 0;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
accum = (accum<<1) | bit(i);
|
||||
history[i] = accum;
|
||||
}
|
||||
|
||||
// Look up histories in the pre-generated state table.
|
||||
char *op = target.begin();
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
unsigned index = coder.cMask() & history[i];
|
||||
for (unsigned g=0; g<coder.iRate(); g++) {
|
||||
*op++ = coder.stateTable(g,index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned BitVector::sum() const
|
||||
{
|
||||
unsigned sum = 0;
|
||||
@@ -287,148 +245,12 @@ ostream& operator<<(ostream& os, const BitVector& hv)
|
||||
|
||||
|
||||
|
||||
ViterbiR2O4::ViterbiR2O4()
|
||||
{
|
||||
assert(mDeferral < 32);
|
||||
mCoeffs[0] = 0x019;
|
||||
mCoeffs[1] = 0x01b;
|
||||
computeStateTables(0);
|
||||
computeStateTables(1);
|
||||
computeGeneratorTable();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::initializeStates()
|
||||
{
|
||||
for (unsigned i=0; i<mIStates; i++) clear(mSurvivors[i]);
|
||||
for (unsigned i=0; i<mNumCands; i++) clear(mCandidates[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::computeStateTables(unsigned g)
|
||||
{
|
||||
assert(g<mIRate);
|
||||
for (unsigned state=0; state<mIStates; state++) {
|
||||
// 0 input
|
||||
uint32_t inputVal = state<<1;
|
||||
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
|
||||
// 1 input
|
||||
inputVal |= 1;
|
||||
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
|
||||
}
|
||||
}
|
||||
|
||||
void ViterbiR2O4::computeGeneratorTable()
|
||||
{
|
||||
for (unsigned index=0; index<mIStates*2; index++) {
|
||||
mGeneratorTable[index] = (mStateTable[0][index]<<1) | mStateTable[1][index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::branchCandidates()
|
||||
{
|
||||
// Branch to generate new input states.
|
||||
const vCand *sp = mSurvivors;
|
||||
for (unsigned i=0; i<mNumCands; i+=2) {
|
||||
// extend and suffix
|
||||
const uint32_t iState0 = (sp->iState) << 1; // input state for 0
|
||||
const uint32_t iState1 = iState0 | 0x01; // input state for 1
|
||||
const uint32_t oStateShifted = (sp->oState) << mIRate; // shifted output
|
||||
const float cost = sp->cost;
|
||||
sp++;
|
||||
// 0 input extension
|
||||
mCandidates[i].cost = cost;
|
||||
mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask];
|
||||
mCandidates[i].iState = iState0;
|
||||
// 1 input extension
|
||||
mCandidates[i+1].cost = cost;
|
||||
mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask];
|
||||
mCandidates[i+1].iState = iState1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost)
|
||||
{
|
||||
const float *cTab[2] = {matchCost,mismatchCost};
|
||||
for (unsigned i=0; i<mNumCands; i++) {
|
||||
vCand& thisCand = mCandidates[i];
|
||||
// We examine input bits 2 at a time for a rate 1/2 coder.
|
||||
const unsigned mismatched = inSample ^ (thisCand.oState);
|
||||
thisCand.cost += cTab[mismatched&0x01][1] + cTab[(mismatched>>1)&0x01][0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViterbiR2O4::pruneCandidates()
|
||||
{
|
||||
const vCand* c1 = mCandidates; // 0-prefix
|
||||
const vCand* c2 = mCandidates + mIStates; // 1-prefix
|
||||
for (unsigned i=0; i<mIStates; i++) {
|
||||
if (c1[i].cost < c2[i].cost) mSurvivors[i] = c1[i];
|
||||
else mSurvivors[i] = c2[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const ViterbiR2O4::vCand& ViterbiR2O4::minCost() const
|
||||
{
|
||||
int minIndex = 0;
|
||||
float minCost = mSurvivors[0].cost;
|
||||
for (unsigned i=1; i<mIStates; i++) {
|
||||
const float thisCost = mSurvivors[i].cost;
|
||||
if (thisCost>=minCost) continue;
|
||||
minCost = thisCost;
|
||||
minIndex=i;
|
||||
}
|
||||
return mSurvivors[minIndex];
|
||||
}
|
||||
|
||||
|
||||
const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs)
|
||||
{
|
||||
branchCandidates();
|
||||
getSoftCostMetrics(inSample,probs,iprobs);
|
||||
pruneCandidates();
|
||||
return minCost();
|
||||
}
|
||||
|
||||
|
||||
uint64_t Parity::syndrome(const BitVector& receivedCodeword)
|
||||
{
|
||||
return receivedCodeword.syndrome(*this);
|
||||
}
|
||||
|
||||
|
||||
void Parity::writeParityWord(const BitVector& data, BitVector& parityTarget, bool invert)
|
||||
{
|
||||
uint64_t pWord = data.parity(*this);
|
||||
if (invert) pWord = ~pWord;
|
||||
parityTarget.fillField(0,pWord,size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SoftVector::SoftVector(const BitVector& source)
|
||||
{
|
||||
resize(source.size());
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
if (source.bit(i)) mStart[i]=1.0F;
|
||||
else mStart[i]=0.0F;
|
||||
else mStart[i]=-1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,102 +260,20 @@ BitVector SoftVector::sliced() const
|
||||
size_t sz = size();
|
||||
BitVector newSig(sz);
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
if (mStart[i]>0.5F) newSig[i]=1;
|
||||
if (mStart[i]>0.0F) newSig[i]=1;
|
||||
else newSig[i] = 0;
|
||||
}
|
||||
return newSig;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
|
||||
{
|
||||
const size_t sz = size();
|
||||
const unsigned deferral = decoder.deferral();
|
||||
const size_t ctsz = sz + deferral*decoder.iRate();
|
||||
assert(sz <= decoder.iRate()*target.size());
|
||||
|
||||
// Build a "history" array where each element contains the full history.
|
||||
uint32_t history[ctsz];
|
||||
{
|
||||
BitVector bits = sliced();
|
||||
uint32_t accum = 0;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
accum = (accum<<1) | bits.bit(i);
|
||||
history[i] = accum;
|
||||
}
|
||||
// Repeat last bit at the end.
|
||||
for (size_t i=sz; i<ctsz; i++) {
|
||||
accum = (accum<<1) | (accum & 0x01);
|
||||
history[i] = accum;
|
||||
}
|
||||
}
|
||||
|
||||
// Precompute metric tables.
|
||||
float matchCostTable[ctsz];
|
||||
float mismatchCostTable[ctsz];
|
||||
{
|
||||
const float *dp = mStart;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
// pVal is the probability that a bit is correct.
|
||||
// ipVal is the probability that a bit is incorrect.
|
||||
float pVal = dp[i];
|
||||
if (pVal>0.5F) pVal = 1.0F-pVal;
|
||||
float ipVal = 1.0F-pVal;
|
||||
// This is a cheap approximation to an ideal cost function.
|
||||
if (pVal<0.01F) pVal = 0.01;
|
||||
if (ipVal<0.01F) ipVal = 0.01;
|
||||
matchCostTable[i] = 0.25F/ipVal;
|
||||
mismatchCostTable[i] = 0.25F/pVal;
|
||||
}
|
||||
|
||||
// pad end of table with unknowns
|
||||
for (size_t i=sz; i<ctsz; i++) {
|
||||
matchCostTable[i] = 0.5F;
|
||||
mismatchCostTable[i] = 0.5F;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
decoder.initializeStates();
|
||||
// Each sample of history[] carries its history.
|
||||
// So we only have to process every iRate-th sample.
|
||||
const unsigned step = decoder.iRate();
|
||||
// input pointer
|
||||
const uint32_t *ip = history + step - 1;
|
||||
// output pointers
|
||||
char *op = target.begin();
|
||||
const char *const opt = target.end();
|
||||
// table pointers
|
||||
const float* match = matchCostTable;
|
||||
const float* mismatch = mismatchCostTable;
|
||||
size_t oCount = 0;
|
||||
while (op<opt) {
|
||||
// Viterbi algorithm
|
||||
assert(match-matchCostTable<sizeof(matchCostTable)/sizeof(matchCostTable[0])-1);
|
||||
assert(mismatch-mismatchCostTable<sizeof(mismatchCostTable)/sizeof(mismatchCostTable[0])-1);
|
||||
const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch);
|
||||
ip += step;
|
||||
match += step;
|
||||
mismatch += step;
|
||||
// output
|
||||
if (oCount>=deferral) *op++ = (minCost.iState >> deferral)&0x01;
|
||||
oCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// (pat) Added 6-22-2012
|
||||
float SoftVector::getEnergy(float *plow) const
|
||||
{
|
||||
const SoftVector &vec = *this;
|
||||
int len = vec.size();
|
||||
float avg = 0; float low = 1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
float bit = vec[i];
|
||||
float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
|
||||
float energy = fabsf(vec[i]);
|
||||
if (energy < low) low = energy;
|
||||
avg += energy/len;
|
||||
}
|
||||
@@ -545,8 +285,12 @@ float SoftVector::getEnergy(float *plow) const
|
||||
ostream& operator<<(ostream& os, const SoftVector& sv)
|
||||
{
|
||||
for (size_t i=0; i<sv.size(); i++) {
|
||||
if (sv[i]<0.25) os << "0";
|
||||
else if (sv[i]>0.75) os << "1";
|
||||
if (sv[i]<-0.5) os << "0";
|
||||
else if (sv[i]<-0.25) os << "o";
|
||||
else if (sv[i]<0.0) os << ".";
|
||||
else if (sv[i]>0.5) os << "1";
|
||||
else if (sv[i]>0.25) os << "|";
|
||||
else if (sv[i]>0.0) os << "'";
|
||||
else os << "-";
|
||||
}
|
||||
return os;
|
||||
|
||||
@@ -30,201 +30,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
class BitVector;
|
||||
class SoftVector;
|
||||
|
||||
|
||||
|
||||
/** Shift-register (LFSR) generator. */
|
||||
class Generator {
|
||||
|
||||
private:
|
||||
|
||||
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
|
||||
uint64_t mState; ///< shift register state. LSB is most recent.
|
||||
uint64_t mMask; ///< mask for reading state
|
||||
unsigned mLen; ///< number of bits used in shift register
|
||||
unsigned mLen_1; ///< mLen - 1
|
||||
|
||||
public:
|
||||
|
||||
Generator(uint64_t wCoeff, unsigned wLen)
|
||||
:mCoeff(wCoeff),mState(0),
|
||||
mMask((1ULL<<wLen)-1),
|
||||
mLen(wLen),mLen_1(wLen-1)
|
||||
{ assert(wLen<64); }
|
||||
|
||||
void clear() { mState=0; }
|
||||
|
||||
/**@name Accessors */
|
||||
//@{
|
||||
uint64_t state() const { return mState & mMask; }
|
||||
unsigned size() const { return mLen; }
|
||||
//@}
|
||||
|
||||
/**
|
||||
Calculate one bit of a syndrome.
|
||||
This is in the .h for inlining.
|
||||
*/
|
||||
void syndromeShift(unsigned inBit)
|
||||
{
|
||||
const unsigned fb = (mState>>(mLen_1)) & 0x01;
|
||||
mState = (mState<<1) ^ (inBit & 0x01);
|
||||
if (fb) mState ^= mCoeff;
|
||||
}
|
||||
|
||||
/**
|
||||
Update the generator state by one cycle.
|
||||
This is in the .h for inlining.
|
||||
*/
|
||||
void encoderShift(unsigned inBit)
|
||||
{
|
||||
const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01;
|
||||
mState <<= 1;
|
||||
if (fb) mState ^= mCoeff;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Parity (CRC-type) generator and checker based on a Generator. */
|
||||
class Parity : public Generator {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned mCodewordSize;
|
||||
|
||||
public:
|
||||
|
||||
Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize)
|
||||
:Generator(wCoefficients, wParitySize),
|
||||
mCodewordSize(wCodewordSize)
|
||||
{ }
|
||||
|
||||
/** Compute the parity word and write it into the target segment. */
|
||||
void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true);
|
||||
|
||||
/** Compute the syndrome of a received sequence. */
|
||||
uint64_t syndrome(const BitVector& receivedCodeword);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Class to represent convolutional coders/decoders of rate 1/2, memory length 4.
|
||||
This is the "workhorse" coder for most GSM channels.
|
||||
*/
|
||||
class ViterbiR2O4 {
|
||||
|
||||
private:
|
||||
/**name Lots of precomputed elements so the compiler can optimize like hell. */
|
||||
//@{
|
||||
/**@name Core values. */
|
||||
//@{
|
||||
static const unsigned mIRate = 2; ///< reciprocal of rate
|
||||
static const unsigned mOrder = 4; ///< memory length of generators
|
||||
//@}
|
||||
/**@name Derived values. */
|
||||
//@{
|
||||
static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors
|
||||
static const uint32_t mSMask = mIStates-1; ///< survivor mask
|
||||
static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask
|
||||
static const uint32_t mOMask = (0x01<<mIRate)-1; ///< ouput mask, all iRate low bits set
|
||||
static const unsigned mNumCands = mIStates*2; ///< number of candidates to generate during branching
|
||||
static const unsigned mDeferral = 6*mOrder; ///< deferral to be used
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/** Precomputed tables. */
|
||||
//@{
|
||||
uint32_t mCoeffs[mIRate]; ///< polynomial for each generator
|
||||
uint32_t mStateTable[mIRate][2*mIStates]; ///< precomputed generator output tables
|
||||
uint32_t mGeneratorTable[2*mIStates]; ///< precomputed coder output table
|
||||
//@}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
A candidate sequence in a Viterbi decoder.
|
||||
The 32-bit state register can support a deferral of 6 with a 4th-order coder.
|
||||
*/
|
||||
typedef struct candStruct {
|
||||
uint32_t iState; ///< encoder input associated with this candidate
|
||||
uint32_t oState; ///< encoder output associated with this candidate
|
||||
float cost; ///< cost (metric value), float to support soft inputs
|
||||
} vCand;
|
||||
|
||||
/** Clear a structure. */
|
||||
void clear(vCand& v)
|
||||
{
|
||||
v.iState=0;
|
||||
v.oState=0;
|
||||
v.cost=0;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**@name Survivors and candidates. */
|
||||
//@{
|
||||
vCand mSurvivors[mIStates]; ///< current survivor pool
|
||||
vCand mCandidates[2*mIStates]; ///< current candidate pool
|
||||
//@}
|
||||
|
||||
public:
|
||||
|
||||
unsigned iRate() const { return mIRate; }
|
||||
uint32_t cMask() const { return mCMask; }
|
||||
uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
|
||||
unsigned deferral() const { return mDeferral; }
|
||||
|
||||
|
||||
ViterbiR2O4();
|
||||
|
||||
/** Set all cost metrics to zero. */
|
||||
void initializeStates();
|
||||
|
||||
/**
|
||||
Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
|
||||
@return reference to minimum-cost candidate.
|
||||
*/
|
||||
const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
|
||||
|
||||
private:
|
||||
|
||||
/** Branch survivors into new candidates. */
|
||||
void branchCandidates();
|
||||
|
||||
/** Compute cost metrics for soft-inputs. */
|
||||
void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
|
||||
|
||||
/** Select survivors from the candidate set. */
|
||||
void pruneCandidates();
|
||||
|
||||
/** Find the minimum cost survivor. */
|
||||
const vCand& minCost() const;
|
||||
|
||||
/**
|
||||
Precompute the state tables.
|
||||
@param g Generator index 0..((1/rate)-1)
|
||||
*/
|
||||
void computeStateTables(unsigned g);
|
||||
|
||||
/**
|
||||
Precompute the generator outputs.
|
||||
mCoeffs must be defined first.
|
||||
*/
|
||||
void computeGeneratorTable();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class BitVector : public Vector<char> {
|
||||
|
||||
|
||||
@@ -282,16 +87,6 @@ class BitVector : public Vector<char> {
|
||||
|
||||
void zero() { fill(0); }
|
||||
|
||||
/**@name FEC operations. */
|
||||
//@{
|
||||
/** Calculate the syndrome of the vector with the given Generator. */
|
||||
uint64_t syndrome(Generator& gen) const;
|
||||
/** Calculate the parity word for the vector with the given Generator. */
|
||||
uint64_t parity(Generator& gen) const;
|
||||
/** Encode the signal with the GSM rate 1/2 convolutional encoder. */
|
||||
void encode(const ViterbiR2O4& encoder, BitVector& target);
|
||||
//@}
|
||||
|
||||
|
||||
/** Invert 0<->1. */
|
||||
void invert();
|
||||
@@ -427,23 +222,20 @@ class SoftVector: public Vector<float> {
|
||||
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
|
||||
//@}
|
||||
|
||||
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
|
||||
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
|
||||
|
||||
// (pat) How good is the SoftVector in the sense of the bits being solid?
|
||||
// Result of 1 is perfect and 0 means all the bits were 0.5
|
||||
// How good is the SoftVector in the sense of the bits being solid?
|
||||
// Result of 1 is perfect and 0 means all the bits were 0.0
|
||||
// If plow is non-NULL, also return the lowest energy bit.
|
||||
float getEnergy(float *low=0) const;
|
||||
|
||||
/** Fill with "unknown" values. */
|
||||
void unknown() { fill(0.5F); }
|
||||
void unknown() { fill(0.0F); }
|
||||
|
||||
/** Return a hard bit value from a given index by slicing. */
|
||||
bool bit(size_t index) const
|
||||
{
|
||||
const float *dp = mStart+index;
|
||||
assert(dp<mEnd);
|
||||
return (*dp)>0.5F;
|
||||
return (*dp)>0.0F;
|
||||
}
|
||||
|
||||
/** Slice the whole signal into bits. */
|
||||
|
||||
@@ -35,27 +35,6 @@ using namespace std;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BitVector v1("0000111100111100101011110000");
|
||||
cout << v1 << endl;
|
||||
v1.LSB8MSB();
|
||||
cout << v1 << endl;
|
||||
ViterbiR2O4 vCoder;
|
||||
BitVector v2(v1.size()*2);
|
||||
v1.encode(vCoder,v2);
|
||||
cout << v2 << endl;
|
||||
SoftVector sv2(v2);
|
||||
cout << sv2 << endl;
|
||||
for (unsigned i=0; i<sv2.size()/4; i++) sv2[random()%sv2.size()]=0.5;
|
||||
cout << sv2 << endl;
|
||||
BitVector v3(v1.size());
|
||||
sv2.decode(vCoder,v3);
|
||||
cout << v3 << endl;
|
||||
|
||||
cout << v3.segment(3,4) << endl;
|
||||
|
||||
BitVector v4(v3.segment(0,4),v3.segment(8,4));
|
||||
cout << v4 << endl;
|
||||
|
||||
BitVector v5("000011110000");
|
||||
int r1 = v5.peekField(0,8);
|
||||
int r2 = v5.peekField(4,4);
|
||||
@@ -70,13 +49,6 @@ int main(int argc, char *argv[])
|
||||
v5.reverse8();
|
||||
cout << v5 << endl;
|
||||
|
||||
BitVector mC = "000000000000111100000000000001110000011100001101000011000000000000000111000011110000100100001010000010100000101000001010000010100000010000000000000000000000000000000000000000000000001100001111000000000000000000000000000000000000000000000000000010010000101000001010000010100000101000001010000001000000000000000000000000110000111100000000000001110000101000001100000001000000000000";
|
||||
SoftVector mCS(mC);
|
||||
BitVector mU(mC.size()/2);
|
||||
mCS.decode(vCoder,mU);
|
||||
cout << "c=" << mCS << endl;
|
||||
cout << "u=" << mU << endl;
|
||||
|
||||
|
||||
unsigned char ts[9] = "abcdefgh";
|
||||
BitVector tp(70);
|
||||
|
||||
@@ -53,6 +53,23 @@ static const char* createConfigTable = {
|
||||
")"
|
||||
};
|
||||
|
||||
static std::string replaceAll(const std::string input, const std::string search, const std::string replace)
|
||||
{
|
||||
std::string output = input;
|
||||
size_t index = 0;
|
||||
|
||||
while (true) {
|
||||
index = output.find(search, index);
|
||||
if (index == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
output.replace(index, replace.length(), replace);
|
||||
index += replace.length();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
float ConfigurationRecord::floatNumber() const
|
||||
@@ -96,7 +113,7 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
|
||||
"Maximum number of alarms to remember inside the application."
|
||||
);
|
||||
mSchema[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
delete tmp;
|
||||
|
||||
tmp = new ConfigurationKey("Log.File","",
|
||||
"",
|
||||
@@ -110,7 +127,7 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
|
||||
"To disable again, execute \"unconfig Log.File\"."
|
||||
);
|
||||
mSchema[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
delete tmp;
|
||||
|
||||
tmp = new ConfigurationKey("Log.Level","NOTICE",
|
||||
"",
|
||||
@@ -128,7 +145,7 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
|
||||
"Default logging level when no other level is defined for a file."
|
||||
);
|
||||
mSchema[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
delete tmp;
|
||||
|
||||
// Add application specific schema
|
||||
mSchema.insert(wSchema.begin(), wSchema.end());
|
||||
@@ -259,8 +276,8 @@ string ConfigurationTable::getTeX(const std::string& program, const std::string&
|
||||
ss << "% END AUTO-GENERATED CONTENT" << endl;
|
||||
ss << endl;
|
||||
|
||||
string tmp = Utils::replaceAll(ss.str(), "^", "\\^");
|
||||
return Utils::replaceAll(tmp, "_", "\\_");
|
||||
string tmp = replaceAll(ss.str(), "^", "\\^");
|
||||
return replaceAll(tmp, "_", "\\_");
|
||||
}
|
||||
|
||||
bool ConfigurationTable::defines(const string& key)
|
||||
|
||||
@@ -47,7 +47,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
gConfig.setUpdateHook(purgeConfig);
|
||||
|
||||
char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
|
||||
const char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
gConfig.set(keys[i],i);
|
||||
|
||||
210
CommonLibs/F16.h
210
CommonLibs/F16.h
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef F16_H
|
||||
#define F16_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ostream>
|
||||
|
||||
|
||||
|
||||
/** Round a float to the appropriate F16 value. */
|
||||
inline int32_t _f16_round(float f)
|
||||
{
|
||||
if (f>0.0F) return (int32_t)(f+0.5F);
|
||||
if (f<0.0F) return (int32_t)(f-0.5F);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** A class for F15.16 fixed point arithmetic with saturation. */
|
||||
class F16 {
|
||||
|
||||
|
||||
private:
|
||||
|
||||
int32_t mV;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
F16() {}
|
||||
|
||||
F16(int i) { mV = i<<16; }
|
||||
F16(float f) { mV = _f16_round(f*65536.0F); }
|
||||
F16(double f) { mV = _f16_round((float)f*65536.0F); }
|
||||
|
||||
int32_t& raw() { return mV; }
|
||||
const int32_t& raw() const { return mV; }
|
||||
|
||||
float f() const { return mV/65536.0F; }
|
||||
|
||||
//operator float() const { return mV/65536.0F; }
|
||||
//operator int() const { return mV>>16; }
|
||||
|
||||
F16 operator=(float f)
|
||||
{
|
||||
mV = _f16_round(f*65536.0F);
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator=(int i)
|
||||
{
|
||||
mV = i<<16;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator=(const F16& other)
|
||||
{
|
||||
mV = other.mV;
|
||||
return mV;
|
||||
}
|
||||
|
||||
F16 operator+(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV + other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator+=(const F16& other)
|
||||
{
|
||||
mV += other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator-(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV - other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator-=(const F16& other)
|
||||
{
|
||||
mV -= other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator*(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
int64_t p = (int64_t)mV * (int64_t)other.mV;
|
||||
retVal.mV = p>>16;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator*=(const F16& other)
|
||||
{
|
||||
int64_t p = (int64_t)mV * (int64_t)other.mV;
|
||||
mV = p>>16;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator*(float f) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV * f;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator*=(float f)
|
||||
{
|
||||
mV *= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator/(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
int64_t pV = (int64_t)mV << 16;
|
||||
retVal.mV = pV / other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator/=(const F16& other)
|
||||
{
|
||||
int64_t pV = (int64_t)mV << 16;
|
||||
mV = pV / other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator/(float f) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV / f;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator/=(float f)
|
||||
{
|
||||
mV /= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator>(const F16& other) const
|
||||
{
|
||||
return mV>other.mV;
|
||||
}
|
||||
|
||||
bool operator<(const F16& other) const
|
||||
{
|
||||
return mV<other.mV;
|
||||
}
|
||||
|
||||
bool operator==(const F16& other) const
|
||||
{
|
||||
return mV==other.mV;
|
||||
}
|
||||
|
||||
bool operator>(float f) const
|
||||
{
|
||||
return (mV/65536.0F) > f;
|
||||
}
|
||||
|
||||
bool operator<(float f) const
|
||||
{
|
||||
return (mV/65536.0F) < f;
|
||||
}
|
||||
|
||||
bool operator==(float f) const
|
||||
{
|
||||
return (mV/65536.0F) == f;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const F16& v)
|
||||
{
|
||||
os << v.f();
|
||||
return os;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "F16.h"
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
F16 a = 2.5;
|
||||
F16 b = 1.5;
|
||||
F16 c = 2.5 * 1.5;
|
||||
F16 d = c + a;
|
||||
F16 e = 10;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << endl;
|
||||
|
||||
a *= 3;
|
||||
b *= 0.3;
|
||||
c *= e;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
|
||||
|
||||
a /= 3;
|
||||
b /= 0.3;
|
||||
c = d * 0.05;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
|
||||
|
||||
F16 f = a/d;
|
||||
cout << f << ' ' << f+0.5 << endl;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
@@ -42,7 +41,6 @@
|
||||
//@{
|
||||
|
||||
|
||||
// UNUSED in osmo-trx
|
||||
/** Pointer FIFO for interthread operations. */
|
||||
// (pat) The elements in the queue are type T*, and
|
||||
// the Fifo class implements the underlying queue.
|
||||
@@ -100,7 +98,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
T* retVal = (T*)mQ.get();
|
||||
if (retVal==NULL) {
|
||||
while (retVal==NULL) {
|
||||
mWriteSignal.wait(mLock);
|
||||
retVal = (T*)mQ.get();
|
||||
}
|
||||
@@ -157,7 +155,6 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
|
||||
}
|
||||
};
|
||||
|
||||
// UNUSED in osmo-trx
|
||||
// (pat) Identical to above but with the threading problem fixed.
|
||||
template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
|
||||
|
||||
@@ -279,7 +276,6 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
|
||||
|
||||
|
||||
|
||||
// UNUSED in osmo-trx
|
||||
/** Pointer FIFO for interthread operations. */
|
||||
template <class T> class InterthreadQueueWithWait {
|
||||
|
||||
@@ -384,7 +380,7 @@ template <class T> class InterthreadQueueWithWait {
|
||||
|
||||
|
||||
|
||||
// UNUSED in osmo-trx
|
||||
|
||||
/** Thread-safe map of pointers to class D, keyed by class K. */
|
||||
template <class K, class D > class InterthreadMap {
|
||||
|
||||
@@ -648,7 +644,7 @@ template <class T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > cl
|
||||
|
||||
|
||||
|
||||
// UNUSED in osmo-trx
|
||||
|
||||
class Semaphore {
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
@@ -28,91 +27,66 @@
|
||||
|
||||
#include "Threads.h"
|
||||
#include "Interthread.h"
|
||||
#include "Configuration.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
InterthreadQueue<int> gQ;
|
||||
InterthreadMap<int,int> gMap;
|
||||
|
||||
class QueueWriter : public Thread
|
||||
void* qWriter(void*)
|
||||
{
|
||||
public:
|
||||
QueueWriter() : Thread("QueueWriter") {}
|
||||
|
||||
protected:
|
||||
virtual void runThread()
|
||||
{
|
||||
int *p;
|
||||
for (int i=0; i<20; i++) {
|
||||
p = new int;
|
||||
*p = i;
|
||||
COUT("queue write " << *p);
|
||||
gQ.write(p);
|
||||
msleep(1);
|
||||
}
|
||||
int *p;
|
||||
for (int i=0; i<20; i++) {
|
||||
p = new int;
|
||||
*p = -1;
|
||||
*p = i;
|
||||
COUT("queue write " << *p);
|
||||
gQ.write(p);
|
||||
if (random()%2) sleep(1);
|
||||
}
|
||||
};
|
||||
p = new int;
|
||||
*p = -1;
|
||||
gQ.write(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
class QueueReader : public Thread
|
||||
void* qReader(void*)
|
||||
{
|
||||
public:
|
||||
QueueReader() : Thread("QueueReader") {}
|
||||
|
||||
protected:
|
||||
virtual void runThread()
|
||||
{
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
int *p = gQ.read();
|
||||
COUT("queue read " << *p);
|
||||
if (*p<0) done=true;
|
||||
delete p;
|
||||
}
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
int *p = gQ.read();
|
||||
COUT("queue read " << *p);
|
||||
if (*p<0) done=true;
|
||||
delete p;
|
||||
}
|
||||
};
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
class MapWriter : public Thread
|
||||
void* mapWriter(void*)
|
||||
{
|
||||
public:
|
||||
MapWriter() : Thread("MapWriter") {}
|
||||
|
||||
protected:
|
||||
virtual void runThread()
|
||||
{
|
||||
int *p;
|
||||
for (int i=0; i<20; i++) {
|
||||
p = new int;
|
||||
*p = i;
|
||||
COUT("map write " << *p);
|
||||
gMap.write(i,p);
|
||||
msleep(1);
|
||||
}
|
||||
int *p;
|
||||
for (int i=0; i<20; i++) {
|
||||
p = new int;
|
||||
*p = i;
|
||||
COUT("map write " << *p);
|
||||
gMap.write(i,p);
|
||||
if (random()%2) sleep(1);
|
||||
}
|
||||
};
|
||||
return NULL;
|
||||
}
|
||||
|
||||
class MapReader : public Thread
|
||||
void* mapReader(void*)
|
||||
{
|
||||
public:
|
||||
MapReader() : Thread("MapReader") {}
|
||||
|
||||
protected:
|
||||
virtual void runThread()
|
||||
{
|
||||
for (int i=0; i<20; i++) {
|
||||
int *p = gMap.read(i);
|
||||
COUT("map read " << *p);
|
||||
// InterthreadMap will delete the pointers
|
||||
}
|
||||
for (int i=0; i<20; i++) {
|
||||
int *p = gMap.read(i);
|
||||
COUT("map read " << *p);
|
||||
// InterthreadMap will delete the pointers
|
||||
// delete p;
|
||||
}
|
||||
};
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -121,25 +95,20 @@ protected:
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
COUT("TEST 1: InterthreadQueue")
|
||||
QueueReader qReaderThread;
|
||||
QueueWriter qWriterThread;
|
||||
qReaderThread.startThread();
|
||||
qWriterThread.startThread();
|
||||
// stopThread() will wait for a thread to stop for 5 seconds, which
|
||||
// is more than enough for this test to finish.
|
||||
qReaderThread.stopThread();
|
||||
qWriterThread.stopThread();
|
||||
Thread qReaderThread;
|
||||
qReaderThread.start(qReader,NULL);
|
||||
Thread mapReaderThread;
|
||||
mapReaderThread.start(mapReader,NULL);
|
||||
|
||||
COUT("TEST 2: InterthreadMap")
|
||||
MapReader mapReaderThread;
|
||||
mapReaderThread.startThread();
|
||||
MapWriter mapWriterThread;
|
||||
mapWriterThread.startThread();
|
||||
// stopThread() will wait for a thread to stop for 5 seconds, which
|
||||
// is more than enough for this test to finish.
|
||||
mapReaderThread.stopThread();
|
||||
mapWriterThread.stopThread();
|
||||
Thread qWriterThread;
|
||||
qWriterThread.start(qWriter,NULL);
|
||||
Thread mapWriterThread;
|
||||
mapWriterThread.start(mapWriter,NULL);
|
||||
|
||||
qReaderThread.join();
|
||||
qWriterThread.join();
|
||||
mapReaderThread.join();
|
||||
mapWriterThread.join();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h> // For gettimeofday
|
||||
|
||||
#include "Configuration.h"
|
||||
#include "Logger.h"
|
||||
@@ -38,6 +39,14 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Switches to enable/disable logging targets
|
||||
// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
|
||||
bool gLogToConsole = true;
|
||||
bool gLogToSyslog = false;
|
||||
FILE *gLogToFile = NULL;
|
||||
Mutex gLogToLock;
|
||||
|
||||
|
||||
// Reference to a global config table, used all over the system.
|
||||
extern ConfigurationTable gConfig;
|
||||
|
||||
@@ -67,9 +76,6 @@ const char *levelNames[] = {
|
||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||
};
|
||||
int numLevels = 8;
|
||||
bool gLogToConsole = 0;
|
||||
FILE *gLogToFile = NULL;
|
||||
Mutex gLogToLock;
|
||||
|
||||
|
||||
int levelStringToInt(const string& name)
|
||||
@@ -106,6 +112,31 @@ int lookupLevel(const string& key)
|
||||
return level;
|
||||
}
|
||||
|
||||
static std::string format(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[300];
|
||||
va_start(ap,fmt);
|
||||
int n = vsnprintf(buf,300,fmt,ap);
|
||||
va_end(ap);
|
||||
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
const std::string timestr()
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm tm;
|
||||
gettimeofday(&tv,NULL);
|
||||
localtime_r(&tv.tv_sec,&tm);
|
||||
unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
|
||||
return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
|
||||
{
|
||||
return os << ss.str();
|
||||
}
|
||||
|
||||
int getLoggingLevel(const char* filename)
|
||||
{
|
||||
@@ -192,18 +223,20 @@ Log::~Log()
|
||||
if (mDummyInit) return;
|
||||
// Anything at or above LOG_CRIT is an "alarm".
|
||||
// Save alarms in the local list and echo them to stderr.
|
||||
if (mPriority <= LOG_CRIT) {
|
||||
if (mPriority <= LOG_ERR) {
|
||||
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||
cerr << mStream.str() << endl;
|
||||
}
|
||||
// Current logging level was already checked by the macro.
|
||||
// So just log.
|
||||
syslog(mPriority, "%s", mStream.str().c_str());
|
||||
// pat added for easy debugging.
|
||||
// Current logging level was already checked by the macro. So just log.
|
||||
// Log to syslog
|
||||
if (gLogToSyslog) {
|
||||
syslog(mPriority, "%s", mStream.str().c_str());
|
||||
}
|
||||
// Log to file and console
|
||||
if (gLogToConsole||gLogToFile) {
|
||||
int mlen = mStream.str().size();
|
||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||
gLogToLock.lock();
|
||||
ScopedLock lock(gLogToLock);
|
||||
if (gLogToConsole) {
|
||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||
// so just use std::cout.
|
||||
@@ -215,7 +248,6 @@ Log::~Log()
|
||||
if (neednl) {fputc('\n',gLogToFile);}
|
||||
fflush(gLogToFile);
|
||||
}
|
||||
gLogToLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,10 +275,9 @@ void gLogInit(const char* name, const char* level, int facility)
|
||||
gConfig.set("Log.Level",level);
|
||||
}
|
||||
|
||||
// Pat added, tired of the syslog facility.
|
||||
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
|
||||
string str = gConfig.getStr("Log.File");
|
||||
if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
||||
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
||||
const char *fn = str.c_str();
|
||||
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
|
||||
gLogToFile = fopen(fn,"w"); // New log file each time we start.
|
||||
@@ -268,9 +299,32 @@ void gLogInit(const char* name, const char* level, int facility)
|
||||
void gLogEarly(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
|
||||
va_start(args, fmt);
|
||||
vsyslog(level | LOG_USER, fmt, args);
|
||||
|
||||
if (gLogToSyslog) {
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
vsyslog(level | LOG_USER, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
if (gLogToConsole) {
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
vprintf(fmt, args_copy);
|
||||
printf("\n");
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
if (gLogToFile) {
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
vfprintf(gLogToFile, fmt, args_copy);
|
||||
fprintf(gLogToFile, "\n");
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
|
||||
|
||||
#include "Threads.h" // must be after defines above, if these files are to be allowed to use LOG()
|
||||
#include "Utils.h"
|
||||
|
||||
/**
|
||||
A C++ stream-based thread-safe logger.
|
||||
@@ -116,12 +115,15 @@ class Log {
|
||||
|
||||
std::ostringstream& get();
|
||||
};
|
||||
extern bool gLogToConsole; // Pat added for easy debugging.
|
||||
extern bool gLogToConsole; // Output log messages to stdout
|
||||
extern bool gLogToSyslog; // Output log messages to syslog
|
||||
|
||||
|
||||
|
||||
std::list<std::string> gGetLoggerAlarms(); ///< Get a copy of the recent alarm list.
|
||||
|
||||
const std::string timestr(); // A timestamp to print in messages.
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
|
||||
|
||||
/**@ Global control and initialization of the logging system. */
|
||||
//@{
|
||||
|
||||
@@ -36,82 +36,62 @@ libcommon_la_SOURCES = \
|
||||
Sockets.cpp \
|
||||
Threads.cpp \
|
||||
Timeval.cpp \
|
||||
Reporting.cpp \
|
||||
Logger.cpp \
|
||||
Configuration.cpp \
|
||||
sqlite3util.cpp \
|
||||
URLEncode.cpp \
|
||||
Utils.cpp
|
||||
sqlite3util.cpp
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
ThreadsTest \
|
||||
BitVectorTest \
|
||||
PRBSTest \
|
||||
InterthreadTest \
|
||||
SocketsTest \
|
||||
TimevalTest \
|
||||
RegexpTest \
|
||||
VectorTest \
|
||||
ConfigurationTest \
|
||||
LogTest \
|
||||
URLEncodeTest \
|
||||
F16Test
|
||||
LogTest
|
||||
|
||||
# ReportingTest
|
||||
|
||||
noinst_HEADERS = \
|
||||
BitVector.h \
|
||||
PRBS.h \
|
||||
Interthread.h \
|
||||
LinkedLists.h \
|
||||
Sockets.h \
|
||||
Threads.h \
|
||||
Timeval.h \
|
||||
Regexp.h \
|
||||
Vector.h \
|
||||
Configuration.h \
|
||||
Reporting.h \
|
||||
F16.h \
|
||||
URLEncode.h \
|
||||
Utils.h \
|
||||
Logger.h \
|
||||
sqlite3util.h
|
||||
|
||||
URLEncodeTest_SOURCES = URLEncodeTest.cpp
|
||||
URLEncodeTest_LDADD = libcommon.la
|
||||
|
||||
BitVectorTest_SOURCES = BitVectorTest.cpp
|
||||
BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
BitVectorTest_LDADD = libcommon.la $(SQLITE3_LIBS)
|
||||
|
||||
ThreadsTest_SOURCES = ThreadsTest.cpp
|
||||
ThreadsTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
ThreadsTest_LDFLAGS = -lpthread
|
||||
PRBSTest_SOURCES = PRBSTest.cpp
|
||||
|
||||
InterthreadTest_SOURCES = InterthreadTest.cpp
|
||||
InterthreadTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
InterthreadTest_LDADD = libcommon.la
|
||||
InterthreadTest_LDFLAGS = -lpthread
|
||||
|
||||
SocketsTest_SOURCES = SocketsTest.cpp
|
||||
SocketsTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
SocketsTest_LDADD = libcommon.la
|
||||
SocketsTest_LDFLAGS = -lpthread
|
||||
|
||||
TimevalTest_SOURCES = TimevalTest.cpp
|
||||
TimevalTest_LDADD = libcommon.la
|
||||
|
||||
VectorTest_SOURCES = VectorTest.cpp
|
||||
VectorTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
RegexpTest_SOURCES = RegexpTest.cpp
|
||||
RegexpTest_LDADD = libcommon.la
|
||||
VectorTest_LDADD = libcommon.la $(SQLITE3_LIBS)
|
||||
|
||||
ConfigurationTest_SOURCES = ConfigurationTest.cpp
|
||||
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
ConfigurationTest_LDADD = libcommon.la $(SQLITE3_LIBS)
|
||||
|
||||
# ReportingTest_SOURCES = ReportingTest.cpp
|
||||
# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
LogTest_SOURCES = LogTest.cpp
|
||||
LogTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
F16Test_SOURCES = F16Test.cpp
|
||||
LogTest_LDADD = libcommon.la $(SQLITE3_LIBS)
|
||||
|
||||
MOSTLYCLEANFILES += testSource testDestination
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
#ifndef _MEMORYLEAK_
|
||||
#define _MEMORYLEAK_ 1
|
||||
#include <map>
|
||||
#include "ScalarTypes.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace Utils {
|
||||
|
||||
struct MemStats {
|
||||
// Enumerates the classes that are checked.
|
||||
// Redundancies are ok, for example, we check BitVector and also
|
||||
// several descendants of BitVector.
|
||||
enum MemoryNames {
|
||||
mZeroIsUnused,
|
||||
mVector,
|
||||
mVectorData,
|
||||
mBitVector,
|
||||
mByteVector,
|
||||
mByteVectorData,
|
||||
mRLCRawBlock,
|
||||
mRLCUplinkDataBlock,
|
||||
mRLCMessage,
|
||||
mRLCMsgPacketDownlinkDummyControlBlock, // Redundant with RLCMessage
|
||||
mTBF,
|
||||
mLlcEngine,
|
||||
mSgsnDownlinkMsg,
|
||||
mRachInfo,
|
||||
mPdpPdu,
|
||||
mFECDispatchInfo,
|
||||
mL3Frame,
|
||||
msignalVector,
|
||||
mSoftVector,
|
||||
mScramblingCode,
|
||||
mURlcDownSdu,
|
||||
mURlcPdu,
|
||||
// Must be last:
|
||||
mMax,
|
||||
};
|
||||
int mMemTotal[mMax]; // In elements, not bytes.
|
||||
int mMemNow[mMax];
|
||||
const char *mMemName[mMax];
|
||||
MemStats();
|
||||
void memChkNew(MemoryNames memIndex, const char *id);
|
||||
void memChkDel(MemoryNames memIndex, const char *id);
|
||||
void text(std::ostream &os);
|
||||
// We would prefer to use an unordered_map, but that requires special compile switches.
|
||||
// What a super great language.
|
||||
typedef std::map<std::string,Int_z> MemMapType;
|
||||
MemMapType mMemMap;
|
||||
};
|
||||
extern struct MemStats gMemStats;
|
||||
extern int gMemLeakDebug;
|
||||
|
||||
// This is a memory leak detector.
|
||||
// Use by putting RN_MEMCHKNEW and RN_MEMCHKDEL in class constructors/destructors,
|
||||
// or use the DEFINE_MEMORY_LEAK_DETECTOR class and add the defined class
|
||||
// as an ancestor to the class to be memory leak checked.
|
||||
|
||||
struct MemLabel {
|
||||
std::string mccKey;
|
||||
virtual ~MemLabel() {
|
||||
Int_z &tmp = Utils::gMemStats.mMemMap[mccKey]; tmp = tmp - 1;
|
||||
}
|
||||
};
|
||||
|
||||
#if RN_DISABLE_MEMORY_LEAK_TEST
|
||||
#define RN_MEMCHKNEW(type)
|
||||
#define RN_MEMCHKDEL(type)
|
||||
#define RN_MEMLOG(type,ptr)
|
||||
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
|
||||
struct checkerClass {};
|
||||
#else
|
||||
|
||||
#define RN_MEMCHKNEW(type) { Utils::gMemStats.memChkNew(Utils::MemStats::m##type,#type); }
|
||||
#define RN_MEMCHKDEL(type) { Utils::gMemStats.memChkDel(Utils::MemStats::m##type,#type); }
|
||||
|
||||
#define RN_MEMLOG(type,ptr) { \
|
||||
static std::string key = format("%s_%s:%d",#type,__FILE__,__LINE__); \
|
||||
(ptr)->/* MemCheck##type:: */ mccKey = key; \
|
||||
Utils::gMemStats.mMemMap[key]++; \
|
||||
}
|
||||
|
||||
// TODO: The above assumes that checkclass is MemCheck ## subClass
|
||||
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
|
||||
struct checkerClass : public virtual Utils::MemLabel { \
|
||||
checkerClass() { RN_MEMCHKNEW(subClass); } \
|
||||
virtual ~checkerClass() { \
|
||||
RN_MEMCHKDEL(subClass); \
|
||||
} \
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
#endif
|
||||
110
CommonLibs/PRBS.h
Normal file
110
CommonLibs/PRBS.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef PRBS_H
|
||||
#define PRBS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/** Pseudo-random binary sequence (PRBS) generator (a Galois LFSR implementation). */
|
||||
class PRBS {
|
||||
public:
|
||||
|
||||
PRBS(unsigned wLen, uint64_t wCoeff, uint64_t wState = 0x01)
|
||||
: mCoeff(wCoeff), mStartState(wState), mState(wState), mLen(wLen)
|
||||
{ assert(wLen<=64); }
|
||||
|
||||
/**@name Accessors */
|
||||
//@{
|
||||
uint64_t coeff() const { return mCoeff; }
|
||||
uint64_t state() const { return mState; }
|
||||
void state(uint64_t state) { mState = state & mask(); }
|
||||
unsigned size() const { return mLen; }
|
||||
//@}
|
||||
|
||||
/**
|
||||
Calculate one bit of a PRBS
|
||||
*/
|
||||
unsigned generateBit()
|
||||
{
|
||||
const unsigned result = mState & 0x01;
|
||||
processBit(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
Update the generator state by one bit.
|
||||
If you want to synchronize your PRBS to a known state, call this function
|
||||
size() times passing your PRBS to it bit by bit.
|
||||
*/
|
||||
void processBit(unsigned inBit)
|
||||
{
|
||||
mState >>= 1;
|
||||
if (inBit) mState ^= mCoeff;
|
||||
}
|
||||
|
||||
/** Return true when PRBS is wrapping through initial state */
|
||||
bool isFinished() const { return mStartState == mState; }
|
||||
|
||||
protected:
|
||||
|
||||
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
|
||||
uint64_t mStartState; ///< initial shift register state.
|
||||
uint64_t mState; ///< shift register state.
|
||||
unsigned mLen; ///< number of bits used in shift register
|
||||
|
||||
/** Return mask for the state register */
|
||||
uint64_t mask() const { return (mLen==64)?0xFFFFFFFFFFFFFFFFUL:((1<<mLen)-1); }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
A standard 9-bit based pseudorandom binary sequence (PRBS) generator.
|
||||
Polynomial: x^9 + x^5 + 1
|
||||
*/
|
||||
class PRBS9 : public PRBS {
|
||||
public:
|
||||
PRBS9(uint64_t wState = 0x01)
|
||||
: PRBS(9, 0x0110, wState)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
A standard 15-bit based pseudorandom binary sequence (PRBS) generator.
|
||||
Polynomial: x^15 + x^14 + 1
|
||||
*/
|
||||
class PRBS15 : public PRBS {
|
||||
public:
|
||||
PRBS15(uint64_t wState = 0x01)
|
||||
: PRBS(15, 0x6000, wState)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
A standard 64-bit based pseudorandom binary sequence (PRBS) generator.
|
||||
Polynomial: x^64 + x^63 + x^61 + x^60 + 1
|
||||
*/
|
||||
class PRBS64 : public PRBS {
|
||||
public:
|
||||
PRBS64(uint64_t wState = 0x01)
|
||||
: PRBS(64, 0xD800000000000000ULL, wState)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif // PRBS_H
|
||||
42
CommonLibs/PRBSTest.cpp
Normal file
42
CommonLibs/PRBSTest.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "PRBS.h"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <assert.h>
|
||||
|
||||
void testPrbs(PRBS &prbs, uint64_t expectedPeriod)
|
||||
{
|
||||
uint64_t period = 0;
|
||||
do {
|
||||
std::cout << prbs.generateBit();
|
||||
period++;
|
||||
} while (!prbs.isFinished());
|
||||
std::cout << std::endl;
|
||||
std::cout << "Period: " << period << std::endl;
|
||||
assert(period == expectedPeriod);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
PRBS9 prbs9(0x01);
|
||||
testPrbs(prbs9, (1<<9)-1);
|
||||
PRBS15 prbs15(0x01);
|
||||
testPrbs(prbs15, (1<<15)-1);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REGEXPW_H
|
||||
#define REGEXPW_H
|
||||
|
||||
#include <regex.h>
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
class Regexp {
|
||||
|
||||
private:
|
||||
|
||||
regex_t mRegex;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Regexp(const char* regexp, int flags=REG_EXTENDED)
|
||||
{
|
||||
int result = regcomp(&mRegex, regexp, flags);
|
||||
if (result) {
|
||||
char msg[256];
|
||||
regerror(result,&mRegex,msg,255);
|
||||
std::cerr << "Regexp compilation of " << regexp << " failed: " << msg << std::endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
~Regexp()
|
||||
{ regfree(&mRegex); }
|
||||
|
||||
bool match(const char *text, int flags=0) const
|
||||
{ return regexec(&mRegex, text, 0, NULL, flags)==0; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Regexp.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
Regexp email("^[[:graph:]]+@[[:graph:]]+ ");
|
||||
Regexp simple("^dburgess@");
|
||||
|
||||
const char text1[] = "dburgess@jcis.net test message";
|
||||
const char text2[] = "no address text message";
|
||||
|
||||
cout << email.match(text1) << " " << text1 << endl;
|
||||
cout << email.match(text2) << " " << text2 << endl;
|
||||
|
||||
cout << simple.match(text1) << " " << text1 << endl;
|
||||
cout << simple.match(text2) << " " << text2 << endl;
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
/**@file Module for performance-reporting mechanisms. */
|
||||
/*
|
||||
* Copyright 2012 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "Reporting.h"
|
||||
#include "Logger.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char* createReportingTable = {
|
||||
"CREATE TABLE IF NOT EXISTS REPORTING ("
|
||||
"NAME TEXT UNIQUE NOT NULL, "
|
||||
"VALUE INTEGER DEFAULT 0, "
|
||||
"CLEAREDTIME INTEGER NOT NULL, "
|
||||
"UPDATETIME INTEGER DEFAULT 0 "
|
||||
")"
|
||||
};
|
||||
|
||||
|
||||
ReportingTable::ReportingTable(const char* filename)
|
||||
{
|
||||
gLogEarly(LOG_INFO | mFacility, "opening reporting table from path %s", filename);
|
||||
// Connect to the database.
|
||||
int rc = sqlite3_open(filename,&mDB);
|
||||
if (rc) {
|
||||
gLogEarly(LOG_EMERG | mFacility, "cannot open reporting database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
|
||||
sqlite3_close(mDB);
|
||||
mDB = NULL;
|
||||
return;
|
||||
}
|
||||
// Create the table, if needed.
|
||||
if (!sqlite3_command(mDB,createReportingTable)) {
|
||||
gLogEarly(LOG_EMERG | mFacility, "cannot create reporting table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::create(const char* paramName)
|
||||
{
|
||||
char cmd[200];
|
||||
sprintf(cmd,"INSERT OR IGNORE INTO REPORTING (NAME,CLEAREDTIME) VALUES (\"%s\",%ld)", paramName, time(NULL));
|
||||
if (!sqlite3_command(mDB,cmd)) {
|
||||
gLogEarly(LOG_CRIT|mFacility, "cannot create reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ReportingTable::incr(const char* paramName)
|
||||
{
|
||||
char cmd[200];
|
||||
sprintf(cmd,"UPDATE REPORTING SET VALUE=VALUE+1, UPDATETIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
|
||||
if (!sqlite3_command(mDB,cmd)) {
|
||||
gLogEarly(LOG_CRIT|mFacility, "cannot increment reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ReportingTable::max(const char* paramName, unsigned newVal)
|
||||
{
|
||||
char cmd[200];
|
||||
sprintf(cmd,"UPDATE REPORTING SET VALUE=MAX(VALUE,%u), UPDATETIME=%ld WHERE NAME=\"%s\"", newVal, time(NULL), paramName);
|
||||
if (!sqlite3_command(mDB,cmd)) {
|
||||
gLogEarly(LOG_CRIT|mFacility, "cannot maximize reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::clear(const char* paramName)
|
||||
{
|
||||
char cmd[200];
|
||||
sprintf(cmd,"UPDATE REPORTING SET VALUE=0, UPDATETIME=0, CLEAREDTIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
|
||||
if (!sqlite3_command(mDB,cmd)) {
|
||||
gLogEarly(LOG_CRIT|mFacility, "cannot clear reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::create(const char* baseName, unsigned minIndex, unsigned maxIndex)
|
||||
{
|
||||
size_t sz = strlen(baseName);
|
||||
for (unsigned i = minIndex; i<=maxIndex; i++) {
|
||||
char name[sz+10];
|
||||
sprintf(name,"%s.%u",baseName,i);
|
||||
if (!create(name)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReportingTable::incr(const char* baseName, unsigned index)
|
||||
{
|
||||
char name[strlen(baseName)+10];
|
||||
sprintf(name,"%s.%u",baseName,index);
|
||||
return incr(name);
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::max(const char* baseName, unsigned index, unsigned newVal)
|
||||
{
|
||||
char name[strlen(baseName)+10];
|
||||
sprintf(name,"%s.%u",baseName,index);
|
||||
return max(name,newVal);
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::clear(const char* baseName, unsigned index)
|
||||
{
|
||||
char name[strlen(baseName)+10];
|
||||
sprintf(name,"%s.%u",baseName,index);
|
||||
return clear(name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/**@file Module for performance-reporting mechanisms. */
|
||||
/*
|
||||
* Copyright 2012 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef REPORTING_H
|
||||
#define REPORTING_H
|
||||
|
||||
#include <sqlite3util.h>
|
||||
#include <ostream>
|
||||
|
||||
|
||||
/**
|
||||
Collect performance statistics into a database.
|
||||
Parameters are counters or max/min trackers, all integer.
|
||||
*/
|
||||
class ReportingTable {
|
||||
|
||||
private:
|
||||
|
||||
sqlite3* mDB; ///< database connection
|
||||
int mFacility; ///< rsyslogd facility
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Open the database connection;
|
||||
create the table if it does not exist yet.
|
||||
*/
|
||||
ReportingTable(const char* filename);
|
||||
|
||||
/** Create a new parameter. */
|
||||
bool create(const char* paramName);
|
||||
|
||||
/** Create an indexed parameter set. */
|
||||
bool create(const char* baseBame, unsigned minIndex, unsigned maxIndex);
|
||||
|
||||
/** Increment a counter. */
|
||||
bool incr(const char* paramName);
|
||||
|
||||
/** Increment an indexed counter. */
|
||||
bool incr(const char* baseName, unsigned index);
|
||||
|
||||
/** Take a max of a parameter. */
|
||||
bool max(const char* paramName, unsigned newVal);
|
||||
|
||||
/** Take a max of an indexed parameter. */
|
||||
bool max(const char* paramName, unsigned index, unsigned newVal);
|
||||
|
||||
/** Clear a value. */
|
||||
bool clear(const char* paramName);
|
||||
|
||||
/** Clear an indexed value. */
|
||||
bool clear(const char* paramName, unsigned index);
|
||||
|
||||
/** Dump the database to a stream. */
|
||||
void dump(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef SCALARTYPES_H
|
||||
#define SCALARTYPES_H
|
||||
#include <iostream> // For size_t
|
||||
#include <stdint.h>
|
||||
//#include "GSMCommon.h" // Was included for Z100Timer
|
||||
|
||||
// We dont bother to define *= /= etc.; you'll have to convert: a*=b; to: a=a*b;
|
||||
#define _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
|
||||
Classname() : value(Init) {} \
|
||||
Classname(Basetype wvalue) { value = wvalue; } /* Can set from basetype. */ \
|
||||
operator Basetype(void) const { return value; } /* Converts from basetype. */ \
|
||||
Basetype operator=(Basetype wvalue) { return value = wvalue; } \
|
||||
Basetype* operator&() { return &value; }
|
||||
|
||||
#define _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) \
|
||||
Basetype operator++() { return ++value; } \
|
||||
Basetype operator++(int) { return value++; } \
|
||||
Basetype operator--() { return --value; } \
|
||||
Basetype operator--(int) { return value--; } \
|
||||
Basetype operator+=(Basetype wvalue) { return value = value + wvalue; } \
|
||||
Basetype operator-=(Basetype wvalue) { return value = value - wvalue; }
|
||||
|
||||
#define _INITIALIZED_SCALAR_FUNCS(Classname,Basetype,Init) \
|
||||
_INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
|
||||
_INITIALIZED_SCALAR_ARITH_FUNCS(Basetype)
|
||||
|
||||
|
||||
#define _DECLARE_SCALAR_TYPE(Classname_i,Classname_z,Basetype) \
|
||||
template <Basetype Init> \
|
||||
struct Classname_i { \
|
||||
Basetype value; \
|
||||
_INITIALIZED_SCALAR_FUNCS(Classname_i,Basetype,Init) \
|
||||
}; \
|
||||
typedef Classname_i<0> Classname_z;
|
||||
|
||||
|
||||
// Usage:
|
||||
// Where 'classname' is one of the types listed below, then:
|
||||
// classname_z specifies a zero initialized type;
|
||||
// classname_i<value> initializes the type to the specified value.
|
||||
// We also define Float_z.
|
||||
_DECLARE_SCALAR_TYPE(Int_i, Int_z, int)
|
||||
_DECLARE_SCALAR_TYPE(Char_i, Char_z, signed char)
|
||||
_DECLARE_SCALAR_TYPE(Int16_i, Int16_z, int16_t)
|
||||
_DECLARE_SCALAR_TYPE(Int32_i, Int32_z, int32_t)
|
||||
_DECLARE_SCALAR_TYPE(UInt_i, UInt_z, unsigned)
|
||||
_DECLARE_SCALAR_TYPE(UChar_i, UChar_z, unsigned char)
|
||||
_DECLARE_SCALAR_TYPE(UInt16_i, UInt16_z, uint16_t)
|
||||
_DECLARE_SCALAR_TYPE(UInt32_i, UInt32_z, uint32_t)
|
||||
_DECLARE_SCALAR_TYPE(Size_t_i, Size_t_z, size_t)
|
||||
|
||||
// Bool is special because it cannot accept some arithmetic funcs
|
||||
//_DECLARE_SCALAR_TYPE(Bool_i, Bool_z, bool)
|
||||
template <bool Init>
|
||||
struct Bool_i {
|
||||
bool value;
|
||||
_INITIALIZED_SCALAR_BASE_FUNCS(Bool_i,bool,Init)
|
||||
};
|
||||
typedef Bool_i<0> Bool_z;
|
||||
|
||||
// float is special, because C++ does not permit the template initalization:
|
||||
struct Float_z {
|
||||
float value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Float_z,float,0)
|
||||
};
|
||||
struct Double_z {
|
||||
double value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Double_z,double,0)
|
||||
};
|
||||
|
||||
|
||||
class ItemWithValueAndWidth {
|
||||
public:
|
||||
virtual unsigned getValue() const = 0;
|
||||
virtual unsigned getWidth() const = 0;
|
||||
};
|
||||
|
||||
// A Range Networks Field with a specified width.
|
||||
// See RLCMessages.h for examples.
|
||||
template <int Width=32, unsigned Init=0>
|
||||
class Field_i : public ItemWithValueAndWidth
|
||||
{
|
||||
public:
|
||||
unsigned value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Field_i,unsigned,Init)
|
||||
unsigned getWidth() const { return Width; }
|
||||
unsigned getValue() const { return value; }
|
||||
};
|
||||
|
||||
// Synonym for Field_i, but no way to do it.
|
||||
template <int Width, unsigned Init=0>
|
||||
class Field_z : public ItemWithValueAndWidth
|
||||
{
|
||||
public:
|
||||
unsigned value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Field_z,unsigned,Init)
|
||||
unsigned getWidth() const { return Width; }
|
||||
unsigned getValue() const { return value; }
|
||||
};
|
||||
|
||||
// This is an uninitialized field.
|
||||
template <int Width=32, unsigned Init=0>
|
||||
class Field : public ItemWithValueAndWidth
|
||||
{
|
||||
public:
|
||||
unsigned value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Field,unsigned,Init)
|
||||
unsigned getWidth() const { return Width; }
|
||||
unsigned getValue() const { return value; }
|
||||
};
|
||||
|
||||
|
||||
// A Z100Timer with an initial value specified.
|
||||
//template <int Init>
|
||||
//class Z100Timer_i : public GSM::Z100Timer {
|
||||
// public:
|
||||
// Z100Timer_i() : GSM::Z100Timer(Init) {}
|
||||
//};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
@@ -130,11 +129,6 @@ void DatagramSocket::close()
|
||||
::close(mSocketFD);
|
||||
}
|
||||
|
||||
void DatagramSocket::shutdown()
|
||||
{
|
||||
::shutdown(mSocketFD, SHUT_RDWR);
|
||||
}
|
||||
|
||||
|
||||
DatagramSocket::~DatagramSocket()
|
||||
{
|
||||
@@ -193,24 +187,20 @@ int DatagramSocket::send(const struct sockaddr* dest, const char * message)
|
||||
return send(dest,message,length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int DatagramSocket::read(char* buffer)
|
||||
int DatagramSocket::read(char* buffer, size_t length)
|
||||
{
|
||||
socklen_t temp_len = sizeof(mSource);
|
||||
int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0,
|
||||
(struct sockaddr*)&mSource,&temp_len);
|
||||
if ((length==-1) && (errno!=EAGAIN)) {
|
||||
socklen_t addr_len = sizeof(mSource);
|
||||
int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
|
||||
(struct sockaddr*) &mSource, &addr_len);
|
||||
|
||||
if ((rd_length==-1) && (errno!=EAGAIN)) {
|
||||
perror("DatagramSocket::read() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
return length;
|
||||
return rd_length;
|
||||
}
|
||||
|
||||
|
||||
int DatagramSocket::read(char* buffer, unsigned timeout)
|
||||
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
@@ -224,7 +214,7 @@ int DatagramSocket::read(char* buffer, unsigned timeout)
|
||||
throw SocketError();
|
||||
}
|
||||
if (sel==0) return -1;
|
||||
if (FD_ISSET(mSocketFD,&fds)) return read(buffer);
|
||||
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -275,7 +265,7 @@ void UDPSocket::open(unsigned short localPort)
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_port = htons(localPort);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
@@ -109,7 +108,7 @@ public:
|
||||
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
|
||||
@return The number of bytes received or -1 on non-blocking pass.
|
||||
*/
|
||||
int read(char* buffer);
|
||||
int read(char* buffer, size_t length);
|
||||
|
||||
/**
|
||||
Receive a packet with a timeout.
|
||||
@@ -117,7 +116,7 @@ public:
|
||||
@param maximum wait time in milliseconds
|
||||
@return The number of bytes received or -1 on timeout.
|
||||
*/
|
||||
int read(char* buffer, unsigned timeout);
|
||||
int read(char* buffer, size_t length, unsigned timeout);
|
||||
|
||||
|
||||
/** Send a packet to a given destination, other than the default. */
|
||||
@@ -135,11 +134,6 @@ public:
|
||||
/** Close the socket. */
|
||||
void close();
|
||||
|
||||
/** Shutdown the socket without destroying the descriptor
|
||||
* Use this to interrupt blocking read()
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
@@ -29,73 +28,59 @@
|
||||
|
||||
#include "Sockets.h"
|
||||
#include "Threads.h"
|
||||
#include "Configuration.h"
|
||||
#include "Timeval.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
static const int gNumToSend = 10;
|
||||
|
||||
|
||||
class TestReaderIP : public Thread
|
||||
void *testReaderIP(void *)
|
||||
{
|
||||
public:
|
||||
TestReaderIP() : Thread("TestReaderIP") {}
|
||||
|
||||
protected:
|
||||
virtual void runThread()
|
||||
{
|
||||
UDPSocket readSocket(5934, "localhost", 5061);
|
||||
readSocket.nonblocking();
|
||||
int rc = 0;
|
||||
while (rc<gNumToSend) {
|
||||
char buf[MAX_UDP_LENGTH];
|
||||
int count = readSocket.read(buf);
|
||||
if (count>0) {
|
||||
COUT("IP read: " << buf);
|
||||
rc++;
|
||||
} else {
|
||||
COUT("IP sleeping...");
|
||||
sleep(2);
|
||||
}
|
||||
UDPSocket readSocket(5934, "localhost", 5061);
|
||||
readSocket.nonblocking();
|
||||
int rc = 0;
|
||||
while (rc<gNumToSend) {
|
||||
char buf[MAX_UDP_LENGTH];
|
||||
int count = readSocket.read(buf, MAX_UDP_LENGTH);
|
||||
if (count>0) {
|
||||
COUT("read: " << buf);
|
||||
rc++;
|
||||
} else {
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
};
|
||||
return NULL;
|
||||
}
|
||||
|
||||
class TestReaderUnix : public Thread
|
||||
|
||||
|
||||
void *testReaderUnix(void *)
|
||||
{
|
||||
public:
|
||||
TestReaderUnix() : Thread("TestReaderUnix") {}
|
||||
|
||||
protected:
|
||||
virtual void runThread()
|
||||
{
|
||||
UDDSocket readSocket("testDestination");
|
||||
readSocket.nonblocking();
|
||||
int rc = 0;
|
||||
while (rc<gNumToSend) {
|
||||
char buf[MAX_UDP_LENGTH];
|
||||
int count = readSocket.read(buf);
|
||||
if (count>0) {
|
||||
COUT("UNIX read: " << buf);
|
||||
rc++;
|
||||
} else {
|
||||
COUT("UNIX sleeping...");
|
||||
sleep(2);
|
||||
}
|
||||
UDDSocket readSocket("testDestination");
|
||||
readSocket.nonblocking();
|
||||
int rc = 0;
|
||||
while (rc<gNumToSend) {
|
||||
char buf[MAX_UDP_LENGTH];
|
||||
int count = readSocket.read(buf, MAX_UDP_LENGTH);
|
||||
if (count>0) {
|
||||
COUT("read: " << buf);
|
||||
rc++;
|
||||
} else {
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
};
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char * argv[] )
|
||||
{
|
||||
|
||||
TestReaderIP readerThreadIP;
|
||||
TestReaderUnix readerThreadUnix;
|
||||
readerThreadIP.startThread();
|
||||
readerThreadUnix.startThread();
|
||||
Thread readerThreadIP;
|
||||
readerThreadIP.start(testReaderIP,NULL);
|
||||
Thread readerThreadUnix;
|
||||
readerThreadUnix.start(testReaderUnix,NULL);
|
||||
|
||||
UDPSocket socket1(5061, "127.0.0.1",5934);
|
||||
UDDSocket socket1U("testSource","testDestination");
|
||||
@@ -107,10 +92,12 @@ int main(int argc, char * argv[] )
|
||||
|
||||
for (int i=0; i<gNumToSend; i++) {
|
||||
socket1.write("Hello IP land");
|
||||
socket1U.write("Hello Unix domain");
|
||||
msleep(1);
|
||||
socket1U.write("Hello Unix domain");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
readerThreadIP.join();
|
||||
readerThreadUnix.join();
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
@@ -30,26 +29,11 @@
|
||||
|
||||
#include "Threads.h"
|
||||
#include "Timeval.h"
|
||||
#include "Logger.h"
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h> // for ETIMEDOUT
|
||||
#include <sys/syscall.h> // for SYS_gettid
|
||||
#include <sys/prctl.h> // Linux specific, for prctl(PR_SET_NAME)
|
||||
|
||||
// Make sure we get MCL_CURRENT and MCL_FUTURE (for mlockall) on OS X 10.3
|
||||
#define _P1003_1B_VISIBLE
|
||||
#include <sys/mman.h>
|
||||
#undef _P1003_1B_VISIBLE
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define POSIX_OK 0
|
||||
#define POSIX_NO_WAIT 0
|
||||
#define POSIX_WAIT_FOREVER (-1)
|
||||
|
||||
static inline int gettid() {return syscall(SYS_gettid);}
|
||||
|
||||
|
||||
Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
|
||||
@@ -111,235 +95,27 @@ Mutex::~Mutex()
|
||||
|
||||
|
||||
/** Block for the signal up to the cancellation timeout. */
|
||||
int Signal::wait(Mutex& wMutex, unsigned timeout) const
|
||||
void Signal::wait(Mutex& wMutex, unsigned timeout) const
|
||||
{
|
||||
Timeval then(timeout);
|
||||
struct timespec waitTime = then.timespec();
|
||||
return pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
|
||||
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
|
||||
}
|
||||
|
||||
Thread::Thread(const string &name, size_t stackSize)
|
||||
: mThreadId((pthread_t)0)
|
||||
, mThreadName(name)
|
||||
, mStackSize(stackSize)
|
||||
, mThreadState(THREAD_STATE_IDLE)
|
||||
, mThreadData(NULL)
|
||||
|
||||
void Thread::start(void *(*task)(void*), void *arg)
|
||||
{
|
||||
assert(mThread==((pthread_t)0));
|
||||
bool res;
|
||||
// (pat) Moved initialization to constructor to avoid crash in destructor.
|
||||
//res = pthread_attr_init(&mAttrib);
|
||||
//assert(!res);
|
||||
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
|
||||
assert(!res);
|
||||
res = pthread_create(&mThread, &mAttrib, task, arg);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
stopThread();
|
||||
}
|
||||
|
||||
void *Thread::threadAdaptor(void *data)
|
||||
{
|
||||
Thread *pThread = (Thread*)data;
|
||||
|
||||
// If we ever receive a thread cancel request, it means that the Thread
|
||||
// object is in the process of being destroyed. To avoid the situation
|
||||
// where a thread attempts to run after its containing Thread object has
|
||||
// been freed, we set the thread up so that the cancel takes effect
|
||||
// immediately (as opposed to waiting until the next thread cancellation
|
||||
// point).
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
||||
|
||||
// =====================================================================
|
||||
// Synchronize with the start() in the parent thread.
|
||||
{
|
||||
// 1. Lock synchronization mutex.
|
||||
ScopedLock lock(pThread->mThreadStartupMutex);
|
||||
|
||||
// 2. Btw, set the thread name, while we're inside the mutex.
|
||||
// FIXME: This works on Linux with glibc >= 2.12. Under *BSD and MacOS X
|
||||
// this function has different arguments.
|
||||
// pthread_setname_np(pThread->mThreadId, pThread->mThreadName.c_str());
|
||||
// FIXME: For some reason the previous call doesn't work on my Ubuntu 12.04,
|
||||
// so we use this one which works.
|
||||
prctl(PR_SET_NAME, pThread->mThreadName.c_str());
|
||||
|
||||
// 3. Signal that we've started.
|
||||
pThread->mThreadStartStopEvent.signal();
|
||||
|
||||
// 4. Wait until start() finishes its initialization.
|
||||
//
|
||||
// The actual thread is created and started with pthread_create(), then
|
||||
// start() does its housekeeping and sets mThreadState=THREAD_STATE_RUNNING.
|
||||
// If we allow Thread::run() to start before this initialization completes,
|
||||
// callers might think (among other things) that the thread is not started
|
||||
// while it's actually started.
|
||||
pThread->mThreadInitializedEvent.wait(pThread->mThreadStartupMutex);
|
||||
}
|
||||
// Synchronization with the parent thread is finished.
|
||||
// =====================================================================
|
||||
|
||||
// Log Thread ID for debugging purposes
|
||||
LOG(INFO) << "Thread started: " << pThread->mThreadName
|
||||
<< " with lwp=" << gettid() << ", pid=" << getpid();
|
||||
|
||||
// Keep all memory locked into physical mem, to guarantee realtime-behaviour
|
||||
int res = mlockall(MCL_CURRENT|MCL_FUTURE);
|
||||
if (res != POSIX_OK) {
|
||||
LOG(WARNING) << "Failed to lock memory for thread: " << pThread->mThreadName;
|
||||
}
|
||||
|
||||
// Run the actual code
|
||||
pThread->runThread();
|
||||
|
||||
// Huh, we're done. Signal to a (potentially) waiting stop()'s.
|
||||
{
|
||||
ScopedLock lock(pThread->mThreadStateMutex);
|
||||
pThread->mThreadState = THREAD_STATE_IDLE;
|
||||
pThread->mThreadStartStopEvent.broadcast();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Thread::ReturnStatus Thread::startThread(void *data)
|
||||
{
|
||||
pthread_attr_t attrib;
|
||||
// timeval threadStartTime;
|
||||
// timespec threadStartTimeout;
|
||||
bool res;
|
||||
|
||||
// Lock startup synchronization mutex. It will be used in conjunction with
|
||||
// mThreadInitializedEvent and mThreadStartStopEvent conditional variables.
|
||||
ScopedLock lock(mThreadStartupMutex);
|
||||
|
||||
{
|
||||
ScopedLock lock(mThreadStateMutex);
|
||||
if (mThreadState != THREAD_STATE_IDLE)
|
||||
return ALREADY_STARTED;
|
||||
mThreadState = THREAD_STATE_STARTING;
|
||||
}
|
||||
|
||||
// Save thread data pointer
|
||||
mThreadData = data;
|
||||
|
||||
LOG(DEBUG) << "Starting thread " << mThreadName << " (" << this << ")";
|
||||
|
||||
// construct thread attribute
|
||||
res = pthread_attr_init(&attrib);
|
||||
if (res != POSIX_OK) {
|
||||
LOG(ALERT) << "pthread_attr_init failed, returned " << res
|
||||
<< " in " << mThreadName << " (" << this << ")";
|
||||
}
|
||||
|
||||
|
||||
// Set the thread stack size
|
||||
res = pthread_attr_setstacksize(&attrib, mStackSize);
|
||||
if (res != POSIX_OK)
|
||||
{
|
||||
LOG(ALERT) << "pthread_attr_setstacksize failed, returned " << res
|
||||
<< " in " << mThreadName << " (" << this << ")";
|
||||
}
|
||||
|
||||
// Create the thread detached
|
||||
res = pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_DETACHED);
|
||||
if (res != POSIX_OK)
|
||||
{
|
||||
LOG(ALERT) << "pthread_attr_setdetachstate failed, returned " << res
|
||||
<< " in " << mThreadName << " (" << this << ")";
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Start the thread and synchronize with it
|
||||
|
||||
// Start the thread!
|
||||
res = pthread_create(&mThreadId, &attrib, threadAdaptor, (void *)this);
|
||||
// Attributes are no longer needed.
|
||||
pthread_attr_destroy(&attrib);
|
||||
|
||||
if (res != POSIX_OK)
|
||||
{
|
||||
LOG(ALERT) << "pthread_create failed, returned " << res
|
||||
<< " in " << mThreadName << " (" << this << ")";
|
||||
|
||||
return PTHREAD_ERROR;
|
||||
}
|
||||
|
||||
// Wait for the thread to startup.
|
||||
res = mThreadStartStopEvent.wait(mThreadStartupMutex, THREAD_STARTUP_TIMEOUT*1000);
|
||||
|
||||
// If the thread does not start in THREAD_STARTUP_TIMEOUT seconds,
|
||||
// then something is terribly wrong here.
|
||||
if (res == ETIMEDOUT)
|
||||
{
|
||||
LOG(ALERT) << "thread " << mThreadName << " (" << this << ") hasn't started up in "
|
||||
<< THREAD_STARTUP_TIMEOUT << " seconds. Bailing out.";
|
||||
|
||||
return RETURN_TIMEOUT;
|
||||
}
|
||||
|
||||
// We're done with the initialization.
|
||||
ackThreadStart();
|
||||
|
||||
// ToDo: Add other initialization here, e.g. adding this thread to a list of all threads.
|
||||
|
||||
// Startup initialization finished. Signal this to started thread, so
|
||||
// it could go on.
|
||||
mThreadInitializedEvent.signal();
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
Thread::ReturnStatus Thread::stopThread()
|
||||
{
|
||||
int res;
|
||||
|
||||
LOG(DEBUG) << "Stopping thread " << mThreadName << " (" << this << ")";
|
||||
|
||||
while (1) {
|
||||
ScopedLock lock(mThreadStateMutex);
|
||||
|
||||
switch (mThreadState) {
|
||||
case THREAD_STATE_IDLE:
|
||||
// Nothing to do.
|
||||
return RETURN_OK;
|
||||
|
||||
case THREAD_STATE_STARTING:
|
||||
// Something is wrong in thi world.
|
||||
assert(mThreadState != THREAD_STATE_STARTING);
|
||||
LOG(ALERT) << "Trying to stop thread " << mThreadName
|
||||
<< " (" << this << ") while it's trying to start.";
|
||||
return WRONG_STATE;
|
||||
|
||||
case THREAD_STATE_RUNNING:
|
||||
// Request shudown
|
||||
mThreadState = THREAD_STATE_STOPPING;
|
||||
// no "break" here to fall through to the next case
|
||||
|
||||
case THREAD_STATE_STOPPING:
|
||||
// Wait for the thread to stop.
|
||||
LOG(DEBUG) << "Waiting for thread " << mThreadName << " (" << this << ") to stop.";
|
||||
res = mThreadStartStopEvent.wait(mThreadStateMutex, THREAD_STOP_TIMEOUT*1000);
|
||||
LOG(DEBUG) << "Thread " << mThreadName << " (" << this << ") signalled stop "
|
||||
<< "with res=" << res << " and mThreadState=" << mThreadState;
|
||||
|
||||
// If the thread does not stop in THREAD_STOP_TIMEOUT seconds,
|
||||
// return error. It may be waiting for something.
|
||||
if (res == ETIMEDOUT)
|
||||
{
|
||||
LOG(ALERT) << "thread " << mThreadName << " (" << this << ") hasn't stopped in "
|
||||
<< THREAD_STARTUP_TIMEOUT << " seconds. Bailing out.";
|
||||
|
||||
return RETURN_TIMEOUT;
|
||||
}
|
||||
|
||||
// Conditional variable could return in case of a signal, so we should
|
||||
// double check that the thread has indeed stopped.
|
||||
if (mThreadState == THREAD_STATE_IDLE)
|
||||
return RETURN_OK;
|
||||
else
|
||||
// Try again...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We should never reach this line
|
||||
assert(false);
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
@@ -122,14 +121,14 @@ class Signal {
|
||||
Block for the signal up to the cancellation timeout.
|
||||
Under Linux, spurious returns are possible.
|
||||
*/
|
||||
int wait(Mutex& wMutex, unsigned timeout) const;
|
||||
void wait(Mutex& wMutex, unsigned timeout) const;
|
||||
|
||||
/**
|
||||
Block for the signal.
|
||||
Under Linux, spurious returns are possible.
|
||||
*/
|
||||
int wait(Mutex& wMutex) const
|
||||
{ return pthread_cond_wait(&mSignal,&wMutex.mMutex); }
|
||||
void wait(Mutex& wMutex) const
|
||||
{ pthread_cond_wait(&mSignal,&wMutex.mMutex); }
|
||||
|
||||
void signal() { pthread_cond_signal(&mSignal); }
|
||||
|
||||
@@ -138,105 +137,54 @@ class Signal {
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define START_THREAD(thread,function,argument) \
|
||||
thread.start((void *(*)(void*))function, (void*)argument);
|
||||
|
||||
/** A C++ wrapper for pthread threads. */
|
||||
class Thread {
|
||||
|
||||
public:
|
||||
private:
|
||||
|
||||
typedef void *(*Adaptor)(void*);
|
||||
enum ReturnStatus {
|
||||
RETURN_OK = 0,
|
||||
ALREADY_STARTED,
|
||||
ALREADY_IDLE,
|
||||
PTHREAD_ERROR,
|
||||
WRONG_STATE,
|
||||
RETURN_TIMEOUT
|
||||
};
|
||||
enum ThreadState {
|
||||
THREAD_STATE_IDLE, ///< Thread is not started. On start() => STARTING
|
||||
THREAD_STATE_STARTING, ///< Thread is about to start. When actually started => RUNNING
|
||||
THREAD_STATE_RUNNING, ///< Thread is active. On stop() => STOPPING
|
||||
THREAD_STATE_STOPPING ///< Thread is about to stop. When actually stopped => IDLE
|
||||
};
|
||||
enum {
|
||||
THREAD_STARTUP_TIMEOUT=5, ///< Time to wait for thread startup (in seconds).
|
||||
THREAD_STOP_TIMEOUT=5 ///< Time to wait for thread stop (in seconds).
|
||||
};
|
||||
pthread_t mThread;
|
||||
pthread_attr_t mAttrib;
|
||||
// FIXME -- Can this be reduced now?
|
||||
size_t mStackSize;
|
||||
|
||||
|
||||
/** Create a thread in a non-running state. */
|
||||
Thread(const std::string &name, size_t stackSize = (65536*4));
|
||||
public:
|
||||
|
||||
/** Destroy the Thread. */
|
||||
virtual ~Thread();
|
||||
/** Create a thread in a non-running state. */
|
||||
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
|
||||
pthread_attr_init(&mAttrib); // (pat) moved this here.
|
||||
mStackSize=wStackSize;
|
||||
}
|
||||
|
||||
/** Start the thread. */
|
||||
ReturnStatus startThread(void *data=NULL);
|
||||
/**
|
||||
Destroy the Thread.
|
||||
It should be stopped and joined.
|
||||
*/
|
||||
// (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
|
||||
~Thread() { pthread_attr_destroy(&mAttrib); }
|
||||
|
||||
/** Stop the thread. */
|
||||
ReturnStatus stopThread();
|
||||
|
||||
ThreadState getThreadState() const
|
||||
{
|
||||
ScopedLock lock(mThreadStateMutex);
|
||||
return mThreadState;
|
||||
}
|
||||
/** Start the thread on a task. */
|
||||
void start(void *(*task)(void*), void *arg);
|
||||
|
||||
bool isThreadRunning() const
|
||||
{
|
||||
ScopedLock lock(mThreadStateMutex);
|
||||
return mThreadState == THREAD_STATE_RUNNING;
|
||||
}
|
||||
void requestThreadStop()
|
||||
{
|
||||
ScopedLock lock(mThreadStateMutex);
|
||||
if (mThreadState == THREAD_STATE_RUNNING)
|
||||
mThreadState = THREAD_STATE_STOPPING;
|
||||
}
|
||||
bool isThreadStopping() const
|
||||
{
|
||||
ScopedLock lock(mThreadStateMutex);
|
||||
return mThreadState == THREAD_STATE_STOPPING;
|
||||
}
|
||||
/** Join a thread that will stop on its own. */
|
||||
void join() {
|
||||
if (mThread) {
|
||||
int s = pthread_join(mThread, NULL);
|
||||
assert(!s);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &getThreadName() const {return mThreadName;}
|
||||
|
||||
protected:
|
||||
|
||||
pthread_t mThreadId; ///< OS id of the thread.
|
||||
const std::string mThreadName; ///< Name of the thread.
|
||||
size_t mStackSize; ///< Requested stack size for the thread.
|
||||
ThreadState mThreadState; ///< The current state of the thread.
|
||||
mutable Mutex mThreadStateMutex; ///< Mutex to protect ThreadState variable
|
||||
void *mThreadData; ///< Data to be passed to the thread loop.
|
||||
Mutex mThreadStartupMutex; ///< Mutex, used with the next two conditional
|
||||
///< variables to synchronize thread startup.
|
||||
Signal mThreadInitializedEvent; ///< Conditional variable, signaling
|
||||
///< that this thread object initialization is completed
|
||||
///< and the thread could go on.
|
||||
Signal mThreadStartStopEvent; ///< Conditional variable, signaling
|
||||
///< that the thread is started and start() method could
|
||||
///< return to caller.
|
||||
|
||||
/** Function with the actual thread loop.
|
||||
* Override this function in child classes to do real work.
|
||||
*/
|
||||
virtual void runThread() =0;
|
||||
|
||||
// Static funciton which actually starts the run() method.
|
||||
static void *threadAdaptor(void *data);
|
||||
|
||||
void ackThreadStart() {
|
||||
ScopedLock lock(mThreadStateMutex);
|
||||
assert(mThreadState == THREAD_STATE_STARTING);
|
||||
mThreadState = THREAD_STATE_RUNNING;
|
||||
}
|
||||
void ackThreadStop() {
|
||||
ScopedLock lock(mThreadStateMutex);
|
||||
assert(mThreadState == THREAD_STATE_STOPPING);
|
||||
mThreadState = THREAD_STATE_IDLE;
|
||||
}
|
||||
/** Send cancelation to thread */
|
||||
void cancel() { pthread_cancel(mThread); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Threads.h"
|
||||
#include "Timeval.h"
|
||||
#include "Configuration.h"
|
||||
#include <iostream>
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
class SimpleThreadTest : public Thread
|
||||
{
|
||||
public:
|
||||
SimpleThreadTest() : Thread("SimpleThreadTest") {}
|
||||
|
||||
void runThread()
|
||||
{
|
||||
COUT(getThreadName() << ": Started thread");
|
||||
while (isThreadRunning()) {
|
||||
COUT(getThreadName() << ": Sleeping...");
|
||||
msleep(50);
|
||||
}
|
||||
COUT(getThreadName() << ": Stopped thread");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
void testSimpleStartStop()
|
||||
{
|
||||
SimpleThreadTest simpleThreadTest;
|
||||
COUT("Main: Starting thread " << simpleThreadTest.getThreadName());
|
||||
simpleThreadTest.startThread();
|
||||
COUT("Main: Started thread " << simpleThreadTest.getThreadName());
|
||||
msleep(30);
|
||||
COUT("Main: Stopping thread " << simpleThreadTest.getThreadName());
|
||||
simpleThreadTest.stopThread();
|
||||
COUT("Main: Stopped thread " << simpleThreadTest.getThreadName());
|
||||
}
|
||||
|
||||
void testDoubleRequestStop()
|
||||
{
|
||||
SimpleThreadTest simpleThreadTest;
|
||||
COUT("Main: Starting thread " << simpleThreadTest.getThreadName());
|
||||
simpleThreadTest.startThread();
|
||||
COUT("Main: Started thread " << simpleThreadTest.getThreadName());
|
||||
msleep(30);
|
||||
COUT("Main: Requesting stop for thread " << simpleThreadTest.getThreadName());
|
||||
simpleThreadTest.requestThreadStop();
|
||||
msleep(30);
|
||||
COUT("Main: Requesting stop for thread " << simpleThreadTest.getThreadName());
|
||||
simpleThreadTest.requestThreadStop();
|
||||
msleep(30);
|
||||
COUT("Main: Stopping thread " << simpleThreadTest.getThreadName());
|
||||
simpleThreadTest.stopThread();
|
||||
COUT("Main: Stopped thread " << simpleThreadTest.getThreadName());
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
std::cout<< std::endl << "Simple start/stop test" << std::endl << std::endl ;
|
||||
testSimpleStartStop();
|
||||
|
||||
std::cout << std::endl << "Double requestThreadStop() test" << std::endl << std::endl ;
|
||||
testDoubleRequestStop();
|
||||
}
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
@@ -1,28 +0,0 @@
|
||||
/* Copyright 2011, Range Networks, Inc. */
|
||||
|
||||
#include <URLEncode.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
//based on javascript encodeURIComponent()
|
||||
string URLEncode(const string &c)
|
||||
{
|
||||
static const char *digits = "01234567890ABCDEF";
|
||||
string retVal="";
|
||||
for (size_t i=0; i<c.length(); i++)
|
||||
{
|
||||
const char ch = c[i];
|
||||
if (isalnum(ch) || strchr("-_.!~'()",ch)) {
|
||||
retVal += ch;
|
||||
} else {
|
||||
retVal += '%';
|
||||
retVal += digits[(ch>>4) & 0x0f];
|
||||
retVal += digits[ch & 0x0f];
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
# include <string>
|
||||
|
||||
std::string URLEncode(const std::string&);
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
#include "URLEncode.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
string test = string("Testing: !@#$%^&*() " __DATE__ " " __TIME__);
|
||||
cout << test << endl;
|
||||
cout << URLEncode(test) << endl;
|
||||
}
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include <unistd.h> // For usleep
|
||||
#include <sys/time.h> // For gettimeofday
|
||||
#include <stdio.h> // For vsnprintf
|
||||
#include <ostream> // For ostream
|
||||
#include <sstream> // For ostringstream
|
||||
#include <string.h> // For strcpy
|
||||
//#include "GSMCommon.h"
|
||||
#include "Utils.h"
|
||||
#include "MemoryLeak.h"
|
||||
|
||||
namespace Utils {
|
||||
|
||||
MemStats gMemStats;
|
||||
int gMemLeakDebug = 0;
|
||||
|
||||
MemStats::MemStats()
|
||||
{
|
||||
memset(mMemNow,0,sizeof(mMemNow));
|
||||
memset(mMemTotal,0,sizeof(mMemTotal));
|
||||
memset(mMemName,0,sizeof(mMemName));
|
||||
}
|
||||
|
||||
void MemStats::text(std::ostream &os)
|
||||
{
|
||||
os << "Structs current total:\n";
|
||||
for (int i = 0; i < mMax; i++) {
|
||||
os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void MemStats::memChkNew(MemoryNames memIndex, const char *id)
|
||||
{
|
||||
/*std::cout << "new " #type "\n";*/
|
||||
mMemNow[memIndex]++;
|
||||
mMemTotal[memIndex]++;
|
||||
mMemName[memIndex] = id;
|
||||
}
|
||||
|
||||
void MemStats::memChkDel(MemoryNames memIndex, const char *id)
|
||||
{
|
||||
/*std::cout << "del " #type "\n";*/
|
||||
mMemNow[memIndex]--;
|
||||
if (mMemNow[memIndex] < 0) {
|
||||
LOG(ERR) << "Memory underflow on type "<<id;
|
||||
if (gMemLeakDebug) assert(0);
|
||||
mMemNow[memIndex] += 100; // Prevent another message for a while.
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
|
||||
{
|
||||
return os << ss.str();
|
||||
}
|
||||
|
||||
std::ostream &osprintf(std::ostream &os, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[300];
|
||||
va_start(ap,fmt);
|
||||
int n = vsnprintf(buf,300,fmt,ap);
|
||||
va_end(ap);
|
||||
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
|
||||
os << buf;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string format(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[300];
|
||||
va_start(ap,fmt);
|
||||
int n = vsnprintf(buf,300,fmt,ap);
|
||||
va_end(ap);
|
||||
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// Return time in seconds with high resolution.
|
||||
// Note: In the past I found this to be a surprisingly expensive system call in linux.
|
||||
double timef()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
return tv.tv_usec / 1000000.0 + tv.tv_sec;
|
||||
}
|
||||
|
||||
const std::string timestr()
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm tm;
|
||||
gettimeofday(&tv,NULL);
|
||||
localtime_r(&tv.tv_sec,&tm);
|
||||
unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
|
||||
return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
|
||||
}
|
||||
|
||||
// High resolution sleep for the specified time.
|
||||
// Return FALSE if time is already past.
|
||||
void sleepf(double howlong)
|
||||
{
|
||||
if (howlong <= 0.00001) return; // Less than 10 usecs, forget it.
|
||||
usleep((useconds_t) (1000000.0 * howlong));
|
||||
}
|
||||
|
||||
//bool sleepuntil(double untilwhen)
|
||||
//{
|
||||
//double now = timef();
|
||||
//double howlong = untilwhen - now; // Fractional time in seconds.
|
||||
// We are not worrying about overflow because all times should be in the near future.
|
||||
//if (howlong <= 0.00001) return false; // Less than 10 usecs, forget it.
|
||||
//sleepf(sleeptime);
|
||||
//}
|
||||
|
||||
std::string Text2Str::str() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
text(ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Text2Str *val)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (val) {
|
||||
val->text(ss);
|
||||
os << ss.str();
|
||||
} else {
|
||||
os << "(null)";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
// Greatest Common Denominator.
|
||||
// This is by Doug Brown.
|
||||
int gcd(int x, int y)
|
||||
{
|
||||
if (x > y) {
|
||||
return x % y == 0 ? y : gcd(y, x % y);
|
||||
} else {
|
||||
return y % x == 0 ? x : gcd(x, y % x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Split a C string into an argc,argv array in place; the input string is modified.
|
||||
// Returns argc, and places results in argv, up to maxargc elements.
|
||||
// The final argv receives the rest of the input string from maxargc on,
|
||||
// even if it contains additional splitchars.
|
||||
// The correct idiom for use is to make a copy of your string, like this:
|
||||
// char *copy = strcpy((char*)alloca(the_string.length()+1),the_string.c_str());
|
||||
// char *argv[2];
|
||||
// int argc = cstrSplit(copy,argv,2,NULL);
|
||||
// If you want to detect the error of too many arguments, add 1 to argv, like this:
|
||||
// char *argv[3];
|
||||
// int argc = cstrSplit(copy,argv,3,NULL);
|
||||
// if (argc == 3) { error("too many arguments"; }
|
||||
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars)
|
||||
{
|
||||
if (splitchars == NULL) { splitchars = " \t\r\n"; } // Default is any space.
|
||||
int argc = 0;
|
||||
while (argc < maxargc) {
|
||||
while (*in && strchr(splitchars,*in)) {in++;} // scan past any splitchars
|
||||
if (! *in) return argc; // return if finished.
|
||||
pargv[argc++] = in; // save ptr to start of arg.
|
||||
in = strpbrk(in,splitchars); // go to end of arg.
|
||||
if (!in) return argc; // return if finished.
|
||||
*in++ = 0; // zero terminate this arg.
|
||||
}
|
||||
return argc;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat) { stat.text(os); return os; }
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat) { stat.text(os); return os; }
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat) { stat.text(os); return os; }
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat) { stat.text(os); return os; }
|
||||
|
||||
std::string replaceAll(const std::string input, const std::string search, const std::string replace)
|
||||
{
|
||||
std::string output = input;
|
||||
int index = 0;
|
||||
|
||||
while (true) {
|
||||
index = output.find(search, index);
|
||||
if (index == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
output.replace(index, replace.length(), replace);
|
||||
index += replace.length();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef GPRSUTILS_H
|
||||
#define GPRSUTILS_H
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <math.h> // for sqrtf
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
namespace Utils {
|
||||
|
||||
extern double timef(); // high resolution time
|
||||
extern const std::string timestr(); // A timestamp to print in messages.
|
||||
extern void sleepf(double howlong); // high resolution sleep
|
||||
extern int gcd(int x, int y);
|
||||
|
||||
// It is irritating to create a string just to interface to the brain-damaged
|
||||
// C++ stream class, but this is only used for debug messages.
|
||||
std::string format(const char *fmt, ...) __attribute__((format (printf,1,2)));
|
||||
|
||||
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars=NULL);
|
||||
|
||||
// For classes with a text() function, provide a function to return a String,
|
||||
// and also a standard << stream function that takes a pointer to the object.
|
||||
// We dont provide the function that takes a reference to the object
|
||||
// because it is too highly overloaded and generally doesnt work.
|
||||
class Text2Str {
|
||||
public:
|
||||
virtual void text(std::ostream &os) const = 0;
|
||||
std::string str() const;
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const Text2Str *val);
|
||||
|
||||
#if 0
|
||||
// Generic Activity Timer. Lots of controls to make everybody happy.
|
||||
class ATimer {
|
||||
double mStart;
|
||||
//bool mActive;
|
||||
double mLimitTime;
|
||||
public:
|
||||
ATimer() : mStart(0), mLimitTime(0) { }
|
||||
ATimer(double wLimitTime) : mStart(0), mLimitTime(wLimitTime) { }
|
||||
void start() { mStart=timef(); }
|
||||
void stop() { mStart=0; }
|
||||
bool active() { return !!mStart; }
|
||||
double elapsed() { return timef() - mStart; }
|
||||
bool expired() { return elapsed() > mLimitTime; }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
struct BitSet {
|
||||
unsigned mBits;
|
||||
void setBit(unsigned whichbit) { mBits |= 1<<whichbit; }
|
||||
void clearBit(unsigned whichbit) { mBits &= ~(1<<whichbit); }
|
||||
unsigned getBit(unsigned whichbit) const { return mBits & (1<<whichbit); }
|
||||
bool isSet(unsigned whichbit) const { return mBits & (1<<whichbit); }
|
||||
unsigned bits() const { return mBits; }
|
||||
operator int(void) const { return mBits; }
|
||||
BitSet() { mBits = 0; }
|
||||
};
|
||||
|
||||
// Store current, min, max and compute running average and standard deviation.
|
||||
template<class Type> struct Statistic {
|
||||
Type mCurrent, mMin, mMax; // min,max optional initialization so you can print before adding any values.
|
||||
unsigned mCnt;
|
||||
double mSum;
|
||||
//double mSum2; // sum of squares.
|
||||
// (Type) cast needed in case Type is an enum, stupid language.
|
||||
Statistic() : mCurrent((Type)0), mMin((Type)0), mMax((Type)0), mCnt(0), mSum(0) /*,mSum2(0)*/ {}
|
||||
// Set the current value and add a statisical point.
|
||||
void addPoint(Type val) {
|
||||
mCurrent = val;
|
||||
if (mCnt == 0 || val < mMin) {mMin = val;}
|
||||
if (mCnt == 0 || val > mMax) {mMax = val;}
|
||||
mCnt++;
|
||||
mSum += val;
|
||||
//mSum2 += val * val;
|
||||
}
|
||||
Type getCurrent() const { // Return current value.
|
||||
return mCnt ? mCurrent : 0;
|
||||
}
|
||||
double getAvg() const { // Return average.
|
||||
return mCnt==0 ? 0 : mSum/mCnt;
|
||||
};
|
||||
//float getSD() const { // Return standard deviation. Use low precision square root function.
|
||||
// return mCnt==0 ? 0 : sqrtf(mCnt * mSum2 - mSum*mSum) / mCnt;
|
||||
//}
|
||||
|
||||
void text(std::ostream &os) const { // Print everything in parens.
|
||||
os << "("<<mCurrent;
|
||||
if (mMin != mMax) { // Not point in printing all this stuff if min == max.
|
||||
os <<LOGVAR2("min",mMin)<<LOGVAR2("max",mMax)<<LOGVAR2("avg",getAvg());
|
||||
if (mCnt <= 999999) {
|
||||
os <<LOGVAR2("N",mCnt);
|
||||
} else { // Shorten this up:
|
||||
char buf[10], *ep;
|
||||
sprintf(buf,"%.3g",round(mCnt));
|
||||
if ((ep = strchr(buf,'e')) && ep[1] == '+') { strcpy(ep+1,ep+2); }
|
||||
os << LOGVAR2("N",buf);
|
||||
}
|
||||
// os<<LOGVAR2("sd",getSD()) standard deviation not interesting
|
||||
}
|
||||
os << ")";
|
||||
// " min="<<mMin <<" max="<<mMax <<format(" avg=%4g sd=%3g)",getAvg(),getSD());
|
||||
}
|
||||
// Not sure if this works:
|
||||
//std::string statStr() const {
|
||||
// return (std::string)mCurrent + " min=" + (std::string) mMin +" max="+(string)mMax+ format(" avg=%4g sd=%3g",getAvg(),getSD());
|
||||
//}
|
||||
};
|
||||
|
||||
// This I/O mechanism is so dumb:
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat);
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat);
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat);
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat);
|
||||
|
||||
|
||||
// Yes, they botched and left this out:
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
|
||||
|
||||
std::ostream &osprintf(std::ostream &os, const char *fmt, ...) __attribute__((format (printf,2,3)));
|
||||
|
||||
std::string replaceAll(const std::string input, const std::string search, const std::string replace);
|
||||
|
||||
}; // namespace
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
#endif
|
||||
@@ -92,6 +92,13 @@ template <class T> class Vector {
|
||||
mEnd = mStart + newSize;
|
||||
}
|
||||
|
||||
/** Reduce addressable size of the Vector, keeping content. */
|
||||
void shrink(size_t newSize)
|
||||
{
|
||||
assert(newSize <= mEnd - mStart);
|
||||
mEnd = mStart + newSize;
|
||||
}
|
||||
|
||||
/** Release memory and clear pointers. */
|
||||
void clear() { resize(0); }
|
||||
|
||||
@@ -222,6 +229,21 @@ template <class T> class Vector {
|
||||
memcpy(other.mStart,base,span*sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
Move (copy) a segment of this vector into a different position in the vector
|
||||
@param from Start point from which to copy.
|
||||
@param to Start point to which to copy.
|
||||
@param span The number of elements to copy.
|
||||
*/
|
||||
void segmentMove(size_t from, size_t to, size_t span)
|
||||
{
|
||||
const T* baseFrom = mStart + from;
|
||||
T* baseTo = mStart + to;
|
||||
assert(baseFrom+span<=mEnd);
|
||||
assert(baseTo+span<=mEnd);
|
||||
memmove(baseTo,baseFrom,span*sizeof(T));
|
||||
}
|
||||
|
||||
void fill(const T& val)
|
||||
{
|
||||
T* dp=mStart;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include <sqlite3.h>
|
||||
#include "sqlite3util.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -41,10 +41,24 @@ const BitVector GSM::gTrainingSequence[] = {
|
||||
BitVector("11101111000100101110111100"),
|
||||
};
|
||||
|
||||
const BitVector GSM::gEdgeTrainingSequence[] = {
|
||||
BitVector("111111001111111001111001001001111111111111001111111111001111111001111001001001"),
|
||||
BitVector("111111001111001001111001001001111001001001001111111111001111001001111001001001"),
|
||||
BitVector("111001111111111111001001001111001001001111001111111001111111111111001001001111"),
|
||||
BitVector("111001111111111001001001001111001001111001111111111001111111111001001001001111"),
|
||||
BitVector("111111111001001111001111001001001111111001111111111111111001001111001111001001"),
|
||||
BitVector("111001111111001001001111001111001001111111111111111001111111001001001111001111"),
|
||||
BitVector("001111001111111001001001001001111001001111111111001111001111111001001001001001"),
|
||||
BitVector("001001001111001001001001111111111001111111001111001001001111001001001001111111"),
|
||||
};
|
||||
|
||||
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
||||
|
||||
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
|
||||
|
||||
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
||||
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
||||
|
||||
|
||||
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
|
||||
{
|
||||
|
||||
@@ -46,12 +46,15 @@ namespace GSM {
|
||||
|
||||
/** GSM Training sequences from GSM 05.02 5.2.3. */
|
||||
extern const BitVector gTrainingSequence[];
|
||||
extern const BitVector gEdgeTrainingSequence[];
|
||||
|
||||
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
|
||||
extern const BitVector gDummyBurst;
|
||||
|
||||
/** Random access burst synch. sequence */
|
||||
extern const BitVector gRACHSynchSequence;
|
||||
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
||||
extern const BitVector gRACHBurst;
|
||||
|
||||
|
||||
/**@name Modulus operations for frame numbers. */
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
||||
ACLOCAL_AMFLAGS = -I config
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES) $(SQLITE3_CFLAGS)
|
||||
AM_CXXFLAGS = -Wall -pthread -ldl
|
||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
|
||||
# Order must be preserved
|
||||
SUBDIRS = \
|
||||
sqlite3 \
|
||||
CommonLibs \
|
||||
GSM \
|
||||
Transceiver52M
|
||||
|
||||
@@ -18,17 +18,18 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
top_srcdir = $(abs_top_srcdir)
|
||||
top_builddir = $(abs_top_builddir)
|
||||
|
||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
||||
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
|
||||
|
||||
STD_DEFINES_AND_INCLUDES = \
|
||||
$(SVNDEV) \
|
||||
-I$(COMMON_INCLUDEDIR) \
|
||||
-I$(GSM_INCLUDEDIR) \
|
||||
-I$(SQLITE_INCLUDEDIR)
|
||||
-I$(GSM_INCLUDEDIR)
|
||||
|
||||
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
||||
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
||||
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl
|
||||
|
||||
MOSTLYCLEANFILES = *~
|
||||
|
||||
260
README
260
README
@@ -1,168 +1,116 @@
|
||||
Welcome to the OpenBTS source code.
|
||||
This is the interface to the transcevier.
|
||||
|
||||
|
||||
For free support, please subscribe to openbts-discuss@lists.sourceforge.net.
|
||||
See http://sourceforge.net/mailarchive/forum.php?forum_name=openbts-discuss
|
||||
and https://lists.sourceforge.net/lists/listinfo/openbts-discuss for details.
|
||||
|
||||
For additional information, refer to http://openbts.org.
|
||||
|
||||
|
||||
These are the directories:
|
||||
|
||||
AsteriskConfig Asterisk configuration files for use with OpenBTS.
|
||||
CommonLib Common-use libraries, mostly C++ wrappers for basic facilities.
|
||||
Control Control-layer functions for the protocols of GSM 04.08 and SIP.
|
||||
GSM The GSM stack.
|
||||
SIP Components of the SIP state machines ued by the control layer.
|
||||
SMS The SMS stack.
|
||||
SR The subscriber registry.
|
||||
TRXManager The interface between the GSM stack and the radio.
|
||||
Transceiver The software transceiver and specific installation tests.
|
||||
apps OpenBTS application binaries.
|
||||
doc Project documentation.
|
||||
tests Test fixtures for subsets of OpenBTS components.
|
||||
smqueue RFC-3428 store-and-forward server for SMS
|
||||
Each TRX Manager UDP socket interface represents a single ARFCN.
|
||||
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
|
||||
Give a base port B (5700), the master clock interface is at port P=B.
|
||||
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
|
||||
The corresponding core-side interface for every socket is at P+100.
|
||||
For any given build, the number of ARFCN interfaces can be fixed.
|
||||
|
||||
|
||||
|
||||
By default, OpenBTS assumes the following UDP port assignments:
|
||||
Indications on the Master Clock Interface
|
||||
|
||||
5060 -- Asterisk SIP interface
|
||||
5061 -- local SIP softphone
|
||||
5062 -- OpenBTS SIP interface
|
||||
5063 -- smqueue SIP interface
|
||||
5064 -- subscriber registry SIP interface
|
||||
5700-range -- OpenBTS-transceiver interface
|
||||
The master clock interface is output only (from the radio).
|
||||
Messages are "indications".
|
||||
|
||||
These can be controlled in the CONFIG table in /etc/OpenBTS.db.
|
||||
CLOCK gives the current value of the transceiver clock to be used by the core.
|
||||
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
|
||||
IND CLOCK <totalFrames>
|
||||
|
||||
Standrd paths:
|
||||
/OpenBTS -- Binary installation.
|
||||
/etc/OpenBTS -- Configuration databases.
|
||||
/var/run/OpenBTS -- Real-time reporting databases.
|
||||
|
||||
The script apps/setUpFiles.sh will create these directories and install the
|
||||
correct files in them.
|
||||
|
||||
Commands on the Per-ARFCN Control Interface
|
||||
|
||||
The per-ARFCN control interface uses a command-reponse protocol.
|
||||
Commands are NULL-terminated ASCII strings, one per UDP socket.
|
||||
Each command has a corresponding response.
|
||||
Every command is of the form:
|
||||
|
||||
CMD <cmdtype> [params]
|
||||
|
||||
The <cmdtype> is the actual command.
|
||||
Parameters are optional depending on the commands type.
|
||||
Every response is of the form:
|
||||
|
||||
RSP <cmdtype> <status> [result]
|
||||
|
||||
The <status> is 0 for success and a non-zero error code for failure.
|
||||
Successful responses may include results, depending on the command type.
|
||||
|
||||
|
||||
Power Control
|
||||
|
||||
POWEROFF shuts off transmitter power and stops the demodulator.
|
||||
CMD POWEROFF
|
||||
RSP POWEROFF <status>
|
||||
|
||||
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
|
||||
This command fails if the transmitter and receiver are not yet tuned.
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
If the transceiver is already on, it response with success to this command.
|
||||
CMD POWERON
|
||||
RSP POWERON <status>
|
||||
|
||||
SETPOWER sets output power in dB wrt full scale.
|
||||
This command fails if the transmitter and receiver are not running.
|
||||
CMD SETPOWER <dB>
|
||||
RSP SETPOWER <status> <dB>
|
||||
|
||||
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
|
||||
This command fails if the transmitter and receiver are not running.
|
||||
CMD ADJPOWER <dBStep>
|
||||
RSP ADJPOWER <status> <dBLevel>
|
||||
|
||||
|
||||
Tuning Control
|
||||
|
||||
RXTUNE tunes the receiver to a given frequency in kHz.
|
||||
This command fails if the receiver is already running.
|
||||
(To re-tune you stop the radio, re-tune, and restart.)
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
CMD RXTUNE <kHz>
|
||||
RSP RXTUNE <status> <kHz>
|
||||
|
||||
TXTUNE tunes the transmitter to a given frequency in kHz.
|
||||
This command fails if the transmitter is already running.
|
||||
(To re-tune you stop the radio, re-tune, and restart.)
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
CMD TXTUNE <kHz>
|
||||
RSP TXTUNE <status> <kHz>
|
||||
|
||||
|
||||
Timeslot Control
|
||||
|
||||
SETSLOT sets the format of the uplink timeslots in the ARFCN.
|
||||
The <timeslot> indicates the timeslot of interest.
|
||||
The <chantype> indicates the type of channel that occupies the timeslot.
|
||||
A chantype of zero indicates the timeslot is off.
|
||||
CMD SETSLOT <timeslot> <chantype>
|
||||
RSP SETSLOT <status> <timeslot> <chantype>
|
||||
|
||||
|
||||
Messages on the per-ARFCN Data Interface
|
||||
|
||||
Messages on the data interface carry one radio burst per UDP message.
|
||||
|
||||
|
||||
Received Data Burst
|
||||
|
||||
1 byte timeslot index
|
||||
4 bytes GSM frame number, big endian
|
||||
1 byte RSSI in -dBm
|
||||
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
|
||||
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
|
||||
|
||||
|
||||
Transmit Data Burst
|
||||
|
||||
1 byte timeslot index
|
||||
4 bytes GSM frame number, big endian
|
||||
1 byte transmit level wrt ARFCN max, -dB (attenuation)
|
||||
148 bytes output symbol values, 0 & 1
|
||||
|
||||
|
||||
|
||||
|
||||
Release history:
|
||||
|
||||
Release Name SVN Reposiory SVN Rev Comments
|
||||
|
||||
1.0 (none) SF.net ?? completed L1, L2
|
||||
|
||||
1.1 Arnaudville GNU Radio r10019 (trunk)
|
||||
|
||||
1.2 Breaux Bridge GNU Radio r10088 (trunk) GNU Build, very early assignment
|
||||
|
||||
1.3 Carencro KSP r1 (trunk) first post-injunction release
|
||||
|
||||
1.4 Donaldsonville KSP r23 (trunk) fixed Ubuntu build error
|
||||
|
||||
1.5 Eunice KSP r39 (trunk) fixed L2 bugs related to segmentation
|
||||
removed incomplete SMS directory
|
||||
moved "abort" calls into L3 subclasses
|
||||
|
||||
1.6 New Iberia KSP r130 (trunk) import of all 2.2 improvements to non-SMS release
|
||||
|
||||
|
||||
2.0 St. Francisville KSP r54 (smswork) SMS support
|
||||
file-based configuration
|
||||
|
||||
2.1 Grand Coteau KSP r70 (smswork) DTMF support
|
||||
fixed more Linux-related build errors
|
||||
-lpthread
|
||||
TLMessage constructor
|
||||
expanded stack to prevent overflows in Linux
|
||||
moved gSIPInterface to main app
|
||||
fixed iterator bug in Pager
|
||||
|
||||
2.2 Houma KSP r122 (smswork) added LEGAL notice
|
||||
removed Assert classes
|
||||
stop paging on page response
|
||||
fixed Pager-spin bug
|
||||
fixed Transceiver spin bugs
|
||||
fixed 2^32 microsecond rollover bug
|
||||
reduced stack footprints in Transceiver
|
||||
fixed SMS timestamps
|
||||
check LAI before using TMSI in LUR
|
||||
reduced memory requirement by 75%
|
||||
removed PagerTest
|
||||
fixed stale-transaction bug in paging handler
|
||||
fixed USRP clock rollover bug
|
||||
faster call connection
|
||||
new USRPDevice design
|
||||
|
||||
2.3 Jean Lafitte KSP r190? (trunk) check for out-of-date RACH bursts
|
||||
better TRX-GSM clock sync
|
||||
formal logging system
|
||||
command line interface
|
||||
emergency call setup
|
||||
|
||||
2.4 Kinder KSP r208? (trunk) fixed BCCH neighbor list bug
|
||||
support for neighbor lists
|
||||
fixed support for non-local Asterisk servers
|
||||
cleaner configuration management
|
||||
more realtime control of BCCH parameters
|
||||
proper rejection of Hold messages
|
||||
fixed L3 hanging bug in MTDCheckBYE
|
||||
|
||||
2.4.1 Kinder KSP r462 fixed lots of valgrind errors
|
||||
|
||||
2.4.2 Kinder KSP r482 zero-length calling party number bug
|
||||
g++ 4.4 #includes
|
||||
|
||||
2.5 Lacassine KSP r551 imported Joshua Lackey patches
|
||||
SIP fixes from Anne Kwong
|
||||
SIP fixes from testing with SMS server
|
||||
L3 TI handling fixes
|
||||
SMS server support
|
||||
GNU Radio 3.2 compatibility
|
||||
configurable max range and LU-reject cause
|
||||
"page" & "testcall" CLI features
|
||||
|
||||
2.5.1 Lacassine KSP r595 fixed some build bugs for some Linux distros
|
||||
|
||||
2.5.2 Lacassine KSP r630 fixed channel assignment bug for Nokia DCT4+ handsets
|
||||
|
||||
2.5.3 Lacassine KSP r756 merged fix for transceiver startup crash
|
||||
due to use of uninitialized variables (r646)
|
||||
merged fix for fusb bug from trunk (r582)
|
||||
|
||||
2.5.4 Lacassine KSP r812 merged fixes to build under latest Fedora and
|
||||
to build with git GnuRadio (r814)
|
||||
|
||||
2.6 Mamou KSP r886 fixed infamous fusb bug (r582)
|
||||
fixed idle-filling table size bug
|
||||
smoother uplink power control
|
||||
load-limiting downlink power control
|
||||
new "config" features (optional, static)
|
||||
IMEI interrogation
|
||||
fixed MOD "missing FIFO" bug
|
||||
configurable short code features
|
||||
fixed transceiver startup crash (r646)
|
||||
readline support is back
|
||||
fixed timing advance bug (r844)
|
||||
added CLI "chans" command
|
||||
track time-of-use in TMSI table (r844)
|
||||
added CLI "noise" command (r844)
|
||||
added CLI "rxpower" command (r844)
|
||||
added CLI "unconfig" command
|
||||
|
||||
2.7 Natchitoches Range rxxx (never released publicly)
|
||||
converted TMSITable to sqlite3 (r902)
|
||||
sqlite3-based configuration (r???)
|
||||
converted Logger to syslogd (r903)
|
||||
added support for rest octets (r1022)
|
||||
external database for transaction reporting (r1184)
|
||||
external database for channel status reporting (r1203)
|
||||
in-call delivery and submission of text messages (r1231)
|
||||
RFC-2833 DMTF (r1249)
|
||||
|
||||
2.8 Opelousas Range rxxx move databases to /etc and /var
|
||||
RRLP aiding support
|
||||
|
||||
|
||||
|
||||
108
Transceiver52M/Channelizer.cpp
Normal file
108
Transceiver52M/Channelizer.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Polyphase channelizer
|
||||
*
|
||||
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Channelizer.h"
|
||||
|
||||
extern "C" {
|
||||
#include "common/fft.h"
|
||||
#include "common/convolve.h"
|
||||
}
|
||||
|
||||
static void deinterleave(const float *in, size_t ilen,
|
||||
float **out, size_t olen, size_t m)
|
||||
{
|
||||
size_t i, n;
|
||||
|
||||
for (i = 0; i < olen; i++) {
|
||||
for (n = 0; n < m; n++) {
|
||||
out[m - 1 - n][2 * i + 0] = in[2 * (i * m + n) + 0];
|
||||
out[m - 1 - n][2 * i + 1] = in[2 * (i * m + n) + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Channelizer::inputLen() const
|
||||
{
|
||||
return blockLen * m;
|
||||
}
|
||||
|
||||
size_t Channelizer::outputLen() const
|
||||
{
|
||||
return blockLen;
|
||||
}
|
||||
|
||||
float *Channelizer::outputBuffer(size_t chan) const
|
||||
{
|
||||
if (chan >= m)
|
||||
return NULL;
|
||||
|
||||
return hInputs[chan];
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation based on material found in:
|
||||
*
|
||||
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
|
||||
* Prentice Hall, 2006."
|
||||
*/
|
||||
bool Channelizer::rotate(const float *in, size_t len)
|
||||
{
|
||||
size_t hSize = 2 * hLen * sizeof(float);
|
||||
|
||||
if (!checkLen(blockLen, len))
|
||||
return false;
|
||||
|
||||
deinterleave(in, len, hInputs, blockLen, m);
|
||||
|
||||
/*
|
||||
* Convolve through filterbank while applying and saving sample history
|
||||
*/
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
|
||||
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
|
||||
|
||||
convolve_real(hInputs[i], blockLen,
|
||||
subFilters[i], hLen,
|
||||
hOutputs[i], blockLen,
|
||||
0, blockLen, 1, 0);
|
||||
}
|
||||
|
||||
cxvec_fft(fftHandle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Setup channelizer paramaters */
|
||||
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
|
||||
: ChannelizerBase(m, blockLen, hLen)
|
||||
{
|
||||
}
|
||||
|
||||
Channelizer::~Channelizer()
|
||||
{
|
||||
}
|
||||
34
Transceiver52M/Channelizer.h
Normal file
34
Transceiver52M/Channelizer.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef _CHANNELIZER_RX_H_
|
||||
#define _CHANNELIZER_RX_H_
|
||||
|
||||
#include "ChannelizerBase.h"
|
||||
|
||||
class Channelizer : public ChannelizerBase {
|
||||
public:
|
||||
/** Constructor for channelizing filter bank
|
||||
@param m number of physical channels
|
||||
@param blockLen number of samples per output of each iteration
|
||||
@param hLen number of taps in each constituent filter path
|
||||
*/
|
||||
Channelizer(size_t m, size_t blockLen, size_t hLen = 16);
|
||||
~Channelizer();
|
||||
|
||||
/* Return required input and output buffer lengths */
|
||||
size_t inputLen() const;
|
||||
size_t outputLen() const;
|
||||
|
||||
/** Rotate "input commutator" and drive samples through filterbank
|
||||
@param in complex input vector
|
||||
@param iLen number of samples in buffer (must match block length)
|
||||
@return false on error and true otherwise
|
||||
*/
|
||||
bool rotate(const float *in, size_t iLen);
|
||||
|
||||
/** Get buffer for an output path
|
||||
@param chan channel number of filterbank
|
||||
@return NULL on error and pointer to buffer otherwise
|
||||
*/
|
||||
float *outputBuffer(size_t chan) const;
|
||||
};
|
||||
|
||||
#endif /* _CHANNELIZER_RX_H_ */
|
||||
251
Transceiver52M/ChannelizerBase.cpp
Normal file
251
Transceiver52M/ChannelizerBase.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Polyphase channelizer
|
||||
*
|
||||
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ChannelizerBase.h"
|
||||
|
||||
extern "C" {
|
||||
#include "common/fft.h"
|
||||
}
|
||||
|
||||
static float sinc(float x)
|
||||
{
|
||||
if (x == 0.0f)
|
||||
return 0.999999999999f;
|
||||
|
||||
return sin(M_PI * x) / (M_PI * x);
|
||||
}
|
||||
|
||||
/*
|
||||
* There are more efficient reversal algorithms, but we only reverse at
|
||||
* initialization so we don't care.
|
||||
*/
|
||||
static void reverse(float *buf, size_t len)
|
||||
{
|
||||
float tmp[2 * len];
|
||||
memcpy(tmp, buf, 2 * len * sizeof(float));
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
buf[2 * i + 0] = tmp[2 * (len - 1 - i) + 0];
|
||||
buf[2 * i + 1] = tmp[2 * (len - 1 - i) + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create polyphase filterbank
|
||||
*
|
||||
* Implementation based material found in,
|
||||
*
|
||||
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
|
||||
* Prentice Hall, 2006."
|
||||
*/
|
||||
bool ChannelizerBase::initFilters()
|
||||
{
|
||||
size_t protoLen = m * hLen;
|
||||
float *proto;
|
||||
float sum = 0.0f, scale = 0.0f;
|
||||
float midpt = (float) (protoLen - 1.0) / 2.0;
|
||||
|
||||
/*
|
||||
* Allocate 'M' partition filters and the temporary prototype
|
||||
* filter. Coefficients are real only and must be 16-byte memory
|
||||
* aligned for SSE usage.
|
||||
*/
|
||||
proto = new float[protoLen];
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
subFilters = (float **) malloc(sizeof(float *) * m);
|
||||
if (!subFilters) {
|
||||
delete[] proto;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
subFilters[i] = (float *)
|
||||
memalign(16, hLen * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the prototype filter with a Blackman-harris window.
|
||||
* Scale coefficients with DC filter gain set to unity divided
|
||||
* by the number of channels.
|
||||
*/
|
||||
float a0 = 0.35875;
|
||||
float a1 = 0.48829;
|
||||
float a2 = 0.14128;
|
||||
float a3 = 0.01168;
|
||||
|
||||
for (size_t i = 0; i < protoLen; i++) {
|
||||
proto[i] = sinc(((float) i - midpt) / (float) m);
|
||||
proto[i] *= a0 -
|
||||
a1 * cos(2 * M_PI * i / (protoLen - 1)) +
|
||||
a2 * cos(4 * M_PI * i / (protoLen - 1)) -
|
||||
a3 * cos(6 * M_PI * i / (protoLen - 1));
|
||||
sum += proto[i];
|
||||
}
|
||||
scale = (float) m / sum;
|
||||
|
||||
/*
|
||||
* Populate partition filters and reverse the coefficients per
|
||||
* convolution requirements.
|
||||
*/
|
||||
for (size_t i = 0; i < hLen; i++) {
|
||||
for (size_t n = 0; n < m; n++) {
|
||||
subFilters[n][2 * i + 0] = proto[i * m + n] * scale;
|
||||
subFilters[n][2 * i + 1] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m; i++)
|
||||
reverse(subFilters[i], hLen);
|
||||
|
||||
delete[] proto;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChannelizerBase::initFFT()
|
||||
{
|
||||
size_t size;
|
||||
|
||||
if (fftInput || fftOutput || fftHandle)
|
||||
return false;
|
||||
|
||||
size = blockLen * m * 2 * sizeof(float);
|
||||
fftInput = (float *) fft_malloc(size);
|
||||
memset(fftInput, 0, size);
|
||||
|
||||
size = (blockLen + hLen) * m * 2 * sizeof(float);
|
||||
fftOutput = (float *) fft_malloc(size);
|
||||
memset(fftOutput, 0, size);
|
||||
|
||||
if (!fftInput | !fftOutput) {
|
||||
LOG(ALERT) << "Memory allocation error";
|
||||
return false;
|
||||
}
|
||||
|
||||
fftHandle = init_fft(0, m, blockLen, blockLen + hLen,
|
||||
fftInput, fftOutput, hLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChannelizerBase::mapBuffers()
|
||||
{
|
||||
if (!fftHandle) {
|
||||
LOG(ALERT) << "FFT buffers not initialized";
|
||||
return false;
|
||||
}
|
||||
|
||||
hInputs = (float **) malloc(sizeof(float *) * m);
|
||||
hOutputs = (float **) malloc(sizeof(float *) * m);
|
||||
if (!hInputs | !hOutputs)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
hInputs[i] = &fftOutput[2 * (i * (blockLen + hLen) + hLen)];
|
||||
hOutputs[i] = &fftInput[2 * (i * blockLen)];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup filterbank internals
|
||||
*/
|
||||
bool ChannelizerBase::init()
|
||||
{
|
||||
/*
|
||||
* Filterbank coefficients, fft plan, history, and output sample
|
||||
* rate conversion blocks
|
||||
*/
|
||||
if (!initFilters()) {
|
||||
LOG(ALERT) << "Failed to initialize channelizing filter";
|
||||
return false;
|
||||
}
|
||||
|
||||
hist = (float **) malloc(sizeof(float *) * m);
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
hist[i] = new float[2 * hLen];
|
||||
memset(hist[i], 0, 2 * hLen * sizeof(float));
|
||||
}
|
||||
|
||||
if (!initFFT()) {
|
||||
LOG(ALERT) << "Failed to initialize FFT";
|
||||
return false;
|
||||
}
|
||||
|
||||
mapBuffers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check vector length validity */
|
||||
bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
|
||||
{
|
||||
if (outerLen != innerLen * m) {
|
||||
LOG(ALERT) << "Invalid outer length " << innerLen
|
||||
<< " is not multiple of " << blockLen;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (innerLen != blockLen) {
|
||||
LOG(ALERT) << "Invalid inner length " << outerLen
|
||||
<< " does not equal " << blockLen;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup channelizer paramaters
|
||||
*/
|
||||
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
|
||||
: fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
|
||||
{
|
||||
this->m = m;
|
||||
this->hLen = hLen;
|
||||
this->blockLen = blockLen;
|
||||
}
|
||||
|
||||
ChannelizerBase::~ChannelizerBase()
|
||||
{
|
||||
free_fft(fftHandle);
|
||||
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
free(subFilters[i]);
|
||||
delete hist[i];
|
||||
}
|
||||
|
||||
fft_free(fftInput);
|
||||
fft_free(fftOutput);
|
||||
|
||||
free(hInputs);
|
||||
free(hOutputs);
|
||||
free(hist);
|
||||
}
|
||||
39
Transceiver52M/ChannelizerBase.h
Normal file
39
Transceiver52M/ChannelizerBase.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef _CHANNELIZER_BASE_H_
|
||||
#define _CHANNELIZER_BASE_H_
|
||||
|
||||
class ChannelizerBase {
|
||||
protected:
|
||||
ChannelizerBase(size_t m, size_t blockLen, size_t hLen);
|
||||
~ChannelizerBase();
|
||||
|
||||
/* Channelizer parameters */
|
||||
size_t m;
|
||||
size_t hLen;
|
||||
size_t blockLen;
|
||||
|
||||
/* Channelizer filterbank sub-filters */
|
||||
float **subFilters;
|
||||
|
||||
/* Input/Output buffers */
|
||||
float **hInputs, **hOutputs, **hist;
|
||||
float *fftInput, *fftOutput;
|
||||
|
||||
/* Pointer to opaque FFT instance */
|
||||
struct fft_hdl *fftHandle;
|
||||
|
||||
/* Initializer internals */
|
||||
bool initFilters();
|
||||
bool initFFT();
|
||||
void releaseFilters();
|
||||
|
||||
/* Map overlapped FFT and filter I/O buffers */
|
||||
bool mapBuffers();
|
||||
|
||||
/* Buffer length validity checking */
|
||||
bool checkLen(size_t innerLen, size_t outerLen);
|
||||
public:
|
||||
/* Initilize channelizer/synthesis filter internals */
|
||||
bool init();
|
||||
};
|
||||
|
||||
#endif /* _CHANNELIZER_BASE_H_ */
|
||||
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "DriveLoop.h"
|
||||
#include <Logger.h>
|
||||
|
||||
using namespace GSM;
|
||||
|
||||
DriveLoop::DriveLoop(int wBasePort, const char *TRXAddress,
|
||||
RadioInterface *wRadioInterface,
|
||||
int wChanM, int wC0, int wSamplesPerSymbol,
|
||||
GSM::Time wTransmitLatency)
|
||||
: Thread("DriveLoop")
|
||||
, mClockSocket(wBasePort, TRXAddress, wBasePort + 100)
|
||||
, mC0(wC0)
|
||||
{
|
||||
mChanM = wChanM;
|
||||
mSamplesPerSymbol = wSamplesPerSymbol;
|
||||
mRadioInterface = wRadioInterface;
|
||||
|
||||
mStartTime = (random() % gHyperframe, 0);
|
||||
|
||||
mTransmitDeadlineClock = mStartTime;
|
||||
mLatencyUpdateTime = mStartTime;
|
||||
mTransmitLatency = wTransmitLatency;
|
||||
mLastClockUpdateTime = mStartTime;
|
||||
|
||||
mRadioInterface->getClock()->set(mStartTime);
|
||||
|
||||
// generate pulse and setup up signal processing library
|
||||
gsmPulse = generateGSMPulse(2, mSamplesPerSymbol);
|
||||
LOG(DEBUG) << "gsmPulse: " << *gsmPulse;
|
||||
sigProcLibSetup(mSamplesPerSymbol);
|
||||
|
||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||
|
||||
// initialize filler tables with dummy bursts on C0, empty bursts otherwise
|
||||
for (int i = 0; i < 8; i++) {
|
||||
signalVector* modBurst = modulateBurst(gDummyBurst, *gsmPulse,
|
||||
8 + (i % 4 == 0), mSamplesPerSymbol);
|
||||
scaleVector(*modBurst, txFullScale);
|
||||
for (int j = 0; j < 102; j++) {
|
||||
for (int n = 0; n < mChanM; n++) {
|
||||
if (n == mC0)
|
||||
fillerTable[n][j][i] = new signalVector(*modBurst);
|
||||
else
|
||||
fillerTable[n][j][i] = new signalVector(modBurst->size());
|
||||
}
|
||||
}
|
||||
delete modBurst;
|
||||
|
||||
for (int n = 0; n < mChanM; n++) {
|
||||
fillerModulus[n][i] = 26;
|
||||
mChanType[n][i] = NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DriveLoop::~DriveLoop()
|
||||
{
|
||||
stopThread();
|
||||
delete gsmPulse;
|
||||
sigProcLibDestroy();
|
||||
}
|
||||
|
||||
void DriveLoop::pushRadioVector(GSM::Time &nowTime)
|
||||
{
|
||||
int i;
|
||||
radioVector *staleBurst;
|
||||
radioVector *next;
|
||||
|
||||
for (i = 0; i < mChanM; i++) {
|
||||
// dump stale bursts, if any
|
||||
while (staleBurst = mTransmitPriorityQueue[i].getStaleBurst(nowTime)) {
|
||||
// Even if the burst is stale, put it in the fillter table.
|
||||
// (It might be an idle pattern.)
|
||||
LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
|
||||
}
|
||||
|
||||
int TN = nowTime.TN();
|
||||
int modFN = nowTime.FN() % fillerModulus[i][nowTime.TN()];
|
||||
|
||||
mTxBursts[i] = fillerTable[i][modFN][TN];
|
||||
mIsFiller[i] = true;
|
||||
mIsZero[i] = (mChanType[i][TN] == NONE);
|
||||
|
||||
// if queue contains data at the desired timestamp, stick it into FIFO
|
||||
if (next = (radioVector*) mTransmitPriorityQueue[i].getCurrentBurst(nowTime)) {
|
||||
LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime;
|
||||
mTxBursts[i] = next;
|
||||
mIsFiller[i] = false;
|
||||
mIsZero[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
mRadioInterface->driveTransmitRadio(mTxBursts, mIsZero);
|
||||
|
||||
for (i = 0; i < mChanM; i++) {
|
||||
if (!mIsFiller[i])
|
||||
delete mTxBursts[i];
|
||||
}
|
||||
}
|
||||
|
||||
void DriveLoop::setModulus(int channel, int timeslot)
|
||||
{
|
||||
switch (mChanType[channel][timeslot]) {
|
||||
case NONE:
|
||||
case I:
|
||||
case II:
|
||||
case III:
|
||||
case FILL:
|
||||
fillerModulus[channel][timeslot] = 26;
|
||||
break;
|
||||
case IV:
|
||||
case VI:
|
||||
case V:
|
||||
fillerModulus[channel][timeslot] = 51;
|
||||
break;
|
||||
//case V:
|
||||
case VII:
|
||||
fillerModulus[channel][timeslot] = 102;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DriveLoop::CorrType DriveLoop::expectedCorrType(int channel, GSM::Time currTime)
|
||||
{
|
||||
unsigned burstTN = currTime.TN();
|
||||
unsigned burstFN = currTime.FN();
|
||||
|
||||
switch (mChanType[channel][burstTN]) {
|
||||
case NONE:
|
||||
return OFF;
|
||||
break;
|
||||
case FILL:
|
||||
return IDLE;
|
||||
break;
|
||||
case I:
|
||||
return TSC;
|
||||
/*if (burstFN % 26 == 25)
|
||||
return IDLE;
|
||||
else
|
||||
return TSC;*/
|
||||
break;
|
||||
case II:
|
||||
if (burstFN % 2 == 1)
|
||||
return IDLE;
|
||||
else
|
||||
return TSC;
|
||||
break;
|
||||
case III:
|
||||
return TSC;
|
||||
break;
|
||||
case IV:
|
||||
case VI:
|
||||
return RACH;
|
||||
break;
|
||||
case V: {
|
||||
int mod51 = burstFN % 51;
|
||||
if ((mod51 <= 36) && (mod51 >= 14))
|
||||
return RACH;
|
||||
else if ((mod51 == 4) || (mod51 == 5))
|
||||
return RACH;
|
||||
else if ((mod51 == 45) || (mod51 == 46))
|
||||
return RACH;
|
||||
else
|
||||
return TSC;
|
||||
break;
|
||||
}
|
||||
case VII:
|
||||
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
|
||||
return IDLE;
|
||||
else
|
||||
return TSC;
|
||||
break;
|
||||
case LOOPBACK:
|
||||
if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
|
||||
return IDLE;
|
||||
else
|
||||
return TSC;
|
||||
break;
|
||||
default:
|
||||
return OFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DriveLoop::driveReceiveFIFO()
|
||||
{
|
||||
SoftVector *rxBurst = NULL;
|
||||
int RSSI;
|
||||
int TOA; // in 1/256 of a symbol
|
||||
GSM::Time burstTime;
|
||||
|
||||
mRadioInterface->driveReceiveRadio();
|
||||
}
|
||||
|
||||
/*
|
||||
* Features a carefully controlled latency mechanism, to
|
||||
* assure that transmit packets arrive at the radio/USRP
|
||||
* before they need to be transmitted.
|
||||
*
|
||||
* Deadline clock indicates the burst that needs to be
|
||||
* pushed into the FIFO right NOW. If transmit queue does
|
||||
* not have a burst, stick in filler data.
|
||||
*/
|
||||
void DriveLoop::driveTransmitFIFO()
|
||||
{
|
||||
int i;
|
||||
|
||||
RadioClock *radioClock = (mRadioInterface->getClock());
|
||||
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
|
||||
pushRadioVector(mTransmitDeadlineClock);
|
||||
mTransmitDeadlineClock.incTN();
|
||||
}
|
||||
|
||||
// FIXME -- This should not be a hard spin.
|
||||
// But any delay here causes us to throw omni_thread_fatal.
|
||||
//else radioClock->wait();
|
||||
}
|
||||
|
||||
void DriveLoop::writeClockInterface()
|
||||
{
|
||||
char command[50];
|
||||
// FIXME -- This should be adaptive.
|
||||
sprintf(command,"IND CLOCK %llu",
|
||||
(unsigned long long) (mTransmitDeadlineClock.FN() + 2));
|
||||
|
||||
LOG(INFO) << "ClockInterface: sending " << command;
|
||||
|
||||
mClockSocket.write(command,strlen(command)+1);
|
||||
|
||||
mLastClockUpdateTime = mTransmitDeadlineClock;
|
||||
}
|
||||
|
||||
void DriveLoop::runThread()
|
||||
{
|
||||
setPriority();
|
||||
|
||||
while (isThreadRunning()) {
|
||||
driveReceiveFIFO();
|
||||
driveTransmitFIFO();
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008, 2012 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Compilation switches
|
||||
TRANSMIT_LOGGING write every burst on the given slot to a log
|
||||
*/
|
||||
|
||||
#ifndef _DRIVELOOP_H_
|
||||
#define _DRIVELOOP_H_
|
||||
|
||||
#include "radioInterface.h"
|
||||
#include "Interthread.h"
|
||||
#include "GSMCommon.h"
|
||||
#include "Sockets.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/** Define this to be the slot number to be logged. */
|
||||
//#define TRANSMIT_LOGGING 1
|
||||
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
class DriveLoop : public Thread {
|
||||
|
||||
private:
|
||||
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||
|
||||
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
||||
|
||||
VectorQueue mTransmitPriorityQueue[CHAN_MAX]; ///< priority queue of transmit bursts received from GSM core
|
||||
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mStartTime; ///< random start time of the radio clock
|
||||
|
||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||
double txFullScale; ///< full scale input to radio
|
||||
double rxFullScale; ///< full scale output to radio
|
||||
|
||||
/** Number of channels supported by the channelizer */
|
||||
int mChanM;
|
||||
|
||||
/** unmodulate a modulated burst */
|
||||
#ifdef TRANSMIT_LOGGING
|
||||
void unModulateVector(signalVector wVector);
|
||||
#endif
|
||||
|
||||
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
|
||||
void pushRadioVector(GSM::Time &nowTime);
|
||||
|
||||
/** Pull and demodulate a burst from the receive FIFO */
|
||||
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
|
||||
|
||||
signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
|
||||
|
||||
int mSamplesPerSymbol; ///< number of samples per GSM symbol
|
||||
|
||||
int fillerModulus[CHAN_MAX][8]; ///< modulus values of all timeslots, in frames
|
||||
signalVector *fillerTable[CHAN_MAX][102][8]; ///< table of modulated filler waveforms for all timeslots
|
||||
|
||||
/** Channelizer path for primary ARFCN */
|
||||
int mC0;
|
||||
|
||||
signalVector *mTxBursts[CHAN_MAX];
|
||||
bool mIsFiller[CHAN_MAX];
|
||||
bool mIsZero[CHAN_MAX];
|
||||
|
||||
public:
|
||||
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX manager, as a string
|
||||
@param wSamplesPerSymbol number of samples per GSM symbol
|
||||
@param wTransmitLatency initial setting of transmit latency
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
DriveLoop(int wBasePort, const char *TRXAddress,
|
||||
RadioInterface *wRadioInterface,
|
||||
int wChanM = 1, int wC0 = 0,
|
||||
int wSamplesPerSymbol = SAMPSPERSYM,
|
||||
GSM::Time wTransmitLatency = GSM::Time(3, 0));
|
||||
|
||||
/** Destructor */
|
||||
~DriveLoop();
|
||||
|
||||
VectorQueue *priorityQueue(int m) { return &mTransmitPriorityQueue[m]; }
|
||||
|
||||
/** Codes for burst types of received bursts*/
|
||||
typedef enum {
|
||||
OFF, ///< timeslot is off
|
||||
TSC, ///< timeslot should contain a normal burst
|
||||
RACH, ///< timeslot should contain an access burst
|
||||
IDLE ///< timeslot is an idle (or dummy) burst
|
||||
} CorrType;
|
||||
|
||||
/** Codes for channel combinations */
|
||||
typedef enum {
|
||||
FILL, ///< Channel is transmitted, but unused
|
||||
I, ///< TCH/FS
|
||||
II, ///< TCH/HS, idle every other slot
|
||||
III, ///< TCH/HS
|
||||
IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
|
||||
V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
|
||||
VI, ///< CCCH+BCCH, uplink RACH
|
||||
VII, ///< SDCCH/8 + SACCH/8
|
||||
NONE, ///< Channel is inactive, default
|
||||
LOOPBACK ///< similar go VII, used in loopback testing
|
||||
} ChannelCombination;
|
||||
|
||||
/** Set modulus for specific timeslot */
|
||||
void setModulus(int channel, int timeslot);
|
||||
|
||||
/** return the expected burst type for the specified timestamp */
|
||||
CorrType expectedCorrType(int channel, GSM::Time currTime);
|
||||
|
||||
void setTimeslot(int m, int timeslot, ChannelCombination comb)
|
||||
{
|
||||
mChanType[m][timeslot] = comb;
|
||||
}
|
||||
|
||||
GSM::Time getStartTime() { return mStartTime; }
|
||||
GSM::Time getLastClockUpdate() { return mLastClockUpdateTime; }
|
||||
GSM::Time getDeadlineClock() { return mTransmitDeadlineClock; }
|
||||
|
||||
/** send messages over the clock socket */
|
||||
void writeClockInterface(void);
|
||||
|
||||
private:
|
||||
|
||||
ChannelCombination mChanType[CHAN_MAX][8]; ///< channel types for all timeslots
|
||||
|
||||
protected:
|
||||
|
||||
/** drive reception and demodulation of GSM bursts */
|
||||
void driveReceiveFIFO();
|
||||
|
||||
/** drive transmission of GSM bursts */
|
||||
void driveTransmitFIFO();
|
||||
|
||||
/** drive handling of control messages from GSM core */
|
||||
void driveControl();
|
||||
|
||||
/**
|
||||
drive modulation and sorting of GSM bursts from GSM core
|
||||
@return true if a burst was transferred successfully
|
||||
*/
|
||||
bool driveTransmitPriorityQueue();
|
||||
|
||||
virtual void runThread();
|
||||
|
||||
void reset();
|
||||
|
||||
/** set priority on current thread */
|
||||
void setPriority() { mRadioInterface->setPriority(); }
|
||||
|
||||
};
|
||||
|
||||
/** FIFO thread loop */
|
||||
void *RadioDriveLoopAdapter(DriveLoop *);
|
||||
|
||||
#endif /* _DRIVELOOP_H_ */
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Compilation Flags
|
||||
|
||||
SWLOOPBACK compile for software loopback testing
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "Threads.h"
|
||||
#include "DummyLoad.h"
|
||||
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
int DummyLoad::loadBurst(short *wDummyBurst, int len) {
|
||||
dummyBurst = wDummyBurst;
|
||||
dummyBurstSz = len;
|
||||
}
|
||||
|
||||
|
||||
DummyLoad::DummyLoad (double _desiredSampleRate)
|
||||
{
|
||||
LOG(INFO) << "creating USRP device...";
|
||||
sampleRate = _desiredSampleRate;
|
||||
}
|
||||
|
||||
void DummyLoad::updateTime(void) {
|
||||
gettimeofday(&currTime,NULL);
|
||||
double timeElapsed = (currTime.tv_sec - startTime.tv_sec)*1.0e6 +
|
||||
(currTime.tv_usec - startTime.tv_usec);
|
||||
currstamp = (TIMESTAMP) floor(timeElapsed/(1.0e6/sampleRate));
|
||||
}
|
||||
|
||||
bool DummyLoad::make(bool wSkipRx)
|
||||
{
|
||||
|
||||
samplesRead = 0;
|
||||
samplesWritten = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyLoad::start()
|
||||
{
|
||||
LOG(INFO) << "starting USRP...";
|
||||
underrun = false;
|
||||
gettimeofday(&startTime,NULL);
|
||||
dummyBurstCursor = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyLoad::stop()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Assumes sequential reads
|
||||
int DummyLoad::readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp,
|
||||
bool *wUnderrun,
|
||||
unsigned *RSSI)
|
||||
{
|
||||
updateTime();
|
||||
underrunLock.lock();
|
||||
*wUnderrun = underrun;
|
||||
underrunLock.unlock();
|
||||
if (currstamp+len < timestamp) {
|
||||
usleep(100);
|
||||
return 0;
|
||||
}
|
||||
else if (currstamp < timestamp) {
|
||||
usleep(100);
|
||||
return 0;
|
||||
}
|
||||
else if (timestamp+len < currstamp) {
|
||||
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*(dummyBurstSz-dummyBurstCursor));
|
||||
int retVal = dummyBurstSz-dummyBurstCursor;
|
||||
dummyBurstCursor = 0;
|
||||
return retVal;
|
||||
}
|
||||
else if (timestamp + len > currstamp) {
|
||||
int amount = timestamp + len - currstamp;
|
||||
if (amount < dummyBurstSz-dummyBurstCursor) {
|
||||
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*amount);
|
||||
dummyBurstCursor += amount;
|
||||
return amount;
|
||||
}
|
||||
else {
|
||||
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*(dummyBurstSz-dummyBurstCursor));
|
||||
int retVal = dummyBurstSz-dummyBurstCursor;
|
||||
dummyBurstCursor = 0;
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DummyLoad::writeSamples(short *buf, int len, bool *wUnderrun,
|
||||
unsigned long long timestamp,
|
||||
bool isControl)
|
||||
{
|
||||
updateTime();
|
||||
underrunLock.lock();
|
||||
underrun |= (currstamp+len < timestamp);
|
||||
underrunLock.unlock();
|
||||
return len;
|
||||
}
|
||||
|
||||
bool DummyLoad::updateAlignment(TIMESTAMP timestamp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyLoad::setTxFreq(double wFreq) { return true;};
|
||||
bool DummyLoad::setRxFreq(double wFreq) { return true;};
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "radioDevice.h"
|
||||
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
|
||||
class DummyLoad: public RadioDevice {
|
||||
|
||||
private:
|
||||
|
||||
double sampleRate; ///< the desired sampling rate
|
||||
unsigned long long samplesRead; ///< number of samples read from USRP
|
||||
unsigned long long samplesWritten; ///< number of samples sent to USRP
|
||||
|
||||
Mutex underrunLock;
|
||||
|
||||
struct timeval startTime, currTime;
|
||||
|
||||
TIMESTAMP currstamp;
|
||||
short *dummyBurst;
|
||||
int dummyBurstSz;
|
||||
int dummyBurstCursor;
|
||||
bool underrun;
|
||||
|
||||
void updateTime(void);
|
||||
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
DummyLoad (double _desiredSampleRate);
|
||||
|
||||
int loadBurst(short *wDummyBurst, int len);
|
||||
|
||||
/** Instantiate the USRP */
|
||||
bool make(bool skipRx = false);
|
||||
|
||||
/** Start the USRP */
|
||||
bool start();
|
||||
|
||||
/** Stop the USRP */
|
||||
bool stop();
|
||||
|
||||
/**
|
||||
Read samples from the USRP.
|
||||
@param buf preallocated buf to contain read result
|
||||
@param len number of samples desired
|
||||
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
|
||||
@param timestamp The timestamp of the first samples to be read
|
||||
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
int readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL);
|
||||
/**
|
||||
Write samples to the USRP.
|
||||
@param buf Contains the data to be written.
|
||||
@param len number of samples to write.
|
||||
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param timestamp The timestamp of the first sample of the data buffer.
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
int writeSamples(short *buf, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool isControl = false);
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
|
||||
/** Set the transmitter frequency */
|
||||
bool setTxFreq(double wFreq);
|
||||
|
||||
/** Set the receiver frequency */
|
||||
bool setRxFreq(double wFreq);
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
TIMESTAMP initialWriteTimestamp(void) { return 20000;}
|
||||
|
||||
/** Returns the starting read Timestamp*/
|
||||
TIMESTAMP initialReadTimestamp(void) { return 20000;}
|
||||
|
||||
/** returns the full-scale transmit amplitude **/
|
||||
double fullScaleInputValue() {return 13500.0;}
|
||||
|
||||
/** returns the full-scale receive amplitude **/
|
||||
double fullScaleOutputValue() {return 9450.0;}
|
||||
|
||||
/** Return internal status values */
|
||||
inline double getTxFreq() { return 0;}
|
||||
inline double getRxFreq() { return 0;}
|
||||
inline double getSampleRate() {return sampleRate;}
|
||||
inline double numberRead() { return samplesRead; }
|
||||
inline double numberWritten() { return samplesWritten;}
|
||||
|
||||
};
|
||||
|
||||
@@ -21,20 +21,23 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
#UHD wins if both are defined
|
||||
if UHD
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(UHD_CFLAGS)
|
||||
else
|
||||
if USRP1
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USRP_CFLAGS)
|
||||
else
|
||||
#we should never be here, as this doesn't build if one of the above
|
||||
#doesn't exist
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
endif
|
||||
endif
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common
|
||||
AM_CXXFLAGS = -ldl -lpthread
|
||||
|
||||
SUBDIRS = arm x86
|
||||
|
||||
if ARCH_ARM
|
||||
ARCH_LA = arm/libarch.la
|
||||
else
|
||||
ARCH_LA = x86/libarch.la
|
||||
endif
|
||||
|
||||
if USRP1
|
||||
AM_CPPFLAGS += $(USRP_CFLAGS)
|
||||
else
|
||||
AM_CPPFLAGS += $(UHD_CFLAGS)
|
||||
endif
|
||||
|
||||
rev2dir = $(datadir)/usrp/rev2
|
||||
rev4dir = $(datadir)/usrp/rev4
|
||||
|
||||
@@ -51,17 +54,25 @@ COMMON_SOURCES = \
|
||||
radioInterface.cpp \
|
||||
radioVector.cpp \
|
||||
radioClock.cpp \
|
||||
radioBuffer.cpp \
|
||||
sigProcLib.cpp \
|
||||
DriveLoop.cpp \
|
||||
signalVector.cpp \
|
||||
Transceiver.cpp \
|
||||
DummyLoad.cpp
|
||||
ChannelizerBase.cpp \
|
||||
Channelizer.cpp \
|
||||
Synthesis.cpp \
|
||||
common/fft.c
|
||||
|
||||
libtransceiver_la_SOURCES = \
|
||||
$(COMMON_SOURCES)
|
||||
$(COMMON_SOURCES) \
|
||||
Resampler.cpp \
|
||||
radioInterfaceResamp.cpp \
|
||||
radioInterfaceMulti.cpp
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
transceiver \
|
||||
sigProcLibTest
|
||||
bin_PROGRAMS = \
|
||||
osmo-trx \
|
||||
osmo-trx-gen \
|
||||
osmo-trx-dec
|
||||
|
||||
noinst_HEADERS = \
|
||||
Complex.h \
|
||||
@@ -69,46 +80,46 @@ noinst_HEADERS = \
|
||||
radioVector.h \
|
||||
radioClock.h \
|
||||
radioDevice.h \
|
||||
radioBuffer.h \
|
||||
sigProcLib.h \
|
||||
signalVector.h \
|
||||
Transceiver.h \
|
||||
USRPDevice.h \
|
||||
DummyLoad.h \
|
||||
rcvLPF_651.h \
|
||||
sendLPF_961.h
|
||||
Resampler.h \
|
||||
ChannelizerBase.h \
|
||||
Channelizer.h \
|
||||
Synthesis.h \
|
||||
common/convolve.h \
|
||||
common/convert.h \
|
||||
common/scale.h \
|
||||
common/mult.h \
|
||||
common/fft.h
|
||||
|
||||
transceiver_SOURCES = multiTRX.cpp
|
||||
transceiver_LDADD = \
|
||||
osmo_trx_SOURCES = osmo-trx.cpp
|
||||
osmo_trx_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(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)
|
||||
|
||||
sigProcLibTest_SOURCES = sigProcLibTest.cpp
|
||||
sigProcLibTest_LDADD = \
|
||||
osmo_trx_dec_SOURCES = osmo-trx-dec.cpp
|
||||
osmo_trx_dec_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
|
||||
#uhd wins
|
||||
if UHD
|
||||
libtransceiver_la_SOURCES += UHDDevice.cpp
|
||||
transceiver_LDADD += $(UHD_LIBS)
|
||||
sigProcLibTest_LDADD += $(UHD_LIBS)
|
||||
else
|
||||
if USRP1
|
||||
if USRP1
|
||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||
transceiver_LDADD += $(USRP_LIBS)
|
||||
sigProcLibTest_LDADD += $(USRP_LIBS)
|
||||
osmo_trx_LDADD += $(USRP_LIBS)
|
||||
else
|
||||
#we should never be here, as one of the above mustbe defined for us to build
|
||||
libtransceiver_la_SOURCES += UHDDevice.cpp
|
||||
osmo_trx_LDADD += $(UHD_LIBS) $(FFTWF_LIBS)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
MOSTLYCLEANFILES +=
|
||||
|
||||
#radioInterface.cpp
|
||||
#ComplexTest.cpp
|
||||
#sigProcLibTest.cpp
|
||||
#sweepGenerator.cpp
|
||||
#testRadio.cpp
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
Basic model:
|
||||
|
||||
Have channel H = {h_0, h_1, ..., h_{K-1}}.
|
||||
Have received sequence Y = {y_0, ..., y_{K+N}}.
|
||||
Have transmitted sequence X = {x_0, ..., x_{N-1}}.
|
||||
Denote state S_n = {x_n, x_{n-1}, ..., x_{n-L}}.
|
||||
|
||||
Define a bag as an unordered collection with two operations, add and take.
|
||||
We have three bags:
|
||||
S: a bag of survivors.
|
||||
F: a bag of available data structures.
|
||||
|
||||
At time n, start with a non-empty bag S of survivors from time n-1.
|
||||
Take a member out of S, and create all possible branches and their corresponding metrics. If metric ratio is above T, discard branch. Otherwise, check branch against entry in pruning table P. If branch metric is smaller than the existing entry's metric in P, then replace entry with branch. Otherwise, discard branch.
|
||||
Once all possible branches of S have been created and pruned, S should be empty.Empty pruning table back into S, thus P is now empty. Repeat.
|
||||
224
Transceiver52M/Resampler.cpp
Normal file
224
Transceiver52M/Resampler.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Rational Sample Rate Conversion
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "Resampler.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convolve.h"
|
||||
}
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327f
|
||||
#endif
|
||||
|
||||
#define MAX_OUTPUT_LEN 4096
|
||||
|
||||
static float sinc(float x)
|
||||
{
|
||||
if (x == 0.0)
|
||||
return 0.9999999999;
|
||||
|
||||
return sin(M_PI * x) / (M_PI * x);
|
||||
}
|
||||
|
||||
bool Resampler::initFilters(float bw)
|
||||
{
|
||||
size_t proto_len = p * filt_len;
|
||||
float *proto, val, cutoff;
|
||||
float sum = 0.0f, scale = 0.0f;
|
||||
float midpt = (float) (proto_len - 1.0) / 2.0;
|
||||
|
||||
/*
|
||||
* Allocate partition filters and the temporary prototype filter
|
||||
* according to numerator of the rational rate. Coefficients are
|
||||
* real only and must be 16-byte memory aligned for SSE usage.
|
||||
*/
|
||||
proto = new float[proto_len];
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
partitions = (float **) malloc(sizeof(float *) * p);
|
||||
if (!partitions) {
|
||||
delete[] proto;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < p; i++) {
|
||||
partitions[i] = (float *)
|
||||
memalign(16, filt_len * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the prototype filter with a Blackman-harris window.
|
||||
* Scale coefficients with DC filter gain set to unity divided
|
||||
* by the number of filter partitions.
|
||||
*/
|
||||
float a0 = 0.35875;
|
||||
float a1 = 0.48829;
|
||||
float a2 = 0.14128;
|
||||
float a3 = 0.01168;
|
||||
|
||||
if (p > q)
|
||||
cutoff = (float) p;
|
||||
else
|
||||
cutoff = (float) q;
|
||||
|
||||
for (size_t i = 0; i < proto_len; i++) {
|
||||
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
||||
proto[i] *= a0 -
|
||||
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
|
||||
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
|
||||
a3 * cos(6 * M_PI * i / (proto_len - 1));
|
||||
sum += proto[i];
|
||||
}
|
||||
scale = p / sum;
|
||||
|
||||
/* Populate filter partitions from the prototype filter */
|
||||
for (size_t i = 0; i < filt_len; i++) {
|
||||
for (size_t n = 0; n < p; n++) {
|
||||
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
|
||||
partitions[n][2 * i + 1] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* For convolution, we store the filter taps in reverse */
|
||||
for (size_t n = 0; n < p; n++) {
|
||||
for (size_t i = 0; i < filt_len / 2; i++) {
|
||||
val = partitions[n][2 * i];
|
||||
partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
|
||||
partitions[n][2 * (filt_len - 1 - i)] = val;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] proto;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resampler::releaseFilters()
|
||||
{
|
||||
if (partitions) {
|
||||
for (size_t i = 0; i < p; i++)
|
||||
free(partitions[i]);
|
||||
}
|
||||
|
||||
free(partitions);
|
||||
partitions = NULL;
|
||||
}
|
||||
|
||||
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||
{
|
||||
if (in_len % q) {
|
||||
std::cerr << "Invalid input length " << in_len
|
||||
<< " is not multiple of " << q << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out_len % p) {
|
||||
std::cerr << "Invalid output length " << out_len
|
||||
<< " is not multiple of " << p << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((in_len / q) != (out_len / p)) {
|
||||
std::cerr << "Input/output block length mismatch" << std::endl;
|
||||
std::cerr << "P = " << p << ", Q = " << q << std::endl;
|
||||
std::cerr << "Input len: " << in_len << std::endl;
|
||||
std::cerr << "Output len: " << out_len << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out_len > MAX_OUTPUT_LEN) {
|
||||
std::cerr << "Block length of " << out_len
|
||||
<< " exceeds max of " << MAX_OUTPUT_LEN << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resampler::computePath()
|
||||
{
|
||||
for (int i = 0; i < MAX_OUTPUT_LEN; i++) {
|
||||
in_index[i] = (q * i) / p;
|
||||
out_path[i] = (q * i) % p;
|
||||
}
|
||||
}
|
||||
|
||||
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
|
||||
{
|
||||
int n, path;
|
||||
|
||||
if (!check_vec_len(in_len, out_len, p, q))
|
||||
return -1;
|
||||
|
||||
/* Generate output from precomputed input/output paths */
|
||||
for (size_t i = 0; i < out_len; i++) {
|
||||
n = in_index[i];
|
||||
path = out_path[i];
|
||||
|
||||
convolve_real(in, in_len,
|
||||
partitions[path], filt_len,
|
||||
&out[2 * i], out_len - i,
|
||||
n, 1, 1, 0);
|
||||
}
|
||||
|
||||
return out_len;
|
||||
}
|
||||
|
||||
bool Resampler::init(float bw)
|
||||
{
|
||||
/* Filterbank filter internals */
|
||||
if (!initFilters(bw))
|
||||
return false;
|
||||
|
||||
/* Precompute filterbank paths */
|
||||
in_index = new size_t[MAX_OUTPUT_LEN];
|
||||
out_path = new size_t[MAX_OUTPUT_LEN];
|
||||
computePath();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Resampler::len()
|
||||
{
|
||||
return filt_len;
|
||||
}
|
||||
|
||||
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||
: in_index(NULL), out_path(NULL), partitions(NULL)
|
||||
{
|
||||
this->p = p;
|
||||
this->q = q;
|
||||
this->filt_len = filt_len;
|
||||
}
|
||||
|
||||
Resampler::~Resampler()
|
||||
{
|
||||
releaseFilters();
|
||||
|
||||
delete in_index;
|
||||
delete out_path;
|
||||
}
|
||||
76
Transceiver52M/Resampler.h
Normal file
76
Transceiver52M/Resampler.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Rational Sample Rate Conversion
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _RESAMPLER_H_
|
||||
#define _RESAMPLER_H_
|
||||
|
||||
class Resampler {
|
||||
public:
|
||||
/* Constructor for rational sample rate conversion
|
||||
* @param p numerator of resampling ratio
|
||||
* @param q denominator of resampling ratio
|
||||
* @param filt_len length of each polyphase subfilter
|
||||
*/
|
||||
Resampler(size_t p, size_t q, size_t filt_len = 16);
|
||||
~Resampler();
|
||||
|
||||
/* Initilize resampler filterbank.
|
||||
* @param bw bandwidth factor on filter generation (pre-window)
|
||||
* @return false on error, zero otherwise
|
||||
*
|
||||
* Automatic setting is to compute the filter to prevent aliasing with
|
||||
* a Blackman-Harris window. Adjustment is made through a bandwith
|
||||
* factor to shift the cutoff and/or the constituent filter lengths.
|
||||
* Calculation of specific rolloff factors or 3-dB cutoff points is
|
||||
* left as an excersize for the reader.
|
||||
*/
|
||||
bool init(float bw = 1.0f);
|
||||
|
||||
/* Rotate "commutator" and drive samples through filterbank
|
||||
* @param in continuous buffer of input complex float values
|
||||
* @param in_len input buffer length
|
||||
* @param out continuous buffer of output complex float values
|
||||
* @param out_len output buffer length
|
||||
* @return number of samples outputted, negative on error
|
||||
*
|
||||
* Input and output vector lengths must of be equal multiples of the
|
||||
* rational conversion rate denominator and numerator respectively.
|
||||
*/
|
||||
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
|
||||
|
||||
/* Get filter length
|
||||
* @return number of taps in each filter partition
|
||||
*/
|
||||
size_t len();
|
||||
|
||||
private:
|
||||
size_t p;
|
||||
size_t q;
|
||||
size_t filt_len;
|
||||
size_t *in_index;
|
||||
size_t *out_path;
|
||||
|
||||
float **partitions;
|
||||
|
||||
bool initFilters(float bw);
|
||||
void releaseFilters();
|
||||
void computePath();
|
||||
};
|
||||
|
||||
#endif /* _RESAMPLER_H_ */
|
||||
121
Transceiver52M/Synthesis.cpp
Normal file
121
Transceiver52M/Synthesis.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Polyphase synthesis filter
|
||||
*
|
||||
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Synthesis.h"
|
||||
|
||||
extern "C" {
|
||||
#include "common/fft.h"
|
||||
#include "common/convolve.h"
|
||||
}
|
||||
|
||||
static void interleave(float **in, size_t ilen,
|
||||
float *out, size_t m)
|
||||
{
|
||||
size_t i, n;
|
||||
|
||||
for (i = 0; i < ilen; i++) {
|
||||
for (n = 0; n < m; n++) {
|
||||
out[2 * (i * m + n) + 0] = in[n][2 * i + 0];
|
||||
out[2 * (i * m + n) + 1] = in[n][2 * i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Synthesis::inputLen() const
|
||||
{
|
||||
return blockLen;
|
||||
}
|
||||
|
||||
size_t Synthesis::outputLen() const
|
||||
{
|
||||
return blockLen * m;
|
||||
}
|
||||
|
||||
float *Synthesis::inputBuffer(size_t chan) const
|
||||
{
|
||||
if (chan >= m)
|
||||
return NULL;
|
||||
|
||||
return hOutputs[chan];
|
||||
}
|
||||
|
||||
bool Synthesis::resetBuffer(size_t chan)
|
||||
{
|
||||
if (chan >= m)
|
||||
return false;
|
||||
|
||||
memset(hOutputs[chan], 0, blockLen * 2 * sizeof(float));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation based on material found in:
|
||||
*
|
||||
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
|
||||
* Prentice Hall, 2006."
|
||||
*/
|
||||
bool Synthesis::rotate(float *out, size_t len)
|
||||
{
|
||||
size_t hSize = 2 * hLen * sizeof(float);
|
||||
|
||||
if (!checkLen(blockLen, len)) {
|
||||
std::cout << "Length fail" << std::endl;
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
cxvec_fft(fftHandle);
|
||||
|
||||
/*
|
||||
* Convolve through filterbank while applying and saving sample history
|
||||
*/
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
|
||||
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
|
||||
|
||||
convolve_real(hInputs[i], blockLen,
|
||||
subFilters[i], hLen,
|
||||
hOutputs[i], blockLen,
|
||||
0, blockLen, 1, 0);
|
||||
}
|
||||
|
||||
/* Interleave into output vector */
|
||||
interleave(hOutputs, blockLen, out, m);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Synthesis::Synthesis(size_t m, size_t blockLen, size_t hLen)
|
||||
: ChannelizerBase(m, blockLen, hLen)
|
||||
{
|
||||
}
|
||||
|
||||
Synthesis::~Synthesis()
|
||||
{
|
||||
}
|
||||
35
Transceiver52M/Synthesis.h
Normal file
35
Transceiver52M/Synthesis.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef _SYNTHESIS_H_
|
||||
#define _SYNTHESIS_H_
|
||||
|
||||
#include "ChannelizerBase.h"
|
||||
|
||||
class Synthesis : public ChannelizerBase {
|
||||
public:
|
||||
/** Constructor for synthesis filterbank
|
||||
@param m number of physical channels
|
||||
@param blockLen number of samples per output of each iteration
|
||||
@param hLen number of taps in each constituent filter path
|
||||
*/
|
||||
Synthesis(size_t m, size_t blockLen, size_t hLen = 16);
|
||||
~Synthesis();
|
||||
|
||||
/* Return required input and output buffer lengths */
|
||||
size_t inputLen() const;
|
||||
size_t outputLen() const;
|
||||
|
||||
/** Rotate "output commutator" and drive samples through filterbank
|
||||
@param out complex output vector
|
||||
@param oLen number of samples in buffer (must match block length * m)
|
||||
@return false on error and true otherwise
|
||||
*/
|
||||
bool rotate(float *out, size_t oLen);
|
||||
|
||||
/** Get buffer for an input path
|
||||
@param chan channel number of filterbank
|
||||
@return NULL on error and pointer to buffer otherwise
|
||||
*/
|
||||
float *inputBuffer(size_t chan) const;
|
||||
bool resetBuffer(size_t chan);
|
||||
};
|
||||
|
||||
#endif /* _SYNTHESIS_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008, 2012 Free Software Foundation, Inc.
|
||||
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
@@ -23,14 +22,6 @@
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Compilation switches
|
||||
TRANSMIT_LOGGING write every burst on the given slot to a log
|
||||
*/
|
||||
|
||||
#include "DriveLoop.h"
|
||||
#include "radioInterface.h"
|
||||
#include "Interthread.h"
|
||||
#include "GSMCommon.h"
|
||||
@@ -39,165 +30,252 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/** Define this to be the slot number to be logged. */
|
||||
//#define TRANSMIT_LOGGING 1
|
||||
class Transceiver;
|
||||
|
||||
/** FIFO thread loop */
|
||||
class FIFOServiceLoopThread : public Thread {
|
||||
public:
|
||||
FIFOServiceLoopThread() : Thread("FIFOServiceLoopThread") {}
|
||||
Thread::ReturnStatus shutdown();
|
||||
/** Channel descriptor for transceiver object and channel number pair */
|
||||
struct TransceiverChannel {
|
||||
TransceiverChannel(Transceiver *trx, int num)
|
||||
{
|
||||
this->trx = trx;
|
||||
this->num = num;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void runThread();
|
||||
~TransceiverChannel()
|
||||
{
|
||||
}
|
||||
|
||||
Transceiver *trx;
|
||||
size_t num;
|
||||
};
|
||||
|
||||
/** control message handler thread loop */
|
||||
class ControlServiceLoopThread : public Thread {
|
||||
public:
|
||||
ControlServiceLoopThread() : Thread("ControlServiceLoopThread") {}
|
||||
Thread::ReturnStatus shutdown();
|
||||
/** Internal transceiver state variables */
|
||||
struct TransceiverState {
|
||||
TransceiverState();
|
||||
~TransceiverState();
|
||||
|
||||
protected:
|
||||
virtual void runThread();
|
||||
};
|
||||
/* Initialize a multiframe slot in the filler table */
|
||||
bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
|
||||
|
||||
/** transmit queueing thread loop */
|
||||
class TransmitPriorityQueueServiceLoopThread : public Thread {
|
||||
public:
|
||||
TransmitPriorityQueueServiceLoopThread() : Thread("TransmitPriorityQueueServiceLoopThread") {}
|
||||
Thread::ReturnStatus shutdown();
|
||||
int chanType[8];
|
||||
|
||||
protected:
|
||||
virtual void runThread();
|
||||
/* Last timestamp of each timeslot's channel estimate */
|
||||
GSM::Time chanEstimateTime[8];
|
||||
|
||||
/* The filler table */
|
||||
signalVector *fillerTable[102][8];
|
||||
int fillerModulus[8];
|
||||
bool mRetrans;
|
||||
|
||||
/* Most recent channel estimate of all timeslots */
|
||||
signalVector *chanResponse[8];
|
||||
|
||||
/* Most recent DFE feedback filter of all timeslots */
|
||||
signalVector *DFEForward[8];
|
||||
signalVector *DFEFeedback[8];
|
||||
|
||||
/* Most recent SNR, timing, and channel amplitude estimates */
|
||||
float SNRestimate[8];
|
||||
float chanRespOffset[8];
|
||||
complex chanRespAmplitude[8];
|
||||
|
||||
/* Received noise energy levels */
|
||||
float mNoiseLev;
|
||||
noiseVector mNoises;
|
||||
|
||||
/* Shadowed downlink attenuation */
|
||||
int mPower;
|
||||
|
||||
/* Pseudorandom bit sequence */
|
||||
PRBS9 mPrbs;
|
||||
};
|
||||
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
class Transceiver {
|
||||
|
||||
public:
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX manager, as a string
|
||||
@param wSPS number of samples per GSM symbol
|
||||
@param wTransmitLatency initial setting of transmit latency
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
double wRssiOffset);
|
||||
|
||||
/** Destructor */
|
||||
~Transceiver();
|
||||
|
||||
/** Start the control loop */
|
||||
bool init(int filler, size_t rtsc, unsigned rach_delay, bool edge);
|
||||
|
||||
/** attach the radioInterface receive FIFO */
|
||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||
{
|
||||
if (chan >= mReceiveFIFO.size())
|
||||
return false;
|
||||
|
||||
mReceiveFIFO[chan] = wFIFO;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** accessor for number of channels */
|
||||
size_t numChans() const { return mChans; };
|
||||
|
||||
/** Codes for channel combinations */
|
||||
typedef enum {
|
||||
FILL, ///< Channel is transmitted, but unused
|
||||
I, ///< TCH/FS
|
||||
II, ///< TCH/HS, idle every other slot
|
||||
III, ///< TCH/HS
|
||||
IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
|
||||
V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
|
||||
VI, ///< CCCH+BCCH, uplink RACH
|
||||
VII, ///< SDCCH/8 + SACCH/8
|
||||
VIII, ///< TCH/F + FACCH/F + SACCH/M
|
||||
IX, ///< TCH/F + SACCH/M
|
||||
X, ///< TCH/FD + SACCH/MD
|
||||
XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
|
||||
XII, ///< PCCCH+PDTCH+PACCH+PTCCH
|
||||
XIII, ///< PDTCH+PACCH+PTCCH
|
||||
NONE, ///< Channel is inactive, default
|
||||
LOOPBACK ///< similar go VII, used in loopback testing
|
||||
} ChannelCombination;
|
||||
|
||||
enum FillerType {
|
||||
FILLER_DUMMY,
|
||||
FILLER_ZERO,
|
||||
FILLER_NORM_RAND,
|
||||
FILLER_EDGE_RAND,
|
||||
FILLER_ACCESS_RAND,
|
||||
};
|
||||
|
||||
private:
|
||||
DriveLoop *mDriveLoop;
|
||||
int mBasePort;
|
||||
std::string mAddr;
|
||||
|
||||
int mBasePort; ///< Base port address for all our ports
|
||||
std::string mTRXAddress; ///< Address of the BTS TRX control interface
|
||||
UDPSocket mDataSocket; ///< socket for writing to/reading from GSM core
|
||||
UDPSocket mControlSocket; ///< socket for writing/reading control commands 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
|
||||
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
||||
|
||||
VectorQueue *mTransmitPriorityQueue; ///< priority queue of transmit bursts received from GSM core
|
||||
VectorFIFO* mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||
|
||||
friend class FIFOServiceLoopThread;
|
||||
FIFOServiceLoopThread mFIFOServiceLoop; ///< thread to push/pull bursts into transmit/receive FIFO
|
||||
friend class ControlServiceLoopThread;
|
||||
ControlServiceLoopThread mControlServiceLoop; ///< thread to process control messages from GSM core
|
||||
friend class TransmitPriorityQueueServiceLoopThread;
|
||||
TransmitPriorityQueueServiceLoopThread mTransmitPriorityQueueServiceLoop;///< thread to process transmit bursts from GSM core
|
||||
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
||||
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
||||
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
|
||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||
|
||||
int mChannel; ///< channelizer attach number between 0 and 'M-1'
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||
|
||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||
double txFullScale; ///< full scale input to radio
|
||||
double rxFullScale; ///< full scale output to radio
|
||||
|
||||
/** unmodulate a modulated burst */
|
||||
#ifdef TRANSMIT_LOGGING
|
||||
void unModulateVector(signalVector wVector);
|
||||
#endif
|
||||
double rssiOffset; ///< RSSI to dBm conversion offset
|
||||
|
||||
/** modulate and add a burst to the transmit queue */
|
||||
void addRadioVector(BitVector &burst,
|
||||
int RSSI,
|
||||
GSM::Time &wTime);
|
||||
void addRadioVector(size_t chan, BitVector &bits,
|
||||
int RSSI, GSM::Time &wTime);
|
||||
|
||||
/** Update filler table */
|
||||
void updateFillerTable(size_t chan, radioVector *burst);
|
||||
|
||||
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
|
||||
void pushRadioVector(GSM::Time &nowTime);
|
||||
|
||||
/** Pull and demodulate a burst from the receive FIFO */
|
||||
SoftVector *pullRadioVector(GSM::Time &wTime,
|
||||
int &RSSI,
|
||||
int &timingOffset);
|
||||
|
||||
/** Pull and demodulate a burst from the receive FIFO */
|
||||
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
||||
double &timingOffset, double &noise,
|
||||
size_t chan = 0);
|
||||
|
||||
/** Set modulus for specific timeslot */
|
||||
void setModulus(size_t timeslot, size_t chan);
|
||||
|
||||
/** return the expected burst type for the specified timestamp */
|
||||
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
|
||||
|
||||
/** send messages over the clock socket */
|
||||
void writeClockInterface(void);
|
||||
|
||||
void pullFIFO(void); ///< blocking call on receive FIFO
|
||||
int mSPSTx; ///< number of samples per Tx symbol
|
||||
int mSPSRx; ///< number of samples per Rx symbol
|
||||
size_t mChans;
|
||||
|
||||
signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
|
||||
|
||||
int mSamplesPerSymbol; ///< number of samples per GSM symbol
|
||||
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
bool mRunning; ///< flag to indicate control loop is running
|
||||
bool mPrimary; ///< flag to indicate C0 channel
|
||||
bool mEdge;
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||
double mTxFreq; ///< the transmit frequency
|
||||
double mRxFreq; ///< the receive frequency
|
||||
double mFreqOffset; ///< RF frequency offset
|
||||
int mPower; ///< the transmit power in dB
|
||||
double mEnergyThreshold; ///< threshold to determine if received data is potentially a GSM burst
|
||||
GSM::Time prevFalseDetectionTime; ///< last timestamp of a false energy detection
|
||||
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
||||
unsigned mTSC; ///< the midamble sequence code
|
||||
unsigned mMaxExpectedDelayAB; ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH)
|
||||
unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
|
||||
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
|
||||
|
||||
GSM::Time channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
|
||||
signalVector *channelResponse[8]; ///< most recent channel estimate of all timeslots
|
||||
float SNRestimate[8]; ///< most recent SNR estimate of all timeslots
|
||||
signalVector *DFEForward[8]; ///< most recent DFE feedforward filter of all timeslots
|
||||
signalVector *DFEFeedback[8]; ///< most recent DFE feedback filter of all timeslots
|
||||
float chanRespOffset[8]; ///< most recent timing offset, e.g. TOA, of all timeslots
|
||||
complex chanRespAmplitude[8]; ///< most recent channel amplitude of all timeslots
|
||||
std::vector<TransceiverState> mStates;
|
||||
|
||||
static int mTSC; ///< the midamble sequence code
|
||||
/** Start and stop I/O threads through the control socket API */
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
public:
|
||||
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX manager, as a string
|
||||
@param wSamplesPerSymbol number of samples per GSM symbol
|
||||
@param wTransmitLatency initial setting of transmit latency
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
Transceiver(int wBasePort, const char *TRXAddress,
|
||||
DriveLoop *wDriveLoop, RadioInterface *wRadioInterface,
|
||||
int wSamplesPerSymbol = SAMPSPERSYM,
|
||||
int wChannel = 0, bool wPrimary = true);
|
||||
|
||||
/** Destructor */
|
||||
~Transceiver();
|
||||
|
||||
/** start the Transceiver */
|
||||
void start();
|
||||
|
||||
/** shutdown (teardown threads) the Transceiver */
|
||||
void shutdown();
|
||||
/** Protect destructor accessable stop call */
|
||||
Mutex mLock;
|
||||
|
||||
protected:
|
||||
/** drive lower receive I/O and burst generation */
|
||||
void driveReceiveRadio();
|
||||
|
||||
/** drive reception and demodulation of GSM bursts */
|
||||
void driveReceiveFIFO();
|
||||
/** drive demodulation of GSM bursts */
|
||||
void driveReceiveFIFO(size_t chan);
|
||||
|
||||
/** drive transmission of GSM bursts */
|
||||
void driveTransmitFIFO();
|
||||
void driveTxFIFO();
|
||||
|
||||
/** drive handling of control messages from GSM core */
|
||||
void driveControl();
|
||||
void driveControl(size_t chan);
|
||||
|
||||
/**
|
||||
drive modulation and sorting of GSM bursts from GSM core
|
||||
@return true if a burst was transferred successfully
|
||||
*/
|
||||
bool driveTransmitPriorityQueue();
|
||||
bool driveTxPriorityQueue(size_t chan);
|
||||
|
||||
friend void *RxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
friend void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
friend void *RxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *TxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
|
||||
void reset();
|
||||
|
||||
/** return transceiver on/off status */
|
||||
bool on() { return mOn; }
|
||||
|
||||
/** return control loop operational status */
|
||||
bool running() { return mRunning; }
|
||||
|
||||
/** return the drive loop pointer */
|
||||
DriveLoop *getDriveLoop() { return mDriveLoop; }
|
||||
|
||||
/** set priority on current thread */
|
||||
void setPriority() { mRadioInterface->setPriority(); }
|
||||
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
|
||||
|
||||
void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
|
||||
double rssi, double noise, double toa);
|
||||
};
|
||||
|
||||
void *RxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** Main drive threads */
|
||||
void *RxLowerLoopAdapter(Transceiver *);
|
||||
void *TxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
/** control message handler thread loop */
|
||||
void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** transmit queueing thread loop */
|
||||
void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -59,14 +59,28 @@ const dboardConfigType dboardConfig = TXA_RXB;
|
||||
|
||||
const double USRPDevice::masterClockRate = 52.0e6;
|
||||
|
||||
USRPDevice::USRPDevice(int sps, bool skipRx)
|
||||
: skipRx(skipRx)
|
||||
USRPDevice::USRPDevice(size_t sps)
|
||||
{
|
||||
LOG(INFO) << "creating USRP device...";
|
||||
|
||||
this->sps = sps;
|
||||
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
|
||||
actualSampleRate = masterClockRate/decimRate;
|
||||
rxGain = 0;
|
||||
|
||||
/*
|
||||
* Undetermined delay b/w ping response timestamp and true
|
||||
* receive timestamp. Values are empirically measured. With
|
||||
* split sample rate Tx/Rx - 4/1 sps we need to need to
|
||||
* compensate for advance rather than delay.
|
||||
*/
|
||||
if (sps == 1)
|
||||
pingOffset = 272;
|
||||
else if (sps == 4)
|
||||
pingOffset = 269 - 7500;
|
||||
else
|
||||
pingOffset = 0;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
samplePeriod = 1.0e6/actualSampleRate;
|
||||
loopbackBufferSize = 0;
|
||||
@@ -75,7 +89,7 @@ USRPDevice::USRPDevice(int sps, bool skipRx)
|
||||
#endif
|
||||
}
|
||||
|
||||
int USRPDevice::open(const std::string &)
|
||||
int USRPDevice::open(const std::string &, int, bool)
|
||||
{
|
||||
writeLock.unlock();
|
||||
|
||||
@@ -86,14 +100,13 @@ int USRPDevice::open(const std::string &)
|
||||
m_uRx.reset();
|
||||
if (!skipRx) {
|
||||
try {
|
||||
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(0,decimRate,1,-1,
|
||||
usrp_standard_rx::FPGA_MODE_NORMAL,
|
||||
1024,16*8,rbf));
|
||||
#ifdef HAVE_LIBUSRP_3_2
|
||||
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
|
||||
0, decimRate * sps, 1, -1,
|
||||
usrp_standard_rx::FPGA_MODE_NORMAL,
|
||||
1024, 16 * 8, rbf));
|
||||
m_uRx->set_fpga_master_clock_freq(masterClockRate);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
catch(...) {
|
||||
LOG(ALERT) << "make failed on Rx";
|
||||
m_uRx.reset();
|
||||
@@ -110,13 +123,12 @@ int USRPDevice::open(const std::string &)
|
||||
}
|
||||
|
||||
try {
|
||||
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(0,decimRate*2,1,-1,
|
||||
1024,16*8,rbf));
|
||||
#ifdef HAVE_LIBUSRP_3_2
|
||||
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
|
||||
0, decimRate * 2, 1, -1,
|
||||
1024, 16 * 8, rbf));
|
||||
m_uTx->set_fpga_master_clock_freq(masterClockRate);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
catch(...) {
|
||||
LOG(ALERT) << "make failed on Tx";
|
||||
m_uTx.reset();
|
||||
@@ -253,49 +265,66 @@ double USRPDevice::minRxGain()
|
||||
return m_dbRx->gain_min();
|
||||
}
|
||||
|
||||
double USRPDevice::setTxGain(double dB) {
|
||||
|
||||
writeLock.lock();
|
||||
if (dB > maxTxGain()) dB = maxTxGain();
|
||||
if (dB < minTxGain()) dB = minTxGain();
|
||||
double USRPDevice::setTxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||
writeLock.lock();
|
||||
if (dB > maxTxGain())
|
||||
dB = maxTxGain();
|
||||
if (dB < minTxGain())
|
||||
dB = minTxGain();
|
||||
|
||||
if (!m_dbTx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting TX gain";
|
||||
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
return dB;
|
||||
if (!m_dbTx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting TX gain";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
return dB;
|
||||
}
|
||||
|
||||
|
||||
double USRPDevice::setRxGain(double dB) {
|
||||
double USRPDevice::setRxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
writeLock.lock();
|
||||
if (dB > maxRxGain()) dB = maxRxGain();
|
||||
if (dB < minRxGain()) dB = minRxGain();
|
||||
|
||||
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
|
||||
dB = 47.0;
|
||||
|
||||
if (!m_dbRx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting RX gain";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
return dB;
|
||||
writeLock.lock();
|
||||
if (dB > maxRxGain())
|
||||
dB = maxRxGain();
|
||||
if (dB < minRxGain())
|
||||
dB = minRxGain();
|
||||
|
||||
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
|
||||
|
||||
if (!m_dbRx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting RX gain";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
return dB;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Assumes sequential reads
|
||||
int USRPDevice::readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp,
|
||||
bool *underrun,
|
||||
unsigned *RSSI)
|
||||
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx) return 0;
|
||||
|
||||
if (!m_uRx)
|
||||
return 0;
|
||||
|
||||
short *buf = bufs[0];
|
||||
|
||||
timestamp += timestampOffset;
|
||||
|
||||
if (timestamp + len < timeStart) {
|
||||
@@ -341,7 +370,7 @@ int USRPDevice::readSamples(short *buf, int len, bool *overrun,
|
||||
uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
|
||||
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
|
||||
timestamp -= timestampOffset;
|
||||
timestampOffset = pktTimestamp - pingTimestamp + PINGOFFSET;
|
||||
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
|
||||
LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
|
||||
timestamp += timestampOffset;
|
||||
isAligned = true;
|
||||
@@ -437,17 +466,20 @@ int USRPDevice::readSamples(short *buf, int len, bool *overrun,
|
||||
#endif
|
||||
}
|
||||
|
||||
int USRPDevice::writeSamples(short *buf, int len, bool *underrun,
|
||||
unsigned long long timestamp,
|
||||
bool isControl)
|
||||
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
bool *underrun, unsigned long long timestamp,
|
||||
bool isControl)
|
||||
{
|
||||
writeLock.lock();
|
||||
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uTx) return 0;
|
||||
|
||||
if (!m_uTx)
|
||||
return 0;
|
||||
|
||||
short *buf = bufs[0];
|
||||
|
||||
static uint32_t outData[128*20];
|
||||
|
||||
|
||||
for (int i = 0; i < len*2; i++) {
|
||||
buf[i] = host_to_usrp_short(buf[i]);
|
||||
}
|
||||
@@ -499,7 +531,9 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
uint32_t *wordPtr = (uint32_t *) data;
|
||||
*wordPtr = host_to_usrp_u32(*wordPtr);
|
||||
bool tmpUnderrun;
|
||||
if (writeSamples((short *) data,1,&tmpUnderrun,timestamp & 0x0ffffffffll,true)) {
|
||||
|
||||
std::vector<short *> buf(1, data);
|
||||
if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
|
||||
pingTimestamp = timestamp;
|
||||
return true;
|
||||
}
|
||||
@@ -510,10 +544,15 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
}
|
||||
|
||||
#ifndef SWLOOPBACK
|
||||
bool USRPDevice::setTxFreq(double wFreq)
|
||||
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
usrp_tune_result result;
|
||||
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
|
||||
LOG(INFO) << "set TX: " << wFreq << std::endl
|
||||
<< " baseband freq: " << result.baseband_freq << std::endl
|
||||
@@ -530,10 +569,15 @@ bool USRPDevice::setTxFreq(double wFreq)
|
||||
}
|
||||
}
|
||||
|
||||
bool USRPDevice::setRxFreq(double wFreq)
|
||||
bool USRPDevice::setRxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
usrp_tune_result result;
|
||||
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
|
||||
LOG(INFO) << "set RX: " << wFreq << std::endl
|
||||
<< " baseband freq: " << result.baseband_freq << std::endl
|
||||
@@ -556,7 +600,8 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
|
||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
||||
#endif
|
||||
|
||||
RadioDevice *RadioDevice::make(int sps, bool skipRx)
|
||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||
size_t chans, double)
|
||||
{
|
||||
return new USRPDevice(sps, skipRx);
|
||||
return new USRPDevice(tx_sps);
|
||||
}
|
||||
|
||||
@@ -21,29 +21,17 @@
|
||||
|
||||
#include "radioDevice.h"
|
||||
|
||||
#ifdef HAVE_LIBUSRP_3_3 // [
|
||||
# include <usrp/usrp_standard.h>
|
||||
# include <usrp/usrp_bytesex.h>
|
||||
# include <usrp/usrp_prims.h>
|
||||
#else // HAVE_LIBUSRP_3_3 ][
|
||||
# include "usrp_standard.h"
|
||||
# include "usrp_bytesex.h"
|
||||
# include "usrp_prims.h"
|
||||
#endif // !HAVE_LIBUSRP_3_3 ]
|
||||
#include <usrp/usrp_standard.h>
|
||||
#include <usrp/usrp_bytesex.h>
|
||||
#include <usrp/usrp_prims.h>
|
||||
#include <sys/time.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/** Define types which are not defined in libusrp-3.1 */
|
||||
#ifndef HAVE_LIBUSRP_3_2
|
||||
#include <boost/shared_ptr.hpp>
|
||||
typedef boost::shared_ptr<usrp_standard_tx> usrp_standard_tx_sptr;
|
||||
typedef boost::shared_ptr<usrp_standard_rx> usrp_standard_rx_sptr;
|
||||
#endif // HAVE_LIBUSRP_3_2
|
||||
|
||||
|
||||
|
||||
/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
|
||||
class USRPDevice: public RadioDevice {
|
||||
@@ -60,6 +48,7 @@ private:
|
||||
usrp_subdev_spec rxSubdevSpec;
|
||||
usrp_subdev_spec txSubdevSpec;
|
||||
|
||||
int sps;
|
||||
double actualSampleRate; ///< the actual USRP sampling rate
|
||||
unsigned int decimRate; ///< the USRP decimation rate
|
||||
|
||||
@@ -87,7 +76,8 @@ private:
|
||||
TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks
|
||||
TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command
|
||||
TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response
|
||||
static const TIMESTAMP PINGOFFSET = 272; ///< undetermined delay b/w ping response timestamp and true receive timestamp
|
||||
|
||||
long long pingOffset;
|
||||
unsigned long hi32Timestamp;
|
||||
unsigned long lastPktTimestamp;
|
||||
|
||||
@@ -103,19 +93,13 @@ private:
|
||||
bool firstRead;
|
||||
#endif
|
||||
|
||||
/** Set the transmission frequency */
|
||||
bool tx_setFreq(double freq, double *actual_freq);
|
||||
|
||||
/** Set the receiver frequency */
|
||||
bool rx_setFreq(double freq, double *actual_freq);
|
||||
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
USRPDevice(int sps, bool skipRx);
|
||||
USRPDevice(size_t sps);
|
||||
|
||||
/** Instantiate the USRP */
|
||||
int open(const std::string &);
|
||||
int open(const std::string &, int, bool);
|
||||
|
||||
/** Start the USRP */
|
||||
bool start();
|
||||
@@ -124,7 +108,7 @@ private:
|
||||
bool stop();
|
||||
|
||||
/** Set priority not supported */
|
||||
void setPriority() { return; }
|
||||
void setPriority(float prio = 0.5) { }
|
||||
|
||||
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
|
||||
|
||||
@@ -138,10 +122,9 @@ private:
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
int readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL);
|
||||
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL);
|
||||
/**
|
||||
Write samples to the USRP.
|
||||
@param buf Contains the data to be written.
|
||||
@@ -151,18 +134,17 @@ private:
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
int writeSamples(short *buf, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool isControl = false);
|
||||
|
||||
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
|
||||
|
||||
/** Set the transmitter frequency */
|
||||
bool setTxFreq(double wFreq);
|
||||
bool setTxFreq(double wFreq, size_t chan = 0);
|
||||
|
||||
/** Set the receiver frequency */
|
||||
bool setRxFreq(double wFreq);
|
||||
bool setRxFreq(double wFreq, size_t chan = 0);
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
TIMESTAMP initialWriteTimestamp(void) { return 20000;}
|
||||
@@ -177,10 +159,10 @@ private:
|
||||
double fullScaleOutputValue() {return 9450.0;}
|
||||
|
||||
/** sets the receive chan gain, returns the gain setting **/
|
||||
double setRxGain(double dB);
|
||||
double setRxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** get the current receive gain */
|
||||
double getRxGain(void) {return rxGain;}
|
||||
double getRxGain(size_t chan = 0) { return rxGain; }
|
||||
|
||||
/** return maximum Rx Gain **/
|
||||
double maxRxGain(void);
|
||||
@@ -189,7 +171,7 @@ private:
|
||||
double minRxGain(void);
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
double setTxGain(double dB);
|
||||
double setTxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** return maximum Tx Gain **/
|
||||
double maxTxGain(void);
|
||||
@@ -197,13 +179,12 @@ private:
|
||||
/** return minimum Rx Gain **/
|
||||
double minTxGain(void);
|
||||
|
||||
|
||||
/** Return internal status values */
|
||||
inline double getTxFreq() { return 0;}
|
||||
inline double getRxFreq() { return 0;}
|
||||
inline double getSampleRate() {return actualSampleRate;}
|
||||
inline double getTxFreq(size_t chan = 0) { return 0; }
|
||||
inline double getRxFreq(size_t chan = 0) { return 0; }
|
||||
inline double getSampleRate() { return actualSampleRate; }
|
||||
inline double numberRead() { return samplesRead; }
|
||||
inline double numberWritten() { return samplesWritten;}
|
||||
inline double numberWritten() { return samplesWritten; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <Logger.h>
|
||||
#include <Configuration.h>
|
||||
#include "radioDevice.h"
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// Configure logger.
|
||||
if (argc>1) gLogInit(argv[1]);
|
||||
else gLogInit("DEBUG");
|
||||
//if (argc>2) gSetLogFile(argv[2]);
|
||||
|
||||
RadioDevice *usrp = RadioDevice::make(52.0e6/192.0, 1);
|
||||
|
||||
usrp->open("");
|
||||
|
||||
TIMESTAMP timestamp;
|
||||
|
||||
usrp->setTxFreq(825.4e6);
|
||||
usrp->setRxFreq(825.4e6);
|
||||
|
||||
usrp->start();
|
||||
|
||||
usrp->setRxGain(57);
|
||||
|
||||
LOG(INFO) << "Looping...";
|
||||
bool underrun;
|
||||
|
||||
short data[]={0x00,0x02};
|
||||
|
||||
usrp->updateAlignment(20000);
|
||||
usrp->updateAlignment(21000);
|
||||
|
||||
int numpkts = 1;
|
||||
short data2[512*2*numpkts];
|
||||
for (int i = 0; i < 512*numpkts; i++) {
|
||||
data2[i<<1] = 10000;//4096*cos(2*3.14159*(i % 126)/126);
|
||||
data2[(i<<1) + 1] = 10000;//4096*sin(2*3.14159*(i % 126)/126);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1; i++)
|
||||
usrp->writeSamples((short*) data2,512*numpkts,&underrun,102000+i*1000);
|
||||
|
||||
timestamp = 19000;
|
||||
double sum = 0.0;
|
||||
unsigned long num = 0;
|
||||
while (1) {
|
||||
short readBuf[512*2];
|
||||
int rd = usrp->readSamples(readBuf,512,&underrun,timestamp);
|
||||
if (rd) {
|
||||
LOG(INFO) << "rcvd. data@:" << timestamp;
|
||||
for (int i = 0; i < 512; i++) {
|
||||
uint32_t *wordPtr = (uint32_t *) &readBuf[2*i];
|
||||
printf ("%llu: %d %d\n", timestamp+i,readBuf[2*i],readBuf[2*i+1]);
|
||||
sum += (readBuf[2*i+1]*readBuf[2*i+1] + readBuf[2*i]*readBuf[2*i]);
|
||||
num++;
|
||||
//if (num % 10000 == 0) printf("avg pwr: %f\n",sum/num);
|
||||
}
|
||||
timestamp += rd;
|
||||
//usrp->writeSamples((short*) data2,512*numpkts,&underrun,timestamp+1000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
23
Transceiver52M/arm/Makefile.am
Normal file
23
Transceiver52M/arm/Makefile.am
Normal file
@@ -0,0 +1,23 @@
|
||||
if ARCH_ARM
|
||||
if ARCH_ARM_A15
|
||||
ARCH_FLAGS = -mfpu=neon-vfpv4
|
||||
else
|
||||
ARCH_FLAGS = -mfpu=neon
|
||||
endif
|
||||
|
||||
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I../common
|
||||
AM_CCASFLAGS = $(ARCH_FLAGS)
|
||||
|
||||
noinst_LTLIBRARIES = libarch.la
|
||||
|
||||
libarch_la_SOURCES = \
|
||||
../common/convolve_base.c \
|
||||
convert.c \
|
||||
convert_neon.S \
|
||||
convolve.c \
|
||||
convolve_neon.S \
|
||||
scale.c \
|
||||
scale_neon.S \
|
||||
mult.c \
|
||||
mult_neon.S
|
||||
endif
|
||||
83
Transceiver52M/arm/convert.c
Normal file
83
Transceiver52M/arm/convert.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* NEON type conversions
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "convert.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
|
||||
void neon_convert_si16_ps_4n(float *, const short *, int);
|
||||
|
||||
/* 4*N 16-bit signed integer conversion with remainder */
|
||||
static void neon_convert_si16_ps(float *out,
|
||||
const short *in,
|
||||
int len)
|
||||
{
|
||||
int start = len / 4 * 4;
|
||||
|
||||
neon_convert_si16_ps_4n(out, in, len >> 2);
|
||||
|
||||
for (int i = 0; i < len % 4; i++)
|
||||
out[start + i] = (float) in[start + i];
|
||||
}
|
||||
|
||||
/* 4*N 16-bit signed integer conversion with remainder */
|
||||
static void neon_convert_ps_si16(short *out,
|
||||
const float *in,
|
||||
const float *scale,
|
||||
int len)
|
||||
{
|
||||
int start = len / 4 * 4;
|
||||
|
||||
neon_convert_ps_si16_4n(out, in, scale, len >> 2);
|
||||
|
||||
for (int i = 0; i < len % 4; i++)
|
||||
out[start + i] = (short) (in[start + i] * (*scale));
|
||||
}
|
||||
#endif
|
||||
|
||||
void convert_float_short(short *out, const float *in, float scale, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
float q[4] = { scale, scale, scale, scale };
|
||||
|
||||
if (len % 4)
|
||||
neon_convert_ps_si16(out, in, q, len);
|
||||
else
|
||||
neon_convert_ps_si16_4n(out, in, q, len >> 2);
|
||||
#else
|
||||
base_convert_float_short(out, in, scale, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void convert_short_float(float *out, const short *in, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if (len % 4)
|
||||
neon_convert_si16_ps(out, in, len);
|
||||
else
|
||||
neon_convert_si16_ps_4n(out, in, len >> 2);
|
||||
#else
|
||||
base_convert_short_float(out, in, len);
|
||||
#endif
|
||||
}
|
||||
51
Transceiver52M/arm/convert_neon.S
Normal file
51
Transceiver52M/arm/convert_neon.S
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* NEON type conversions
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.align 2
|
||||
.global neon_convert_ps_si16_4n
|
||||
.type neon_convert_ps_si16_4n, %function
|
||||
neon_convert_ps_si16_4n:
|
||||
vld1.32 {q1}, [r2]
|
||||
.loop_fltint:
|
||||
vld1.64 {d0-d1}, [r1]!
|
||||
vmul.f32 q0, q1
|
||||
vcvt.s32.f32 q2, q0
|
||||
vqmovn.s32 d0, q2
|
||||
vst1.64 {d0}, [r0]!
|
||||
subs r3, #1
|
||||
bne .loop_fltint
|
||||
bx lr
|
||||
.size neon_convert_ps_si16_4n, .-neon_convert_ps_si16_4n
|
||||
.text
|
||||
.align 2
|
||||
.global neon_convert_si16_ps_4n
|
||||
.type neon_convert_si16_ps_4n, %function
|
||||
neon_convert_si16_ps_4n:
|
||||
.loop_intflt:
|
||||
vld1.64 {d0}, [r1]!
|
||||
vmovl.s16 q1, d0
|
||||
vcvt.f32.s32 q0, q1
|
||||
vst1.64 {q0}, [r0]!
|
||||
subs r2, #1
|
||||
bne .loop_intflt
|
||||
bx lr
|
||||
.size neon_convert_si16_ps_4n, .-neon_convert_si16_ps_4n
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
146
Transceiver52M/arm/convolve.c
Normal file
146
Transceiver52M/arm/convolve.c
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* NEON Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Forward declarations from base implementation */
|
||||
int _base_convolve_real(float *x, int x_len,
|
||||
float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int _base_convolve_complex(float *x, int x_len,
|
||||
float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int bounds_check(int x_len, int h_len, int y_len,
|
||||
int start, int len, int step);
|
||||
|
||||
#ifdef HAVE_NEON
|
||||
/* Calls into NEON assembler */
|
||||
void neon_conv_real4(float *x, float *h, float *y, int len);
|
||||
void neon_conv_real8(float *x, float *h, float *y, int len);
|
||||
void neon_conv_real12(float *x, float *h, float *y, int len);
|
||||
void neon_conv_real16(float *x, float *h, float *y, int len);
|
||||
void neon_conv_real20(float *x, float *h, float *y, int len);
|
||||
void mac_cx_neon4(float *x, float *h, float *y, int len);
|
||||
|
||||
/* Complex-complex convolution */
|
||||
static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
mac_cx_neon4(&x[2 * i], h, &y[2 * i], h_len >> 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* API: Initalize convolve module */
|
||||
void convolve_init(void)
|
||||
{
|
||||
/* Stub */
|
||||
return;
|
||||
}
|
||||
|
||||
/* API: Aligned complex-real */
|
||||
int convolve_real(float *x, int x_len,
|
||||
float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
void (*conv_func)(float *, float *, float *, int) = NULL;
|
||||
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
#ifdef HAVE_NEON
|
||||
if (step <= 4) {
|
||||
switch (h_len) {
|
||||
case 4:
|
||||
conv_func = neon_conv_real4;
|
||||
break;
|
||||
case 8:
|
||||
conv_func = neon_conv_real8;
|
||||
break;
|
||||
case 12:
|
||||
conv_func = neon_conv_real12;
|
||||
break;
|
||||
case 16:
|
||||
conv_func = neon_conv_real16;
|
||||
break;
|
||||
case 20:
|
||||
conv_func = neon_conv_real20;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (conv_func) {
|
||||
conv_func(&x[2 * (-(h_len - 1) + start)],
|
||||
h, y, len);
|
||||
} else {
|
||||
_base_convolve_real(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* API: Aligned complex-complex */
|
||||
int convolve_complex(float *x, int x_len,
|
||||
float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
void (*conv_func)(float *, float *, float *, int, int) = NULL;
|
||||
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
#ifdef HAVE_NEON
|
||||
if (step <= 4 && !(h_len % 4))
|
||||
conv_func = neon_conv_cmplx_4n;
|
||||
#endif
|
||||
if (conv_func) {
|
||||
conv_func(&x[2 * (-(h_len - 1) + start)],
|
||||
h, y, h_len, len);
|
||||
} else {
|
||||
_base_convolve_complex(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
277
Transceiver52M/arm/convolve_neon.S
Normal file
277
Transceiver52M/arm/convolve_neon.S
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* NEON Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.align 2
|
||||
.global neon_conv_real4
|
||||
.type neon_conv_real4, %function
|
||||
neon_conv_real4:
|
||||
push {r4, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]
|
||||
ldr r4, =8
|
||||
.neon_conv_loop4:
|
||||
vld2.32 {q2-q3}, [r0], r4
|
||||
vmul.f32 q4, q2, q0
|
||||
vmul.f32 q5, q3, q0
|
||||
vpadd.f32 d12, d8, d9
|
||||
vpadd.f32 d13, d10, d11
|
||||
vpadd.f32 d14, d12, d13
|
||||
vst1.64 {d14}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop4
|
||||
vpop {q4-q7}
|
||||
pop {r4, pc}
|
||||
.size neon_conv_real4, .-neon_conv_real4
|
||||
.align 2
|
||||
.p2align 4,,15
|
||||
.global neon_conv_real8
|
||||
.type neon_conv_real8, %function
|
||||
neon_conv_real8:
|
||||
push {r4-r5, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r1]
|
||||
add r4, r0, #32
|
||||
ldr r5, =8
|
||||
.neon_conv_loop8:
|
||||
vld2.32 {q4-q5}, [r0], r5
|
||||
vld2.32 {q6-q7}, [r4], r5
|
||||
vmul.f32 q8, q4, q0
|
||||
vmul.f32 q9, q5, q0
|
||||
vmul.f32 q10, q6, q2
|
||||
vmul.f32 q11, q7, q2
|
||||
|
||||
vadd.f32 q12, q8, q10
|
||||
vadd.f32 q13, q9, q11
|
||||
|
||||
vpadd.f32 d22, d24, d25
|
||||
vpadd.f32 d23, d26, d27
|
||||
vpadd.f32 d24, d22, d23
|
||||
vst1.64 {d24}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop8
|
||||
vpop {q4-q7}
|
||||
pop {r4-r5, pc}
|
||||
.size neon_conv_real8, .-neon_conv_real8
|
||||
.align 2
|
||||
.global neon_conv_real12
|
||||
.type neon_conv_real12, %function
|
||||
neon_conv_real12:
|
||||
push {r4-r6, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r1]!
|
||||
vld2.32 {q4-q5}, [r1]!
|
||||
add r4, r0, #32
|
||||
add r5, r0, #64
|
||||
ldr r6, =8
|
||||
.neon_conv_loop12:
|
||||
vld2.32 {q6-q7}, [r0], r6
|
||||
vld2.32 {q8-q9}, [r4], r6
|
||||
vld2.32 {q10-q11}, [r5], r6
|
||||
#ifdef HAVE_NEON_FMA
|
||||
vfma.f32 q1, q6, q0
|
||||
vfma.f32 q3, q7, q0
|
||||
vfma.f32 q1, q8, q2
|
||||
vfma.f32 q3, q9, q2
|
||||
vfma.f32 q1, q10, q4
|
||||
vfma.f32 q3, q11, q4
|
||||
#else
|
||||
vmul.f32 q12, q6, q0
|
||||
vmul.f32 q13, q7, q0
|
||||
vmul.f32 q14, q8, q2
|
||||
vmul.f32 q15, q9, q2
|
||||
vmul.f32 q1, q10, q4
|
||||
vmul.f32 q3, q11, q4
|
||||
|
||||
vadd.f32 q5, q12, q14
|
||||
vadd.f32 q6, q13, q15
|
||||
vadd.f32 q1, q5, q1
|
||||
vadd.f32 q3, q6, q3
|
||||
#endif
|
||||
vpadd.f32 d2, d2, d3
|
||||
vpadd.f32 d3, d6, d7
|
||||
vpadd.f32 d6, d2, d3
|
||||
vst1.64 {d6}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop12
|
||||
vpop {q4-q7}
|
||||
pop {r4-r6, pc}
|
||||
.size neon_conv_real12, .-neon_conv_real12
|
||||
.align 2
|
||||
.global neon_conv_real16
|
||||
.type neon_conv_real16, %function
|
||||
neon_conv_real16:
|
||||
push {r4-r7, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r1]!
|
||||
vld2.32 {q4-q5}, [r1]!
|
||||
vld2.32 {q6-q7}, [r1]
|
||||
add r4, r0, #32
|
||||
add r5, r0, #64
|
||||
add r6, r0, #96
|
||||
ldr r7, =8
|
||||
.neon_conv_loop16:
|
||||
vld2.32 {q8-q9}, [r0], r7
|
||||
vld2.32 {q10-q11}, [r4], r7
|
||||
vld2.32 {q12-q13}, [r5], r7
|
||||
vld2.32 {q14-q15}, [r6], r7
|
||||
#ifdef HAVE_NEON_FMA
|
||||
vmul.f32 q1, q8, q0
|
||||
vmul.f32 q3, q9, q0
|
||||
vfma.f32 q1, q10, q2
|
||||
vfma.f32 q3, q11, q2
|
||||
vfma.f32 q1, q12, q4
|
||||
vfma.f32 q3, q13, q4
|
||||
vfma.f32 q1, q14, q6
|
||||
vfma.f32 q3, q15, q6
|
||||
#else
|
||||
vmul.f32 q1, q8, q0
|
||||
vmul.f32 q3, q9, q0
|
||||
vmul.f32 q5, q10, q2
|
||||
vmul.f32 q7, q11, q2
|
||||
vmul.f32 q8, q12, q4
|
||||
vmul.f32 q9, q13, q4
|
||||
vmul.f32 q10, q14, q6
|
||||
vmul.f32 q11, q15, q6
|
||||
|
||||
vadd.f32 q1, q1, q5
|
||||
vadd.f32 q3, q3, q7
|
||||
vadd.f32 q5, q8, q10
|
||||
vadd.f32 q7, q9, q11
|
||||
vadd.f32 q1, q1, q5
|
||||
vadd.f32 q3, q3, q7
|
||||
#endif
|
||||
vpadd.f32 d2, d2, d3
|
||||
vpadd.f32 d3, d6, d7
|
||||
vpadd.f32 d6, d2, d3
|
||||
vst1.64 {d6}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop16
|
||||
vpop {q4-q7}
|
||||
pop {r4-r7, pc}
|
||||
.size neon_conv_real16, .-neon_conv_real16
|
||||
.align 2
|
||||
.global neon_conv_real20
|
||||
.type neon_conv_real20, %function
|
||||
neon_conv_real20:
|
||||
push {r4-r8, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r1]!
|
||||
vld2.32 {q4-q5}, [r1]!
|
||||
vld2.32 {q6-q7}, [r1]!
|
||||
vld2.32 {q8-q9}, [r1]
|
||||
add r4, r0, #32
|
||||
add r5, r0, #64
|
||||
add r6, r0, #96
|
||||
add r7, r0, #128
|
||||
ldr r8, =8
|
||||
.neon_conv_loop20:
|
||||
vld2.32 {q10-q11}, [r0], r8
|
||||
vld2.32 {q12-q13}, [r4], r8
|
||||
vld2.32 {q14-q15}, [r5], r8
|
||||
#ifdef HAVE_NEON_FMA
|
||||
vmul.f32 q1, q10, q0
|
||||
vfma.f32 q1, q12, q2
|
||||
vfma.f32 q1, q14, q4
|
||||
vmul.f32 q3, q11, q0
|
||||
vfma.f32 q3, q13, q2
|
||||
vfma.f32 q3, q15, q4
|
||||
|
||||
vld2.32 {q12-q13}, [r6], r8
|
||||
vld2.32 {q14-q15}, [r7], r8
|
||||
|
||||
vfma.f32 q1, q12, q6
|
||||
vfma.f32 q3, q13, q6
|
||||
vfma.f32 q1, q14, q8
|
||||
vfma.f32 q3, q15, q8
|
||||
#else
|
||||
vmul.f32 q1, q10, q0
|
||||
vmul.f32 q3, q12, q2
|
||||
vmul.f32 q5, q14, q4
|
||||
vmul.f32 q7, q11, q0
|
||||
vmul.f32 q9, q13, q2
|
||||
vmul.f32 q10, q15, q4
|
||||
vadd.f32 q1, q1, q3
|
||||
vadd.f32 q3, q7, q9
|
||||
vadd.f32 q9, q1, q5
|
||||
vadd.f32 q10, q3, q10
|
||||
|
||||
vld2.32 {q12-q13}, [r6], r8
|
||||
vld2.32 {q14-q15}, [r7], r8
|
||||
|
||||
vmul.f32 q1, q12, q6
|
||||
vmul.f32 q3, q13, q6
|
||||
vmul.f32 q5, q14, q8
|
||||
vmul.f32 q7, q15, q8
|
||||
vadd.f32 q12, q1, q9
|
||||
vadd.f32 q14, q3, q10
|
||||
vadd.f32 q1, q12, q5
|
||||
vadd.f32 q3, q14, q7
|
||||
#endif
|
||||
vpadd.f32 d2, d2, d3
|
||||
vpadd.f32 d3, d6, d7
|
||||
vpadd.f32 d6, d2, d3
|
||||
vst1.64 {d6}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop20
|
||||
vpop {q4-q7}
|
||||
pop {r4-r8, pc}
|
||||
.size neon_conv_real20, .-neon_conv_real20
|
||||
.align 2
|
||||
.global mac_cx_neon4
|
||||
.type mac_cx_neon4, %function
|
||||
mac_cx_neon4:
|
||||
push {r4, lr}
|
||||
ldr r4, =32
|
||||
veor q14, q14
|
||||
veor q15, q15
|
||||
.neon_conv_loop_mac4:
|
||||
vld2.32 {q0-q1}, [r0], r4
|
||||
vld2.32 {q2-q3}, [r1]!
|
||||
|
||||
vmul.f32 q10, q0, q2
|
||||
vmul.f32 q11, q1, q3
|
||||
vmul.f32 q12, q0, q3
|
||||
vmul.f32 q13, q2, q1
|
||||
vsub.f32 q8, q10, q11
|
||||
vadd.f32 q9, q12, q13
|
||||
|
||||
vadd.f32 q14, q8
|
||||
vadd.f32 q15, q9
|
||||
subs r3, #1
|
||||
bne .neon_conv_loop_mac4
|
||||
|
||||
vld1.64 d0, [r2]
|
||||
vpadd.f32 d28, d28, d29
|
||||
vpadd.f32 d30, d30, d31
|
||||
vpadd.f32 d1, d28, d30
|
||||
vadd.f32 d1, d0
|
||||
vst1.64 d1, [r2]
|
||||
pop {r4, pc}
|
||||
.size mac_cx_neon4, .-mac_cx_neon4
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
56
Transceiver52M/arm/mult.c
Normal file
56
Transceiver52M/arm/mult.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* NEON scaling
|
||||
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <mult.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
void neon_cmplx_mul_4n(float *, float *, float *, int);
|
||||
|
||||
static void cmplx_mul_ps(float *out, float *a, float *b, int len)
|
||||
{
|
||||
float ai, aq, bi, bq;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
ai = a[2 * i + 0];
|
||||
aq = a[2 * i + 1];
|
||||
|
||||
bi = b[2 * i + 0];
|
||||
bq = b[2 * i + 1];
|
||||
|
||||
out[2 * i + 0] = ai * bi - aq * bq;
|
||||
out[2 * i + 1] = ai * bq + aq * bi;
|
||||
}
|
||||
}
|
||||
|
||||
void mul_complex(float *out, float *a, float *b, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if (len % 4)
|
||||
cmplx_mul_ps(out, a, b, len);
|
||||
else
|
||||
neon_cmplx_mul_4n(out, a, b, len >> 2);
|
||||
#else
|
||||
cmplx_mul_ps(out, a, b, len);
|
||||
#endif
|
||||
}
|
||||
42
Transceiver52M/arm/mult_neon.S
Normal file
42
Transceiver52M/arm/mult_neon.S
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* NEON complex multiplication
|
||||
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.align 2
|
||||
.global neon_cmplx_mul_4n
|
||||
.type neon_cmplx_mul_4n, %function
|
||||
neon_cmplx_mul_4n:
|
||||
vpush {q4-q7}
|
||||
.loop_mul:
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r2]!
|
||||
vmul.f32 q4, q0, q2
|
||||
vmul.f32 q5, q1, q3
|
||||
vmul.f32 q6, q0, q3
|
||||
vmul.f32 q7, q2, q1
|
||||
vsub.f32 q8, q4, q5
|
||||
vadd.f32 q9, q6, q7
|
||||
vst2.32 {q8-q9}, [r0]!
|
||||
subs r3, #1
|
||||
bne .loop_mul
|
||||
vpop {q4-q7}
|
||||
bx lr
|
||||
.size neon_cmplx_mul_4n, .-neon_cmplx_mul_4n
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
56
Transceiver52M/arm/scale.c
Normal file
56
Transceiver52M/arm/scale.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* NEON scaling
|
||||
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <scale.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
void neon_scale_4n(float *, float *, float *, int);
|
||||
|
||||
static void scale_ps(float *out, float *in, float *scale, int len)
|
||||
{
|
||||
float ai, aq, bi, bq;
|
||||
|
||||
bi = scale[0];
|
||||
bq = scale[1];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
ai = in[2 * i + 0];
|
||||
aq = in[2 * i + 1];
|
||||
|
||||
out[2 * i + 0] = ai * bi - aq * bq;
|
||||
out[2 * i + 1] = ai * bq + aq * bi;
|
||||
}
|
||||
}
|
||||
|
||||
void scale_complex(float *out, float *in, float* scale, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if (len % 4)
|
||||
scale_ps(out, in, scale, len);
|
||||
else
|
||||
neon_scale_4n(in, scale, out, len >> 2);
|
||||
#else
|
||||
scale_ps(out, in, scale, len);
|
||||
#endif
|
||||
}
|
||||
50
Transceiver52M/arm/scale_neon.S
Normal file
50
Transceiver52M/arm/scale_neon.S
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* ARM NEON Scaling
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.align 2
|
||||
.global neon_scale_4n
|
||||
.type neon_scale_4n, %function
|
||||
neon_scale_4n:
|
||||
push {r4, lr}
|
||||
ldr r4, =32
|
||||
|
||||
vld1.64 d0, [r1]
|
||||
vmov.32 s4, s1
|
||||
vmov.32 s1, s0
|
||||
vmov.64 d1, d0
|
||||
vmov.32 s5, s4
|
||||
vmov.64 d3, d2
|
||||
.loop_mul_const:
|
||||
vld2.32 {q2-q3}, [r0], r4
|
||||
|
||||
vmul.f32 q8, q0, q2
|
||||
vmul.f32 q9, q1, q3
|
||||
vmul.f32 q10, q0, q3
|
||||
vmul.f32 q11, q1, q2
|
||||
vsub.f32 q8, q8, q9
|
||||
vadd.f32 q9, q10, q11
|
||||
|
||||
vst2.32 {q8-q9}, [r2]!
|
||||
subs r3, #1
|
||||
bne .loop_mul_const
|
||||
pop {r4, pc}
|
||||
.size neon_scale_4n, .-neon_scale_4n
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
15
Transceiver52M/common/convert.h
Normal file
15
Transceiver52M/common/convert.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _CONVERT_H_
|
||||
#define _CONVERT_H_
|
||||
|
||||
void convert_float_short(short *out, const float *in, float scale, int len);
|
||||
|
||||
void convert_short_float(float *out, const short *in, int len);
|
||||
|
||||
void base_convert_float_short(short *out, const float *in,
|
||||
float scale, int len);
|
||||
|
||||
void base_convert_short_float(float *out, const short *in, int len);
|
||||
|
||||
void convert_init(void);
|
||||
|
||||
#endif /* _CONVERT_H_ */
|
||||
34
Transceiver52M/common/convert_base.c
Normal file
34
Transceiver52M/common/convert_base.c
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Conversion
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "convert.h"
|
||||
|
||||
void base_convert_float_short(short *out, const float *in,
|
||||
float scale, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
out[i] = in[i] * scale;
|
||||
}
|
||||
|
||||
void base_convert_short_float(float *out, const short *in, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
out[i] = in[i];
|
||||
}
|
||||
|
||||
32
Transceiver52M/common/convolve.h
Normal file
32
Transceiver52M/common/convolve.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _CONVOLVE_H_
|
||||
#define _CONVOLVE_H_
|
||||
|
||||
void *convolve_h_alloc(int num);
|
||||
|
||||
int convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int base_convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int base_convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
void convolve_init(void);
|
||||
|
||||
#endif /* _CONVOLVE_H_ */
|
||||
156
Transceiver52M/common/convolve_base.c
Normal file
156
Transceiver52M/common/convolve_base.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Base multiply and accumulate complex-real */
|
||||
static void mac_real(const float *x, const float *h, float *y)
|
||||
{
|
||||
y[0] += x[0] * h[0];
|
||||
y[1] += x[1] * h[0];
|
||||
}
|
||||
|
||||
/* Base multiply and accumulate complex-complex */
|
||||
static void mac_cmplx(const float *x, const float *h, float *y)
|
||||
{
|
||||
y[0] += x[0] * h[0] - x[1] * h[1];
|
||||
y[1] += x[0] * h[1] + x[1] * h[0];
|
||||
}
|
||||
|
||||
/* Base vector complex-complex multiply and accumulate */
|
||||
static void mac_real_vec_n(const float *x, const float *h, float *y,
|
||||
int len, int step, int offset)
|
||||
{
|
||||
for (int i = offset; i < len; i += step)
|
||||
mac_real(&x[2 * i], &h[2 * i], y);
|
||||
}
|
||||
|
||||
/* Base vector complex-complex multiply and accumulate */
|
||||
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
|
||||
int len, int step, int offset)
|
||||
{
|
||||
for (int i = offset; i < len; i += step)
|
||||
mac_cmplx(&x[2 * i], &h[2 * i], y);
|
||||
}
|
||||
|
||||
/* Base complex-real convolution */
|
||||
int _base_convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
||||
h,
|
||||
&y[2 * i], h_len,
|
||||
step, offset);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Base complex-complex convolution */
|
||||
int _base_convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
||||
h,
|
||||
&y[2 * i],
|
||||
h_len, step, offset);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Buffer validity checks */
|
||||
int bounds_check(int x_len, int h_len, int y_len,
|
||||
int start, int len, int step)
|
||||
{
|
||||
if ((x_len < 1) || (h_len < 1) ||
|
||||
(y_len < 1) || (len < 1) || (step < 1)) {
|
||||
fprintf(stderr, "Convolve: Invalid input\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((start + len > x_len) || (len > y_len) || (x_len < h_len)) {
|
||||
fprintf(stderr, "Convolve: Boundary exception\n");
|
||||
fprintf(stderr, "start: %i, len: %i, x: %i, h: %i, y: %i\n",
|
||||
start, len, x_len, h_len, y_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API: Non-aligned (no SSE) complex-real */
|
||||
int base_convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
return _base_convolve_real(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
|
||||
/* API: Non-aligned (no SSE) complex-complex */
|
||||
int base_convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
return _base_convolve_complex(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
|
||||
/* Aligned filter tap allocation */
|
||||
void *convolve_h_alloc(int len)
|
||||
{
|
||||
#ifdef HAVE_SSE3
|
||||
return memalign(16, len * 2 * sizeof(float));
|
||||
#else
|
||||
return malloc(len * 2 * sizeof(float));
|
||||
#endif
|
||||
}
|
||||
112
Transceiver52M/common/fft.c
Normal file
112
Transceiver52M/common/fft.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Fast Fourier transform
|
||||
*
|
||||
* Copyright (C) 2012 Tom Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <fftw3.h>
|
||||
|
||||
#include "fft.h"
|
||||
|
||||
struct fft_hdl {
|
||||
float *fft_in;
|
||||
float *fft_out;
|
||||
int len;
|
||||
fftwf_plan fft_plan;
|
||||
};
|
||||
|
||||
/*! \brief Initialize FFT backend
|
||||
* \param[in] reverse FFT direction
|
||||
* \param[in] m FFT length
|
||||
* \param[in] istride input stride count
|
||||
* \param[in] ostride output stride count
|
||||
* \param[in] in input buffer (FFTW aligned)
|
||||
* \param[in] out output buffer (FFTW aligned)
|
||||
* \param[in] ooffset initial offset into output buffer
|
||||
*
|
||||
* If the reverse is non-NULL, then an inverse FFT will be used. This is a
|
||||
* wrapper for advanced non-contiguous FFTW usage. See FFTW documentation for
|
||||
* further details.
|
||||
*
|
||||
* http://www.fftw.org/doc/Advanced-Complex-DFTs.html
|
||||
*
|
||||
* It is currently unknown how the offset of the output buffer affects FFTW
|
||||
* memory alignment.
|
||||
*/
|
||||
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
|
||||
float *in, float *out, int ooffset)
|
||||
{
|
||||
int rank = 1;
|
||||
int n[] = { m };
|
||||
int howmany = istride;
|
||||
int idist = 1;
|
||||
int odist = 1;
|
||||
int *inembed = n;
|
||||
int *onembed = n;
|
||||
fftwf_complex *obuffer, *ibuffer;
|
||||
|
||||
struct fft_hdl *hdl = (struct fft_hdl *) malloc(sizeof(struct fft_hdl));
|
||||
if (!hdl)
|
||||
return NULL;
|
||||
|
||||
int direction = FFTW_FORWARD;
|
||||
if (reverse)
|
||||
direction = FFTW_BACKWARD;
|
||||
|
||||
ibuffer = (fftwf_complex *) in;
|
||||
obuffer = (fftwf_complex *) out + ooffset;
|
||||
|
||||
hdl->fft_in = in;
|
||||
hdl->fft_out = out;
|
||||
hdl->fft_plan = fftwf_plan_many_dft(rank, n, howmany,
|
||||
ibuffer, inembed, istride, idist,
|
||||
obuffer, onembed, ostride, odist,
|
||||
direction, FFTW_MEASURE);
|
||||
return hdl;
|
||||
}
|
||||
|
||||
void *fft_malloc(size_t size)
|
||||
{
|
||||
return fftwf_malloc(size);
|
||||
}
|
||||
|
||||
void fft_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/*! \brief Free FFT backend resources
|
||||
*/
|
||||
void free_fft(struct fft_hdl *hdl)
|
||||
{
|
||||
fftwf_destroy_plan(hdl->fft_plan);
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
/*! \brief Run multiple DFT operations with the initialized plan
|
||||
* \param[in] hdl handle to an intitialized fft struct
|
||||
*
|
||||
* Input and output buffers are configured with init_fft().
|
||||
*/
|
||||
int cxvec_fft(struct fft_hdl *hdl)
|
||||
{
|
||||
fftwf_execute(hdl->fft_plan);
|
||||
return 0;
|
||||
}
|
||||
13
Transceiver52M/common/fft.h
Normal file
13
Transceiver52M/common/fft.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef _FFT_H_
|
||||
#define _FFT_H_
|
||||
|
||||
struct fft_hdl;
|
||||
|
||||
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
|
||||
float *in, float *out, int ooffset);
|
||||
void *fft_malloc(size_t size);
|
||||
void fft_free(void *ptr);
|
||||
void free_fft(struct fft_hdl *hdl);
|
||||
int cxvec_fft(struct fft_hdl *hdl);
|
||||
|
||||
#endif /* _FFT_H_ */
|
||||
6
Transceiver52M/common/mult.h
Normal file
6
Transceiver52M/common/mult.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _MULT_H_
|
||||
#define _MULT_H_
|
||||
|
||||
void mul_complex(float *out, float *a, float *b, int len);
|
||||
|
||||
#endif /* _MULT_H_ */
|
||||
6
Transceiver52M/common/scale.h
Normal file
6
Transceiver52M/common/scale.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _SCALE_H_
|
||||
#define _SCALE_H_
|
||||
|
||||
void scale_complex(float *out, float *in, float *scale, int len);
|
||||
|
||||
#endif /* _SCALE_H_ */
|
||||
83
Transceiver52M/laurent.m
Normal file
83
Transceiver52M/laurent.m
Normal file
@@ -0,0 +1,83 @@
|
||||
%
|
||||
% Laurent decomposition of GMSK signals
|
||||
% Generates C0, C1, and C2 pulse shapes
|
||||
%
|
||||
% Pierre Laurent, "Exact and Approximate Construction of Digital Phase
|
||||
% Modulations by Superposition of Amplitude Modulated Pulses", IEEE
|
||||
% Transactions of Communications, Vol. 34, No. 2, Feb 1986.
|
||||
%
|
||||
% Author: Thomas Tsou <tom@tsou.cc>
|
||||
%
|
||||
|
||||
% Modulation parameters
|
||||
oversamp = 16;
|
||||
L = 3;
|
||||
f = 270.83333e3;
|
||||
T = 1/f;
|
||||
h = 0.5;
|
||||
BT = 0.30;
|
||||
B = BT / T;
|
||||
|
||||
% Generate sampling points for L symbol periods
|
||||
t = -(L*T/2):T/oversamp:(L*T/2);
|
||||
t = t(1:end-1) + (T/oversamp/2);
|
||||
|
||||
% Generate Gaussian pulse
|
||||
g = qfunc(2*pi*B*(t - T/2)/(log(2)^.5)) - qfunc(2*pi*B*(t + T/2)/(log(2)^.5));
|
||||
g = g / sum(g) * pi/2;
|
||||
g = [0 g];
|
||||
|
||||
% Integrate phase
|
||||
q = 0;
|
||||
for i = 1:size(g,2);
|
||||
q(i) = sum(g(1:i));
|
||||
end
|
||||
|
||||
% Compute two sided "generalized phase pulse" function
|
||||
s = 0;
|
||||
for i = 1:size(g,2);
|
||||
s(i) = sin(q(i)) / sin(pi*h);
|
||||
end
|
||||
for i = (size(g,2) + 1):(2 * size(g,2) - 1);
|
||||
s(i) = sin(pi*h - q(i - (size(g,2) - 1))) / sin(pi*h);
|
||||
end
|
||||
|
||||
% Compute C0 pulse: valid for all L values
|
||||
c0 = s(1:end-(oversamp*(L-1)));
|
||||
for i = 1:L-1;
|
||||
c0 = c0 .* s((1 + i*oversamp):end-(oversamp*(L - 1 - i)));
|
||||
end
|
||||
|
||||
% Compute C1 pulse: valid for L = 3 only!
|
||||
% C1 = S0 * S4 * S2
|
||||
c1 = s(1:end-(oversamp*(4)));
|
||||
c1 = c1 .* s((1 + 4*oversamp):end-(oversamp*(4 - 1 - 3)));
|
||||
c1 = c1 .* s((1 + 2*oversamp):end-(oversamp*(4 - 1 - 1)));
|
||||
|
||||
% Compute C2 pulse: valid for L = 3 only!
|
||||
% C2 = S0 * S1 * S5
|
||||
c2 = s(1:end-(oversamp*(5)));
|
||||
c2 = c2 .* s((1 + 1*oversamp):end-(oversamp*(5 - 1 - 0)));
|
||||
c2 = c2 .* s((1 + 5*oversamp):end-(oversamp*(5 - 1 - 4)));
|
||||
|
||||
% Plot C0, C1, C2 Laurent pulse series
|
||||
figure(1);
|
||||
hold off;
|
||||
plot((0:size(c0,2)-1)/oversamp - 2,c0, 'b');
|
||||
hold on;
|
||||
plot((0:size(c1,2)-1)/oversamp - 2,c1, 'r');
|
||||
plot((0:size(c2,2)-1)/oversamp - 2,c2, 'g');
|
||||
|
||||
% Generate OpenBTS pulse
|
||||
numSamples = size(c0,2);
|
||||
centerPoint = (numSamples - 1)/2;
|
||||
i = ((0:numSamples) - centerPoint) / oversamp;
|
||||
xP = .96*exp(-1.1380*i.^2 - 0.527*i.^4);
|
||||
xP = xP / max(xP) * max(c0);
|
||||
|
||||
% Plot C0 pulse compared to OpenBTS pulse
|
||||
figure(2);
|
||||
hold off;
|
||||
plot((0:size(c0,2)-1)/oversamp, c0, 'b');
|
||||
hold on;
|
||||
plot((0:size(xP,2)-1)/oversamp, xP, 'r');
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Thomas Tsou <ttsou@vt.edu>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <GSMCommon.h>
|
||||
#include <Logger.h>
|
||||
#include <Configuration.h>
|
||||
|
||||
#include "Transceiver.h"
|
||||
#include "radioDevice.h"
|
||||
|
||||
ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
|
||||
|
||||
volatile bool gbShutdown = false;
|
||||
|
||||
int Transceiver::mTSC = -1;
|
||||
|
||||
static void sigHandler(int signum)
|
||||
{
|
||||
LOG(NOTICE) << "Received shutdown signal";
|
||||
gbShutdown = true;
|
||||
}
|
||||
|
||||
static int setupSignals()
|
||||
{
|
||||
struct sigaction action;
|
||||
|
||||
action.sa_handler = sigHandler;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
|
||||
if (sigaction(SIGINT, &action, NULL) < 0)
|
||||
return -1;
|
||||
if (sigaction(SIGTERM, &action, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int numARFCN = 1;
|
||||
|
||||
if (argc > 1) {
|
||||
numARFCN = atoi(argv[1]);
|
||||
if (numARFCN > CHAN_MAX) {
|
||||
LOG(ALERT) << numARFCN << " channels not supported "
|
||||
<< " with with current build";
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
gLogInit("transceiver", gConfig.getStr("Log.Level").c_str(), LOG_LOCAL7);
|
||||
srandom(time(NULL));
|
||||
|
||||
if (setupSignals() < 0) {
|
||||
LOG(ERR) << "Failed to setup signal handlers, exiting...";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
RadioDevice *device = RadioDevice::make(SAMPSPERSYM);
|
||||
int radioType = device->open("");
|
||||
if (radioType < 0) {
|
||||
LOG(ALERT) << "Failed to open device, exiting...";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
RadioInterface *radio;
|
||||
switch (radioType) {
|
||||
case RadioDevice::NORMAL:
|
||||
radio = new RadioInterface(device, numARFCN);
|
||||
break;
|
||||
case RadioDevice::RESAMP:
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported configuration";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
DriveLoop *drive = new DriveLoop(5700, "127.0.0.1", radio, numARFCN, 0);
|
||||
|
||||
Transceiver *trx[CHAN_MAX];
|
||||
bool primary = true;
|
||||
for (int i = 0; i < numARFCN; i++) {
|
||||
trx[i] = new Transceiver(5700 + 2 * i, "127.0.0.1",
|
||||
drive, radio, SAMPSPERSYM,
|
||||
i, primary);
|
||||
trx[i]->start();
|
||||
primary = false;
|
||||
}
|
||||
|
||||
while (!gbShutdown)
|
||||
sleep(1);
|
||||
|
||||
LOG(NOTICE) << "Shutting down transceivers...";
|
||||
for (int i = 0; i < numARFCN; i++)
|
||||
trx[i]->shutdown();
|
||||
|
||||
for (int i = 0; i < numARFCN; i++)
|
||||
delete trx[i];
|
||||
delete drive;
|
||||
delete radio;
|
||||
delete device;
|
||||
}
|
||||
520
Transceiver52M/osmo-trx-dec.cpp
Normal file
520
Transceiver52M/osmo-trx-dec.cpp
Normal file
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
334
Transceiver52M/osmo-trx-gen.cpp
Normal file
334
Transceiver52M/osmo-trx-gen.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
482
Transceiver52M/osmo-trx.cpp
Normal file
482
Transceiver52M/osmo-trx.cpp
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* 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 "Transceiver.h"
|
||||
#include "radioDevice.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <GSMCommon.h>
|
||||
#include <Logger.h>
|
||||
#include <Configuration.h>
|
||||
|
||||
extern "C" {
|
||||
#include "convolve.h"
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
/* Samples-per-symbol for downlink path
|
||||
* 4 - Uses precision modulator (more computation, less distortion)
|
||||
* 1 - Uses minimized modulator (less computation, more distortion)
|
||||
*
|
||||
* Other values are invalid. Receive path (uplink) is always
|
||||
* downsampled to 1 sps. Default to 4 sps for all cases.
|
||||
*/
|
||||
#define DEFAULT_TX_SPS 4
|
||||
|
||||
/*
|
||||
* Samples-per-symbol for uplink (receiver) path
|
||||
* Do not modify this value. EDGE configures 4 sps automatically on
|
||||
* B200/B210 devices only. Use of 4 sps on the receive path for other
|
||||
* configurations is not supported.
|
||||
*/
|
||||
#define DEFAULT_RX_SPS 1
|
||||
|
||||
/* Default configuration parameters */
|
||||
#define DEFAULT_TRX_PORT 5700
|
||||
#define DEFAULT_TRX_IP "127.0.0.1"
|
||||
#define DEFAULT_CHANS 1
|
||||
|
||||
struct trx_config {
|
||||
std::string log_level;
|
||||
std::string addr;
|
||||
std::string dev_args;
|
||||
unsigned port;
|
||||
unsigned tx_sps;
|
||||
unsigned rx_sps;
|
||||
unsigned chans;
|
||||
unsigned rtsc;
|
||||
unsigned rach_delay;
|
||||
bool extref;
|
||||
bool gpsref;
|
||||
Transceiver::FillerType filler;
|
||||
bool mcbts;
|
||||
double offset;
|
||||
double rssi_offset;
|
||||
bool swap_channels;
|
||||
bool edge;
|
||||
};
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
volatile bool gshutdown = false;
|
||||
|
||||
/* Setup configuration values
|
||||
* Don't query the existence of the Log.Level because it's a
|
||||
* mandatory value. That is, if it doesn't exist, the configuration
|
||||
* table will crash or will have already crashed. Everything else we
|
||||
* can survive without and use default values if the database entries
|
||||
* are empty.
|
||||
*/
|
||||
bool trx_setup_config(struct trx_config *config)
|
||||
{
|
||||
std::string refstr, fillstr, divstr, mcstr, edgestr;
|
||||
|
||||
if (config->mcbts && config->chans > 5) {
|
||||
std::cout << "Unsupported number of channels" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
edgestr = config->edge ? "Enabled" : "Disabled";
|
||||
mcstr = config->mcbts ? "Enabled" : "Disabled";
|
||||
|
||||
if (config->extref)
|
||||
refstr = "External";
|
||||
else if (config->gpsref)
|
||||
refstr = "GPS";
|
||||
else
|
||||
refstr = "Internal";
|
||||
|
||||
switch (config->filler) {
|
||||
case Transceiver::FILLER_DUMMY:
|
||||
fillstr = "Dummy bursts";
|
||||
break;
|
||||
case Transceiver::FILLER_ZERO:
|
||||
fillstr = "Disabled";
|
||||
break;
|
||||
case Transceiver::FILLER_NORM_RAND:
|
||||
fillstr = "Normal busrts with random payload";
|
||||
break;
|
||||
case Transceiver::FILLER_EDGE_RAND:
|
||||
fillstr = "EDGE busrts with random payload";
|
||||
break;
|
||||
case Transceiver::FILLER_ACCESS_RAND:
|
||||
fillstr = "Access busrts with random payload";
|
||||
break;
|
||||
}
|
||||
|
||||
std::ostringstream ost("");
|
||||
ost << "Config Settings" << std::endl;
|
||||
ost << " Log Level............... " << config->log_level << std::endl;
|
||||
ost << " Device args............. " << config->dev_args << std::endl;
|
||||
ost << " TRX Base Port........... " << config->port << std::endl;
|
||||
ost << " TRX Address............. " << config->addr << std::endl;
|
||||
ost << " Channels................ " << config->chans << std::endl;
|
||||
ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
|
||||
ost << " Rx Samples-per-Symbol... " << config->rx_sps << std::endl;
|
||||
ost << " EDGE support............ " << edgestr << std::endl;
|
||||
ost << " Reference............... " << refstr << std::endl;
|
||||
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
||||
ost << " Multi-Carrier........... " << mcstr << std::endl;
|
||||
ost << " Tuning offset........... " << config->offset << std::endl;
|
||||
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
|
||||
ost << " Swap channels........... " << config->swap_channels << std::endl;
|
||||
std::cout << ost << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Create radio interface
|
||||
* The interface consists of sample rate changes, frequency shifts,
|
||||
* channel multiplexing, and other conversions. The transceiver core
|
||||
* accepts input vectors sampled at multiples of the GSM symbol rate.
|
||||
* The radio interface connects the main transceiver with the device
|
||||
* object, which may be operating some other rate.
|
||||
*/
|
||||
RadioInterface *makeRadioInterface(struct trx_config *config,
|
||||
RadioDevice *usrp, int type)
|
||||
{
|
||||
RadioInterface *radio = NULL;
|
||||
|
||||
switch (type) {
|
||||
case RadioDevice::NORMAL:
|
||||
radio = new RadioInterface(usrp, config->tx_sps,
|
||||
config->rx_sps, config->chans);
|
||||
break;
|
||||
case RadioDevice::RESAMP_64M:
|
||||
case RadioDevice::RESAMP_100M:
|
||||
radio = new RadioInterfaceResamp(usrp, config->tx_sps,
|
||||
config->rx_sps);
|
||||
break;
|
||||
case RadioDevice::MULTI_ARFCN:
|
||||
radio = new RadioInterfaceMulti(usrp, config->tx_sps,
|
||||
config->rx_sps, config->chans);
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported radio interface configuration";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!radio->init(type)) {
|
||||
LOG(ALERT) << "Failed to initialize radio interface";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return radio;
|
||||
}
|
||||
|
||||
/* Create transceiver core
|
||||
* The multi-threaded modem core operates at multiples of the GSM rate of
|
||||
* 270.8333 ksps and consists of GSM specific modulation, demodulation,
|
||||
* and decoding schemes. Also included are the socket interfaces for
|
||||
* connecting to the upper layer stack.
|
||||
*/
|
||||
Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
|
||||
{
|
||||
Transceiver *trx;
|
||||
VectorFIFO *fifo;
|
||||
|
||||
trx = new Transceiver(config->port, config->addr.c_str(),
|
||||
config->tx_sps, config->rx_sps, config->chans,
|
||||
GSM::Time(3,0), radio, config->rssi_offset);
|
||||
if (!trx->init(config->filler, config->rtsc,
|
||||
config->rach_delay, config->edge)) {
|
||||
LOG(ALERT) << "Failed to initialize transceiver";
|
||||
delete trx;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < config->chans; i++) {
|
||||
fifo = radio->receiveFIFO(i);
|
||||
if (fifo && trx->receiveFIFO(fifo, i))
|
||||
continue;
|
||||
|
||||
LOG(ALERT) << "Could not attach FIFO to channel " << i;
|
||||
delete trx;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return trx;
|
||||
}
|
||||
|
||||
static void sig_handler(int signo)
|
||||
{
|
||||
fprintf(stdout, "Received shutdown signal");
|
||||
gshutdown = true;
|
||||
}
|
||||
|
||||
static void setup_signal_handlers()
|
||||
{
|
||||
if (signal(SIGINT, sig_handler) == SIG_ERR) {
|
||||
fprintf(stderr, "Failed to install SIGINT signal handler\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (signal(SIGTERM, sig_handler) == SIG_ERR) {
|
||||
fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
|
||||
exit( EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
fprintf(stdout, "Options:\n"
|
||||
" -h This text\n"
|
||||
" -a UHD device args\n"
|
||||
" -l Logging level (%s)\n"
|
||||
" -i IP address of GSM core\n"
|
||||
" -p Base port number\n"
|
||||
" -e Enable EDGE receiver\n"
|
||||
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
|
||||
" -x Enable external 10 MHz reference\n"
|
||||
" -g Enable GPSDO reference\n"
|
||||
" -s Tx samples-per-symbol (1 or 4)\n"
|
||||
" -b Rx samples-per-symbol (1 or 4)\n"
|
||||
" -c Number of ARFCN channels (default=1)\n"
|
||||
" -f Enable C0 filler table\n"
|
||||
" -o Set baseband frequency offset (default=auto)\n"
|
||||
" -r Random GMSK Normal Burst test mode with given TSC\n"
|
||||
" -E Random 8-PSK Normal Burst test mode with given TSC\n"
|
||||
" -A Random Access Burst test mode with delay\n"
|
||||
" -R RSSI to dBm offset in dB (default=0)\n"
|
||||
" -S Swap channels (UmTRX only)\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
{
|
||||
int option;
|
||||
|
||||
config->log_level = "NOTICE";
|
||||
config->addr = DEFAULT_TRX_IP;
|
||||
config->port = DEFAULT_TRX_PORT;
|
||||
config->tx_sps = DEFAULT_TX_SPS;
|
||||
config->rx_sps = DEFAULT_RX_SPS;
|
||||
config->chans = DEFAULT_CHANS;
|
||||
config->rtsc = 0;
|
||||
config->rach_delay = 0;
|
||||
config->extref = false;
|
||||
config->gpsref = false;
|
||||
config->filler = Transceiver::FILLER_ZERO;
|
||||
config->mcbts = false;
|
||||
config->offset = 0.0;
|
||||
config->rssi_offset = 0.0;
|
||||
config->swap_channels = false;
|
||||
config->edge = false;
|
||||
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dmxgfo:s:b:r:E:A:R:Se")) != -1) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'a':
|
||||
config->dev_args = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
config->log_level = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
config->addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
config->port = atoi(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
config->chans = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
config->mcbts = true;
|
||||
break;
|
||||
case 'x':
|
||||
config->extref = true;
|
||||
break;
|
||||
case 'g':
|
||||
config->gpsref = true;
|
||||
break;
|
||||
case 'f':
|
||||
config->filler = Transceiver::FILLER_DUMMY;
|
||||
break;
|
||||
case 'o':
|
||||
config->offset = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
config->tx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
config->rx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
config->rtsc = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_NORM_RAND;
|
||||
break;
|
||||
case 'E':
|
||||
config->rtsc = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_EDGE_RAND;
|
||||
break;
|
||||
case 'A':
|
||||
config->rach_delay = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_ACCESS_RAND;
|
||||
break;
|
||||
case 'R':
|
||||
config->rssi_offset = atof(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
config->swap_channels = true;
|
||||
break;
|
||||
case 'e':
|
||||
config->edge = true;
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
|
||||
if ((config->edge) || (config->mcbts)) {
|
||||
config->tx_sps = 4;
|
||||
config->rx_sps = 4;
|
||||
}
|
||||
|
||||
if (config->gpsref && config->extref) {
|
||||
printf("External and GPSDO references unavailable at the same time\n\n");
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if (!config->edge && (config->filler == Transceiver::FILLER_EDGE_RAND)) {
|
||||
printf("Can't enable EDGE filler when EDGE mode is disabled\n\n");
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if ((config->tx_sps != 1) && (config->tx_sps != 4) &&
|
||||
(config->rx_sps != 1) && (config->rx_sps != 4)) {
|
||||
printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if (config->rtsc > 7) {
|
||||
printf("Invalid training sequence %i\n\n", config->rtsc);
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if (config->rach_delay > 68) {
|
||||
printf("RACH delay is too big %i\n\n", config->rach_delay);
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
bad_config:
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int type, chans, ref;
|
||||
RadioDevice *usrp;
|
||||
RadioInterface *radio = NULL;
|
||||
Transceiver *trx = NULL;
|
||||
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
|
||||
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();
|
||||
|
||||
handle_options(argc, argv, &config);
|
||||
|
||||
setup_signal_handlers();
|
||||
|
||||
/* Check database sanity */
|
||||
if (!trx_setup_config(&config)) {
|
||||
std::cerr << "Config: Database failure - exiting" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
|
||||
|
||||
srandom(time(NULL));
|
||||
|
||||
/* Create the low level device object */
|
||||
if (config.mcbts)
|
||||
iface = RadioDevice::MULTI_ARFCN;
|
||||
|
||||
if (config.extref)
|
||||
ref = RadioDevice::REF_EXTERNAL;
|
||||
else if (config.gpsref)
|
||||
ref = RadioDevice::REF_GPS;
|
||||
else
|
||||
ref = RadioDevice::REF_INTERNAL;
|
||||
|
||||
usrp = RadioDevice::make(config.tx_sps, config.rx_sps, iface,
|
||||
config.chans, config.offset);
|
||||
type = usrp->open(config.dev_args, ref, config.swap_channels);
|
||||
if (type < 0) {
|
||||
LOG(ALERT) << "Failed to create radio device" << std::endl;
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Setup the appropriate device interface */
|
||||
radio = makeRadioInterface(&config, usrp, type);
|
||||
if (!radio)
|
||||
goto shutdown;
|
||||
|
||||
/* Create the transceiver core */
|
||||
trx = makeTransceiver(&config, radio);
|
||||
if (!trx)
|
||||
goto shutdown;
|
||||
|
||||
chans = trx->numChans();
|
||||
std::cout << "-- Transceiver active with "
|
||||
<< chans << " channel(s)" << std::endl;
|
||||
|
||||
while (!gshutdown)
|
||||
sleep(1);
|
||||
|
||||
shutdown:
|
||||
std::cout << "Shutting down transceiver..." << std::endl;
|
||||
|
||||
delete trx;
|
||||
delete radio;
|
||||
delete usrp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
228
Transceiver52M/radioBuffer.cpp
Normal file
228
Transceiver52M/radioBuffer.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Segmented Ring Buffer
|
||||
*
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* Author: Tom Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include "radioBuffer.h"
|
||||
|
||||
RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
|
||||
size_t hLen, bool outDirection)
|
||||
: writeIndex(0), readIndex(0), availSamples(0)
|
||||
{
|
||||
if (!outDirection)
|
||||
hLen = 0;
|
||||
|
||||
buffer = new float[2 * (hLen + numSegments * segmentLen)];
|
||||
bufferLen = numSegments * segmentLen;
|
||||
|
||||
segments.resize(numSegments);
|
||||
|
||||
for (size_t i = 0; i < numSegments; i++)
|
||||
segments[i] = &buffer[2 * (hLen + i * segmentLen)];
|
||||
|
||||
this->outDirection = outDirection;
|
||||
this->numSegments = numSegments;
|
||||
this->segmentLen = segmentLen;
|
||||
this->hLen = hLen;
|
||||
}
|
||||
|
||||
RadioBuffer::~RadioBuffer()
|
||||
{
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void RadioBuffer::reset()
|
||||
{
|
||||
writeIndex = 0;
|
||||
readIndex = 0;
|
||||
availSamples = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Output direction
|
||||
*
|
||||
* Return a pointer to the oldest segment or NULL if a complete segment is not
|
||||
* available.
|
||||
*/
|
||||
const float *RadioBuffer::getReadSegment()
|
||||
{
|
||||
if (!outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
if (availSamples < segmentLen) {
|
||||
std::cout << "Not enough samples " << std::endl;
|
||||
std::cout << availSamples << " available per segment "
|
||||
<< segmentLen << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t num = readIndex / segmentLen;
|
||||
|
||||
if (num >= numSegments) {
|
||||
std::cout << "Invalid segment" << std::endl;
|
||||
return NULL;
|
||||
} else if (!num) {
|
||||
memcpy(buffer,
|
||||
&buffer[2 * bufferLen],
|
||||
hLen * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
availSamples -= segmentLen;
|
||||
readIndex = (readIndex + segmentLen) % bufferLen;
|
||||
|
||||
return segments[num];
|
||||
}
|
||||
|
||||
/*
|
||||
* Output direction
|
||||
*
|
||||
* Write a non-segment length of samples to the buffer.
|
||||
*/
|
||||
bool RadioBuffer::write(const float *wr, size_t len)
|
||||
{
|
||||
if (!outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (availSamples + len > bufferLen) {
|
||||
std::cout << "Insufficient space" << std::endl;
|
||||
std::cout << bufferLen - availSamples << " available per write "
|
||||
<< len << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeIndex + len <= bufferLen) {
|
||||
memcpy(&buffer[2 * (writeIndex + hLen)],
|
||||
wr, len * 2 * sizeof(float));
|
||||
} else {
|
||||
size_t len0 = bufferLen - writeIndex;
|
||||
size_t len1 = len - len0;
|
||||
memcpy(&buffer[2 * (writeIndex + hLen)], wr, len0 * 2 * sizeof(float));
|
||||
memcpy(&buffer[2 * hLen], &wr[2 * len0], len1 * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
availSamples += len;
|
||||
writeIndex = (writeIndex + len) % bufferLen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioBuffer::zero(size_t len)
|
||||
{
|
||||
if (!outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (availSamples + len > bufferLen) {
|
||||
std::cout << "Insufficient space" << std::endl;
|
||||
std::cout << bufferLen - availSamples << " available per zero "
|
||||
<< len << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeIndex + len <= bufferLen) {
|
||||
memset(&buffer[2 * (writeIndex + hLen)],
|
||||
0, len * 2 * sizeof(float));
|
||||
} else {
|
||||
size_t len0 = bufferLen - writeIndex;
|
||||
size_t len1 = len - len0;
|
||||
memset(&buffer[2 * (writeIndex + hLen)], 0, len0 * 2 * sizeof(float));
|
||||
memset(&buffer[2 * hLen], 0, len1 * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
availSamples += len;
|
||||
writeIndex = (writeIndex + len) % bufferLen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Input direction
|
||||
*/
|
||||
float *RadioBuffer::getWriteSegment()
|
||||
{
|
||||
if (outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
if (bufferLen - availSamples < segmentLen) {
|
||||
std::cout << "Insufficient samples" << std::endl;
|
||||
std::cout << bufferLen - availSamples
|
||||
<< " available for segment " << segmentLen
|
||||
<< std::endl;
|
||||
return NULL;
|
||||
}
|
||||
if (writeIndex % segmentLen) {
|
||||
std::cout << "Internal segment error" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t num = writeIndex / segmentLen;
|
||||
|
||||
if (num >= numSegments)
|
||||
return NULL;
|
||||
|
||||
availSamples += segmentLen;
|
||||
writeIndex = (writeIndex + segmentLen) % bufferLen;
|
||||
|
||||
return segments[num];
|
||||
}
|
||||
|
||||
bool RadioBuffer::zeroWriteSegment()
|
||||
{
|
||||
float *segment = getWriteSegment();
|
||||
if (!segment)
|
||||
return false;
|
||||
|
||||
memset(segment, 0, segmentLen * 2 * sizeof(float));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioBuffer::read(float *rd, size_t len)
|
||||
{
|
||||
if (outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (availSamples < len) {
|
||||
std::cout << "Insufficient samples" << std::endl;
|
||||
std::cout << availSamples << " available for "
|
||||
<< len << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readIndex + len <= bufferLen) {
|
||||
memcpy(rd, &buffer[2 * readIndex], len * 2 * sizeof(float));
|
||||
} else {
|
||||
size_t len0 = bufferLen - readIndex;
|
||||
size_t len1 = len - len0;
|
||||
memcpy(rd, &buffer[2 * readIndex], len0 * 2 * sizeof(float));
|
||||
memcpy(&rd[2 * len0], buffer, len1 * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
availSamples -= len;
|
||||
readIndex = (readIndex + len) % bufferLen;
|
||||
|
||||
return true;
|
||||
}
|
||||
45
Transceiver52M/radioBuffer.h
Normal file
45
Transceiver52M/radioBuffer.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
class RadioBuffer {
|
||||
public:
|
||||
RadioBuffer(size_t numSegments, size_t segmentLen,
|
||||
size_t hLen, bool outDirection);
|
||||
|
||||
~RadioBuffer();
|
||||
|
||||
const size_t getSegmentLen() { return segmentLen; }
|
||||
const size_t getNumSegments() { return numSegments; }
|
||||
const size_t getAvailSamples() { return availSamples; }
|
||||
const size_t getAvailSegments() { return availSamples / segmentLen; }
|
||||
|
||||
const size_t getFreeSamples()
|
||||
{
|
||||
return bufferLen - availSamples;
|
||||
}
|
||||
|
||||
const size_t getFreeSegments()
|
||||
{
|
||||
return getFreeSamples() / segmentLen;
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
/* Output direction */
|
||||
const float *getReadSegment();
|
||||
bool write(const float *wr, size_t len);
|
||||
bool zero(size_t len);
|
||||
|
||||
/* Input direction */
|
||||
float *getWriteSegment();
|
||||
bool zeroWriteSegment();
|
||||
bool read(float *rd, size_t len);
|
||||
|
||||
private:
|
||||
size_t writeIndex, readIndex, availSamples;
|
||||
size_t bufferLen, numSegments, segmentLen, hLen;
|
||||
float *buffer;
|
||||
std::vector<float *> segments;
|
||||
bool outDirection;
|
||||
};
|
||||
@@ -23,32 +23,27 @@
|
||||
|
||||
void RadioClock::set(const GSM::Time& wTime)
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
mClock = wTime;
|
||||
updateSignal.signal();
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
void RadioClock::incTN()
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
mClock.incTN();
|
||||
updateSignal.signal();
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
GSM::Time RadioClock::get()
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
GSM::Time retVal = mClock;
|
||||
mLock.unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RadioClock::wait()
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
updateSignal.wait(mLock,1);
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
#define __RADIO_DEVICE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#define GSMRATE 1625e3/6
|
||||
#define GSMRATE (1625e3/6)
|
||||
#define MCBTS_SPACING 800000.0
|
||||
|
||||
/** a 64-bit virtual timestamp for radio data */
|
||||
typedef unsigned long long TIMESTAMP;
|
||||
@@ -34,14 +36,26 @@ class RadioDevice {
|
||||
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
|
||||
|
||||
/* Radio interface types */
|
||||
enum RadioInterfaceType { NORMAL, RESAMP };
|
||||
enum InterfaceType {
|
||||
NORMAL,
|
||||
RESAMP_64M,
|
||||
RESAMP_100M,
|
||||
MULTI_ARFCN,
|
||||
};
|
||||
|
||||
static RadioDevice *make(int sps, bool skipRx = false);
|
||||
enum ReferenceType {
|
||||
REF_INTERNAL,
|
||||
REF_EXTERNAL,
|
||||
REF_GPS,
|
||||
};
|
||||
|
||||
virtual ~RadioDevice() {};
|
||||
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||
size_t chans = 1, double offset = 0.0);
|
||||
|
||||
/** Initialize the USRP */
|
||||
virtual int open(const std::string &args)=0;
|
||||
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
|
||||
|
||||
virtual ~RadioDevice() { }
|
||||
|
||||
/** Start the USRP */
|
||||
virtual bool start()=0;
|
||||
@@ -53,7 +67,7 @@ class RadioDevice {
|
||||
virtual enum TxWindowType getWindowType()=0;
|
||||
|
||||
/** Enable thread priority */
|
||||
virtual void setPriority()=0;
|
||||
virtual void setPriority(float prio = 0.5) = 0;
|
||||
|
||||
/**
|
||||
Read samples from the radio.
|
||||
@@ -65,9 +79,9 @@ class RadioDevice {
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
virtual int readSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
|
||||
bool *overrun = NULL, bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL)=0;
|
||||
virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
|
||||
unsigned *RSSI = 0) = 0;
|
||||
/**
|
||||
Write samples to the radio.
|
||||
@param buf Contains the data to be written.
|
||||
@@ -77,17 +91,17 @@ class RadioDevice {
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
virtual int writeSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
|
||||
bool *underrun = NULL, bool isControl = false)=0;
|
||||
|
||||
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp, bool isControl = false) = 0;
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
virtual bool updateAlignment(TIMESTAMP timestamp)=0;
|
||||
|
||||
|
||||
/** Set the transmitter frequency */
|
||||
virtual bool setTxFreq(double wFreq, int chan = 0)=0;
|
||||
virtual bool setTxFreq(double wFreq, size_t chan = 0) = 0;
|
||||
|
||||
/** Set the receiver frequency */
|
||||
virtual bool setRxFreq(double wFreq, int chan = 0)=0;
|
||||
virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
virtual TIMESTAMP initialWriteTimestamp(void)=0;
|
||||
@@ -102,10 +116,10 @@ class RadioDevice {
|
||||
virtual double fullScaleOutputValue()=0;
|
||||
|
||||
/** sets the receive chan gain, returns the gain setting **/
|
||||
virtual double setRxGain(double dB, int chan = 0)=0;
|
||||
virtual double setRxGain(double dB, size_t chan = 0) = 0;
|
||||
|
||||
/** gets the current receive gain **/
|
||||
virtual double getRxGain(int chan = 0)=0;
|
||||
virtual double getRxGain(size_t chan = 0) = 0;
|
||||
|
||||
/** return maximum Rx Gain **/
|
||||
virtual double maxRxGain(void) = 0;
|
||||
@@ -114,7 +128,7 @@ class RadioDevice {
|
||||
virtual double minRxGain(void) = 0;
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
virtual double setTxGain(double dB, int chan = 0)=0;
|
||||
virtual double setTxGain(double dB, size_t chan = 0) = 0;
|
||||
|
||||
/** return maximum Tx Gain **/
|
||||
virtual double maxTxGain(void) = 0;
|
||||
@@ -122,18 +136,13 @@ class RadioDevice {
|
||||
/** return minimum Tx Gain **/
|
||||
virtual double minTxGain(void) = 0;
|
||||
|
||||
/** set and return antennas selection **/
|
||||
virtual void setTxAntenna(std::string &name) = 0;
|
||||
virtual void setRxAntenna(std::string &name) = 0;
|
||||
virtual std::string getRxAntenna() = 0;
|
||||
virtual std::string getTxAntenna() = 0;
|
||||
|
||||
/** Return internal status values */
|
||||
virtual double getTxFreq(int chan = 0)=0;
|
||||
virtual double getRxFreq(int chan = 0)=0;
|
||||
virtual double getTxFreq(size_t chan = 0) = 0;
|
||||
virtual double getRxFreq(size_t chan = 0) = 0;
|
||||
virtual double getSampleRate()=0;
|
||||
virtual double numberRead()=0;
|
||||
virtual double numberWritten()=0;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,79 +1,85 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2012 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
* Radio device interface
|
||||
*
|
||||
* Copyright (C) 2008-2014 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include "radioInterface.h"
|
||||
#include "Resampler.h"
|
||||
#include <Logger.h>
|
||||
#include <PRBS.h>
|
||||
|
||||
bool started = false;
|
||||
|
||||
/* Device side buffers */
|
||||
static short *rx_buf[CHAN_MAX];
|
||||
static short *tx_buf[CHAN_MAX];
|
||||
|
||||
/* Complex float to short conversion */
|
||||
static void floatToShort(short *out, float *in, int num)
|
||||
{
|
||||
for (int i = 0; i < num; i++) {
|
||||
out[2 * i + 0] = (short) in[2 * i + 0];
|
||||
out[2 * i + 1] = (short) in[2 * i + 1];
|
||||
}
|
||||
extern "C" {
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
/* Complex short to float conversion */
|
||||
static void shortToFloat(float *out, short *in, int num)
|
||||
{
|
||||
for (int i = 0; i < num; i++) {
|
||||
out[2 * i + 0] = (float) in[2 * i + 0];
|
||||
out[2 * i + 1] = (float) in[2 * i + 1];
|
||||
}
|
||||
}
|
||||
#define CHUNK 625
|
||||
#define NUMCHUNKS 4
|
||||
|
||||
RadioInterface::RadioInterface(RadioDevice *wRadio,
|
||||
int wChanM,
|
||||
int wSPS,
|
||||
int wReceiveOffset,
|
||||
GSM::Time wStartTime)
|
||||
: mChanM(wChanM), underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
|
||||
mRadio(wRadio), receiveOffset(wReceiveOffset), samplesPerSymbol(wSPS),
|
||||
powerScaling(1.0), loadTest(false)
|
||||
RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
|
||||
size_t rx_sps, size_t chans,
|
||||
int wReceiveOffset, GSM::Time wStartTime)
|
||||
: mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
|
||||
underrun(false), overrun(false), receiveOffset(wReceiveOffset), mOn(false)
|
||||
{
|
||||
mClock.set(wStartTime);
|
||||
}
|
||||
|
||||
RadioInterface::~RadioInterface(void)
|
||||
{
|
||||
if (mOn) {
|
||||
mRadio->stop();
|
||||
close();
|
||||
close();
|
||||
}
|
||||
|
||||
for (int i = 0; i < mChanM; i++) {
|
||||
if (rcvBuffer[i] != NULL)
|
||||
delete rcvBuffer[i];
|
||||
if (sendBuffer[i] != NULL)
|
||||
delete sendBuffer[i];
|
||||
}
|
||||
bool RadioInterface::init(int type)
|
||||
{
|
||||
if ((type != RadioDevice::NORMAL) || !mChans) {
|
||||
LOG(ALERT) << "Invalid configuration";
|
||||
return false;
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
sendBuffer.resize(mChans);
|
||||
recvBuffer.resize(mChans);
|
||||
convertSendBuffer.resize(mChans);
|
||||
convertRecvBuffer.resize(mChans);
|
||||
mReceiveFIFO.resize(mChans);
|
||||
powerScaling.resize(mChans);
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
|
||||
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
|
||||
|
||||
convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
|
||||
convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
|
||||
|
||||
powerScaling[i] = 1.0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RadioInterface::close()
|
||||
{
|
||||
sendBuffer.resize(0);
|
||||
recvBuffer.resize(0);
|
||||
convertSendBuffer.resize(0);
|
||||
convertRecvBuffer.resize(0);
|
||||
}
|
||||
|
||||
double RadioInterface::fullScaleInputValue(void) {
|
||||
@@ -84,111 +90,112 @@ double RadioInterface::fullScaleOutputValue(void) {
|
||||
return mRadio->fullScaleOutputValue();
|
||||
}
|
||||
|
||||
|
||||
void RadioInterface::setPowerAttenuation(double atten, int chan)
|
||||
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
||||
{
|
||||
double rfGain, digAtten;
|
||||
|
||||
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
|
||||
digAtten = atten - mRadio->maxTxGain() + rfGain;
|
||||
if (chan >= mChans) {
|
||||
LOG(ALERT) << "Invalid channel requested";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (atten < 0.0)
|
||||
atten = 0.0;
|
||||
|
||||
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
|
||||
digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
|
||||
|
||||
if (digAtten < 1.0)
|
||||
powerScaling = 1.0;
|
||||
powerScaling[chan] = 1.0;
|
||||
else
|
||||
powerScaling = 1.0/sqrt(pow(10, (digAtten/10.0)));
|
||||
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
|
||||
|
||||
return atten;
|
||||
}
|
||||
|
||||
int RadioInterface::radioifyVector(signalVector &wVector,
|
||||
float *retVector,
|
||||
float scale,
|
||||
bool zero)
|
||||
size_t chan, bool zero)
|
||||
{
|
||||
int i;
|
||||
signalVector::iterator itr = wVector.begin();
|
||||
|
||||
if (zero) {
|
||||
memset(retVector, 0, wVector.size() * 2 * sizeof(float));
|
||||
return wVector.size();
|
||||
}
|
||||
|
||||
for (i = 0; i < wVector.size(); i++) {
|
||||
retVector[2 * i + 0] = itr->real() * scale;
|
||||
retVector[2 * i + 1] = itr->imag() * scale;
|
||||
itr++;
|
||||
}
|
||||
if (zero)
|
||||
sendBuffer[chan]->zero(wVector.size());
|
||||
else
|
||||
sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
|
||||
|
||||
return wVector.size();
|
||||
}
|
||||
|
||||
int RadioInterface::unRadioifyVector(float *floatVector, int offset,
|
||||
signalVector &newVector)
|
||||
int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
|
||||
{
|
||||
int i;
|
||||
signalVector::iterator itr = newVector.begin();
|
||||
|
||||
for (i = 0; i < newVector.size(); i++) {
|
||||
*itr++ = Complex<float>(floatVector[offset + 2 * i + 0],
|
||||
floatVector[offset + 2 * i + 1]);
|
||||
if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
|
||||
LOG(ALERT) << "Insufficient number of samples in receive buffer";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return newVector.size();
|
||||
recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
|
||||
|
||||
return newVector->size();
|
||||
}
|
||||
|
||||
bool RadioInterface::tuneTx(double freq, int chan)
|
||||
bool RadioInterface::tuneTx(double freq, size_t chan)
|
||||
{
|
||||
return mRadio->setTxFreq(freq, chan);
|
||||
}
|
||||
|
||||
bool RadioInterface::tuneRx(double freq, int chan)
|
||||
bool RadioInterface::tuneRx(double freq, size_t chan)
|
||||
{
|
||||
return mRadio->setRxFreq(freq, chan);
|
||||
}
|
||||
|
||||
|
||||
bool RadioInterface::start()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (mOn)
|
||||
return true;
|
||||
|
||||
LOG(INFO) << "Starting radio device";
|
||||
#ifdef USRP1
|
||||
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
||||
(void*)this);
|
||||
#endif
|
||||
|
||||
if (!mRadio->start())
|
||||
return false;
|
||||
|
||||
mOn = true;
|
||||
#ifdef USRP1
|
||||
mAlignRadioServiceLoopThread = new Thread(32768);
|
||||
mAlignRadioServiceLoopThread->start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
||||
(void*)this);
|
||||
#endif
|
||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||
readTimestamp = mRadio->initialReadTimestamp();
|
||||
for (i = 0; i < mChanM; i++) {
|
||||
sendBuffer[i] = new float[8*2*INCHUNK];
|
||||
rcvBuffer[i] = new float[8*2*OUTCHUNK];
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
sendBuffer[i]->reset();
|
||||
recvBuffer[i]->reset();
|
||||
}
|
||||
|
||||
/* Init I/O specific variables if applicable */
|
||||
init();
|
||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||
readTimestamp = mRadio->initialReadTimestamp();
|
||||
|
||||
mRadio->start();
|
||||
LOG(DEBUG) << "Radio started";
|
||||
mRadio->updateAlignment(writeTimestamp-10000);
|
||||
mRadio->updateAlignment(writeTimestamp-10000);
|
||||
mRadio->updateAlignment(writeTimestamp-10000);
|
||||
|
||||
mOn = true;
|
||||
LOG(INFO) << "Radio started";
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop the radio device
|
||||
*
|
||||
* This is a pass-through call to the device interface. Because the underlying
|
||||
* stop command issuance generally doesn't return confirmation on device status,
|
||||
* this call will only return false if the device is already stopped.
|
||||
*/
|
||||
bool RadioInterface::stop()
|
||||
{
|
||||
if (!mOn)
|
||||
if (!mOn || !mRadio->stop())
|
||||
return false;
|
||||
|
||||
mOn = false;
|
||||
mRadio->stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USRP1
|
||||
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
|
||||
{
|
||||
while (radioInterface->on()) {
|
||||
while (1) {
|
||||
radioInterface->alignRadio();
|
||||
pthread_testcancel();
|
||||
}
|
||||
@@ -201,154 +208,151 @@ void RadioInterface::alignRadio() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void RadioInterface::driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!mOn)
|
||||
return;
|
||||
|
||||
for (i = 0; i < mChanM; i++) {
|
||||
radioifyVector(*radioBurst[i], sendBuffer[i] + 2 * sendCursor,
|
||||
powerScaling, zeroBurst[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* All bursts should be the same size since all transceivers are
|
||||
* tied with a single clock in the radio interface.
|
||||
*/
|
||||
sendCursor += radioBurst[0]->size();
|
||||
|
||||
pushBuffer();
|
||||
}
|
||||
|
||||
static inline void shiftRxBuffers(float **buf, int offset, int len, int chanM)
|
||||
{
|
||||
for (int i = 0; i < chanM; i++)
|
||||
memmove(buf[i], buf[i] + offset, sizeof(float) * len);
|
||||
}
|
||||
|
||||
void RadioInterface::loadVectors(unsigned tN, int samplesPerBurst,
|
||||
int idx, GSM::Time rxClock)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mChanM; i++) {
|
||||
signalVector rxVector(samplesPerBurst);
|
||||
unRadioifyVector(rcvBuffer[i], idx * 2, rxVector);
|
||||
radioVector *rxBurst = new radioVector(rxVector, rxClock);
|
||||
mReceiveFIFO[i].write(rxBurst);
|
||||
}
|
||||
}
|
||||
|
||||
void RadioInterface::driveReceiveRadio()
|
||||
void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
|
||||
std::vector<bool> &zeros)
|
||||
{
|
||||
if (!mOn)
|
||||
return;
|
||||
|
||||
if (mReceiveFIFO[0].size() > 8)
|
||||
return;
|
||||
for (size_t i = 0; i < mChans; i++)
|
||||
radioifyVector(*bursts[i], i, zeros[i]);
|
||||
|
||||
while (pushBuffer());
|
||||
}
|
||||
|
||||
bool RadioInterface::driveReceiveRadio()
|
||||
{
|
||||
radioVector *burst = NULL;
|
||||
|
||||
if (!mOn)
|
||||
return false;
|
||||
|
||||
pullBuffer();
|
||||
|
||||
GSM::Time rcvClock = mClock.get();
|
||||
rcvClock.decTN(receiveOffset);
|
||||
unsigned tN = rcvClock.TN();
|
||||
int rcvSz = rcvCursor;
|
||||
int readSz = 0;
|
||||
int recvSz = recvBuffer[0]->getAvailSamples();
|
||||
const int symbolsPerSlot = gSlotLen + 8;
|
||||
int samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
|
||||
int burstSize;
|
||||
|
||||
// while there's enough data in receive buffer, form received
|
||||
// GSM bursts and pass up to Transceiver
|
||||
// Using the 157-156-156-156 symbols per timeslot format.
|
||||
while (rcvSz >= samplesPerBurst) {
|
||||
if (rcvClock.FN() >= 0) {
|
||||
loadVectors(tN, samplesPerBurst, readSz, rcvClock);
|
||||
if (mSPSRx == 4)
|
||||
burstSize = 625;
|
||||
else
|
||||
burstSize = symbolsPerSlot + (tN % 4 == 0);
|
||||
|
||||
/*
|
||||
* Pre-allocate head room for the largest correlation size
|
||||
* so we can later avoid a re-allocation and copy
|
||||
* */
|
||||
size_t head = GSM::gRACHSynchSequence.size();
|
||||
|
||||
/*
|
||||
* Form receive bursts and pass up to transceiver. Use repeating
|
||||
* pattern of 157-156-156-156 symbols per timeslot
|
||||
*/
|
||||
while (recvSz > burstSize) {
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
burst = new radioVector(rcvClock, burstSize, head);
|
||||
unRadioifyVector(burst->getVector(), i);
|
||||
|
||||
if (mReceiveFIFO[i].size() < 32)
|
||||
mReceiveFIFO[i].write(burst);
|
||||
else
|
||||
delete burst;
|
||||
}
|
||||
|
||||
mClock.incTN();
|
||||
rcvClock.incTN();
|
||||
|
||||
readSz += samplesPerBurst;
|
||||
rcvSz -= samplesPerBurst;
|
||||
recvSz -= burstSize;
|
||||
|
||||
tN = rcvClock.TN();
|
||||
samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
|
||||
|
||||
if (mSPSRx != 4)
|
||||
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
|
||||
}
|
||||
|
||||
if (readSz > 0) {
|
||||
rcvCursor -= readSz;
|
||||
shiftRxBuffers(rcvBuffer, 2 * readSz, 2 * rcvCursor, mChanM);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double RadioInterface::setRxGain(double dB, int chan)
|
||||
bool RadioInterface::isUnderrun()
|
||||
{
|
||||
if (mRadio)
|
||||
return mRadio->setRxGain(dB, chan);
|
||||
else
|
||||
return -1;
|
||||
bool retVal = underrun;
|
||||
underrun = false;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
double RadioInterface::getRxGain(int chan)
|
||||
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
|
||||
{
|
||||
if (mRadio)
|
||||
return mRadio->getRxGain(chan);
|
||||
else
|
||||
return -1;
|
||||
if (chan >= mReceiveFIFO.size())
|
||||
return NULL;
|
||||
|
||||
return &mReceiveFIFO[chan];
|
||||
}
|
||||
|
||||
bool RadioInterface::init()
|
||||
double RadioInterface::setRxGain(double dB, size_t chan)
|
||||
{
|
||||
for (int i = 0; i < CHAN_MAX; i++) {
|
||||
rx_buf[i] = new short[2 * OUTCHUNK];
|
||||
tx_buf[i] = new short[4 * 2 * INCHUNK];
|
||||
}
|
||||
return mRadio->setRxGain(dB, chan);
|
||||
}
|
||||
|
||||
void RadioInterface::close()
|
||||
double RadioInterface::getRxGain(size_t chan)
|
||||
{
|
||||
for (int i = 0; i < CHAN_MAX; i++) {
|
||||
delete rx_buf[i];
|
||||
delete tx_buf[i];
|
||||
}
|
||||
return mRadio->getRxGain(chan);
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterface::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
|
||||
|
||||
/* Read samples. Fail if we don't get what we want. */
|
||||
int num_rd = mRadio->readSamples(rx_buf, mChanM, OUTCHUNK, readTimestamp);
|
||||
|
||||
LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
|
||||
assert(num_rd == OUTCHUNK);
|
||||
|
||||
underrun |= local_underrun;
|
||||
readTimestamp += (TIMESTAMP) num_rd;
|
||||
|
||||
for (int i = 0; i < mChanM; i++)
|
||||
shortToFloat(rcvBuffer[i] + 2 * rcvCursor, rx_buf[i], num_rd);
|
||||
|
||||
rcvCursor += num_rd;
|
||||
}
|
||||
|
||||
/* Send timestamped chunk to the device with arbitrary size */
|
||||
void RadioInterface::pushBuffer()
|
||||
{
|
||||
if (sendCursor < INCHUNK)
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < mChanM; i++)
|
||||
floatToShort(tx_buf[i], sendBuffer[i], sendCursor);
|
||||
/* Outer buffer access size is fixed */
|
||||
numRecv = mRadio->readSamples(convertRecvBuffer,
|
||||
segmentLen,
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
|
||||
/* Write samples. Fail if we don't get what we want. */
|
||||
int num_smpls = mRadio->writeSamples(tx_buf, mChanM, sendCursor,
|
||||
writeTimestamp, &underrun);
|
||||
assert(num_smpls == sendCursor);
|
||||
if (numRecv != segmentLen) {
|
||||
LOG(ALERT) << "Receive error " << numRecv;
|
||||
return;
|
||||
}
|
||||
|
||||
writeTimestamp += (TIMESTAMP) num_smpls;
|
||||
sendCursor = 0;
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
convert_short_float(recvBuffer[i]->getWriteSegment(),
|
||||
convertRecvBuffer[i],
|
||||
segmentLen * 2);
|
||||
}
|
||||
|
||||
underrun |= local_underrun;
|
||||
readTimestamp += numRecv;
|
||||
}
|
||||
|
||||
/* Send timestamped chunk to the device with arbitrary size */
|
||||
bool RadioInterface::pushBuffer()
|
||||
{
|
||||
size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
|
||||
|
||||
if (sendBuffer[0]->getAvailSegments() < 1)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
convert_float_short(convertSendBuffer[i],
|
||||
(float *) sendBuffer[i]->getReadSegment(),
|
||||
powerScaling[i],
|
||||
segmentLen * 2);
|
||||
}
|
||||
|
||||
/* Send the all samples in the send buffer */
|
||||
numSent = mRadio->writeSamples(convertSendBuffer,
|
||||
segmentLen,
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
writeTimestamp += numSent;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008, 2012 Free Software Foundation, Inc.
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
@@ -12,8 +12,7 @@
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _RADIOINTEFACE_H_
|
||||
#define _RADIOINTEFACE_H_
|
||||
|
||||
|
||||
#include "sigProcLib.h"
|
||||
#include "GSMCommon.h"
|
||||
@@ -21,13 +20,10 @@
|
||||
#include "radioDevice.h"
|
||||
#include "radioVector.h"
|
||||
#include "radioClock.h"
|
||||
|
||||
/** samples per GSM symbol */
|
||||
#define SAMPSPERSYM 1
|
||||
#define INCHUNK (625)
|
||||
#define OUTCHUNK (625)
|
||||
#define CHAN_MAX 2
|
||||
|
||||
#include "radioBuffer.h"
|
||||
#include "Resampler.h"
|
||||
#include "Channelizer.h"
|
||||
#include "Synthesis.h"
|
||||
|
||||
static const unsigned gSlotLen = 148; ///< number of symbols per slot, not counting guard periods
|
||||
|
||||
@@ -36,18 +32,22 @@ class RadioInterface {
|
||||
|
||||
protected:
|
||||
|
||||
int mChanM; ///< channelizer width
|
||||
Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections
|
||||
|
||||
VectorFIFO mReceiveFIFO[CHAN_MAX]; ///< FIFO that holds receive bursts
|
||||
std::vector<VectorFIFO> mReceiveFIFO; ///< FIFO that holds receive bursts
|
||||
|
||||
RadioDevice *mRadio; ///< the USRP object
|
||||
|
||||
float *sendBuffer[CHAN_MAX];
|
||||
unsigned sendCursor;
|
||||
|
||||
float *rcvBuffer[CHAN_MAX];
|
||||
unsigned rcvCursor;
|
||||
size_t mSPSTx;
|
||||
size_t mSPSRx;
|
||||
size_t mChans;
|
||||
|
||||
std::vector<RadioBuffer *> sendBuffer;
|
||||
std::vector<RadioBuffer *> recvBuffer;
|
||||
|
||||
std::vector<short *> convertRecvBuffer;
|
||||
std::vector<short *> convertSendBuffer;
|
||||
std::vector<float> powerScaling;
|
||||
bool underrun; ///< indicates writes to USRP are too slow
|
||||
bool overrun; ///< indicates reads from USRP are too slow
|
||||
TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
|
||||
@@ -55,89 +55,71 @@ protected:
|
||||
|
||||
RadioClock mClock; ///< the basestation clock!
|
||||
|
||||
int samplesPerSymbol; ///< samples per GSM symbol
|
||||
int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots
|
||||
|
||||
bool mOn; ///< indicates radio is on
|
||||
|
||||
double powerScaling;
|
||||
|
||||
bool loadTest;
|
||||
int mNumARFCNs;
|
||||
signalVector *finalVec, *finalVec9;
|
||||
|
||||
private:
|
||||
/** initialize I/O internals */
|
||||
bool init();
|
||||
|
||||
/** format samples to USRP */
|
||||
int radioifyVector(signalVector &wVector,
|
||||
float *floatVector,
|
||||
float scale,
|
||||
bool zero);
|
||||
/** format samples to USRP */
|
||||
int radioifyVector(signalVector &wVector, size_t chan, bool zero);
|
||||
|
||||
/** format samples from USRP */
|
||||
int unRadioifyVector(float *floatVector, int offset, signalVector &wVector);
|
||||
int unRadioifyVector(signalVector *wVector, size_t chan);
|
||||
|
||||
/** push GSM bursts into the transmit buffer */
|
||||
virtual void pushBuffer(void);
|
||||
virtual bool pushBuffer(void);
|
||||
|
||||
/** pull GSM bursts from the receive buffer */
|
||||
virtual void pullBuffer(void);
|
||||
|
||||
/** load receive vectors into FIFO's */
|
||||
void loadVectors(unsigned tN, int samplesPerBurst, int index, GSM::Time rxClock);
|
||||
|
||||
public:
|
||||
|
||||
/** start the interface */
|
||||
bool start();
|
||||
bool stop();
|
||||
|
||||
bool started() { return mOn; };
|
||||
|
||||
/** shutdown interface */
|
||||
void close();
|
||||
/** intialization */
|
||||
virtual bool init(int type);
|
||||
virtual void close();
|
||||
|
||||
/** constructor */
|
||||
RadioInterface(RadioDevice* wRadio,
|
||||
int wChanM = 1,
|
||||
int wSPS = SAMPSPERSYM,
|
||||
int receiveOffset = 3,
|
||||
GSM::Time wStartTime = GSM::Time(0, 0));
|
||||
|
||||
RadioInterface(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps,
|
||||
size_t chans = 1, int receiveOffset = 3,
|
||||
GSM::Time wStartTime = GSM::Time(0));
|
||||
|
||||
/** destructor */
|
||||
~RadioInterface();
|
||||
virtual ~RadioInterface();
|
||||
|
||||
void setSamplesPerSymbol(int wSamplesPerSymbol) {if (!mOn) samplesPerSymbol = wSamplesPerSymbol;}
|
||||
/** check for underrun, resets underrun value */
|
||||
bool isUnderrun();
|
||||
|
||||
int getSamplesPerSymbol() { return samplesPerSymbol;}
|
||||
|
||||
/** return the receive FIFO */
|
||||
VectorFIFO* receiveFIFO(int num) { return &mReceiveFIFO[num];}
|
||||
VectorFIFO* receiveFIFO(size_t chan = 0);
|
||||
|
||||
/** return the basestation clock */
|
||||
RadioClock* getClock(void) { return &mClock;};
|
||||
|
||||
/** set transmit frequency */
|
||||
bool tuneTx(double freq, int chan = 0);
|
||||
virtual bool tuneTx(double freq, size_t chan = 0);
|
||||
|
||||
/** set receive frequency */
|
||||
bool tuneRx(double freq, int chan = 0);
|
||||
virtual bool tuneRx(double freq, size_t chan = 0);
|
||||
|
||||
/** set receive gain */
|
||||
double setRxGain(double dB, int chan = 0);
|
||||
double setRxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** get receive gain */
|
||||
double getRxGain(int chan = 0);
|
||||
double getRxGain(size_t chan = 0);
|
||||
|
||||
/** drive transmission of GSM bursts */
|
||||
void driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst);
|
||||
void driveTransmitRadio(std::vector<signalVector *> &bursts,
|
||||
std::vector<bool> &zeros);
|
||||
|
||||
/** drive reception of GSM bursts */
|
||||
void driveReceiveRadio();
|
||||
bool driveReceiveRadio();
|
||||
|
||||
void setPowerAttenuation(double atten, int chan = 0);
|
||||
int setPowerAttenuation(int atten, size_t chan = 0);
|
||||
|
||||
/** returns the full-scale transmit amplitude **/
|
||||
double fullScaleInputValue();
|
||||
@@ -146,10 +128,66 @@ public:
|
||||
double fullScaleOutputValue();
|
||||
|
||||
/** set thread priority on current thread */
|
||||
void setPriority() { mRadio->setPriority(); }
|
||||
void setPriority(float prio = 0.5) { mRadio->setPriority(prio); }
|
||||
|
||||
/** get transport window type of attached device */
|
||||
enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
|
||||
|
||||
#if USRP1
|
||||
protected:
|
||||
|
||||
/** drive synchronization of Tx/Rx of USRP */
|
||||
void alignRadio();
|
||||
|
||||
friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* _RADIOINTEFACE_H_ */
|
||||
#if USRP1
|
||||
/** synchronization thread loop */
|
||||
void *AlignRadioServiceLoopAdapter(RadioInterface*);
|
||||
#endif
|
||||
|
||||
class RadioInterfaceResamp : public RadioInterface {
|
||||
private:
|
||||
signalVector *outerSendBuffer;
|
||||
signalVector *outerRecvBuffer;
|
||||
|
||||
bool pushBuffer();
|
||||
void pullBuffer();
|
||||
|
||||
public:
|
||||
RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
|
||||
~RadioInterfaceResamp();
|
||||
|
||||
bool init(int type);
|
||||
void close();
|
||||
};
|
||||
|
||||
class RadioInterfaceMulti : public RadioInterface {
|
||||
private:
|
||||
bool pushBuffer();
|
||||
void pullBuffer();
|
||||
|
||||
signalVector *outerSendBuffer;
|
||||
signalVector *outerRecvBuffer;
|
||||
std::vector<signalVector *> history;
|
||||
std::vector<bool> active;
|
||||
|
||||
Resampler *dnsampler;
|
||||
Resampler *upsampler;
|
||||
Channelizer *channelizer;
|
||||
Synthesis *synthesis;
|
||||
|
||||
public:
|
||||
RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
|
||||
size_t rx_sps, size_t chans = 1);
|
||||
~RadioInterfaceMulti();
|
||||
|
||||
bool init(int type);
|
||||
void close();
|
||||
|
||||
bool tuneTx(double freq, size_t chan);
|
||||
bool tuneRx(double freq, size_t chan);
|
||||
double setRxGain(double dB, size_t chan);
|
||||
};
|
||||
|
||||
391
Transceiver52M/radioInterfaceMulti.cpp
Normal file
391
Transceiver52M/radioInterfaceMulti.cpp
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Multi-carrier radio interface
|
||||
*
|
||||
* Copyright (C) 2016 Ettus Research LLC
|
||||
*
|
||||
* Author: Tom Tsou <tom.tsou@ettus.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <radioInterface.h>
|
||||
#include <Logger.h>
|
||||
|
||||
#include "Resampler.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
/* Resampling parameters for 64 MHz clocking */
|
||||
#define RESAMP_INRATE 65
|
||||
#define RESAMP_OUTRATE (96 / 2)
|
||||
|
||||
/* Universal resampling parameters */
|
||||
#define NUMCHUNKS 24
|
||||
|
||||
#define MCHANS 4
|
||||
|
||||
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
|
||||
size_t rx_sps, size_t chans)
|
||||
: RadioInterface(radio, tx_sps, rx_sps, chans),
|
||||
outerSendBuffer(NULL), outerRecvBuffer(NULL),
|
||||
dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
RadioInterfaceMulti::~RadioInterfaceMulti()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void RadioInterfaceMulti::close()
|
||||
{
|
||||
delete outerSendBuffer;
|
||||
delete outerRecvBuffer;
|
||||
delete dnsampler;
|
||||
delete upsampler;
|
||||
delete channelizer;
|
||||
delete synthesis;
|
||||
|
||||
outerSendBuffer = NULL;
|
||||
outerRecvBuffer = NULL;
|
||||
dnsampler = NULL;
|
||||
upsampler = NULL;
|
||||
channelizer = NULL;
|
||||
synthesis = NULL;
|
||||
|
||||
mReceiveFIFO.resize(0);
|
||||
powerScaling.resize(0);
|
||||
history.resize(0);
|
||||
active.resize(0);
|
||||
|
||||
RadioInterface::close();
|
||||
}
|
||||
|
||||
static int getLogicalChan(size_t pchan, size_t chans)
|
||||
{
|
||||
switch (chans) {
|
||||
case 1:
|
||||
if (pchan == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 2:
|
||||
if (pchan == 0)
|
||||
return 0;
|
||||
if (pchan == 3)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 3:
|
||||
if (pchan == 1)
|
||||
return 0;
|
||||
if (pchan == 0)
|
||||
return 1;
|
||||
if (pchan == 3)
|
||||
return 2;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int getFreqShift(size_t chans)
|
||||
{
|
||||
switch (chans) {
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 0;
|
||||
case 3:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize I/O specific objects */
|
||||
bool RadioInterfaceMulti::init(int type)
|
||||
{
|
||||
float cutoff = 1.0f;
|
||||
size_t inchunk = 0, outchunk = 0;
|
||||
|
||||
if (mChans > MCHANS - 1) {
|
||||
LOG(ALERT) << "Invalid channel configuration " << mChans;
|
||||
return false;
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
sendBuffer.resize(mChans);
|
||||
recvBuffer.resize(mChans);
|
||||
convertSendBuffer.resize(1);
|
||||
convertRecvBuffer.resize(1);
|
||||
|
||||
mReceiveFIFO.resize(mChans);
|
||||
powerScaling.resize(mChans);
|
||||
history.resize(mChans);
|
||||
active.resize(MCHANS, false);
|
||||
|
||||
inchunk = RESAMP_INRATE * 4;
|
||||
outchunk = RESAMP_OUTRATE * 4;
|
||||
|
||||
if (inchunk * NUMCHUNKS < 625 * 2) {
|
||||
LOG(ALERT) << "Invalid inner chunk size " << inchunk;
|
||||
return false;
|
||||
}
|
||||
|
||||
dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
|
||||
if (!dnsampler->init(1.0)) {
|
||||
LOG(ALERT) << "Rx resampler failed to initialize";
|
||||
return false;
|
||||
}
|
||||
|
||||
upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
|
||||
if (!upsampler->init(cutoff)) {
|
||||
LOG(ALERT) << "Tx resampler failed to initialize";
|
||||
return false;
|
||||
}
|
||||
|
||||
channelizer = new Channelizer(MCHANS, outchunk);
|
||||
if (!channelizer->init()) {
|
||||
LOG(ALERT) << "Rx channelizer failed to initialize";
|
||||
return false;
|
||||
}
|
||||
|
||||
synthesis = new Synthesis(MCHANS, outchunk);
|
||||
if (!synthesis->init()) {
|
||||
LOG(ALERT) << "Tx synthesis filter failed to initialize";
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate high and low rate buffers. The high rate receive
|
||||
* buffer and low rate transmit vectors feed into the resampler
|
||||
* and requires headroom equivalent to the filter length. Low
|
||||
* rate buffers are allocated in the main radio interface code.
|
||||
*/
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
|
||||
upsampler->len(), true);
|
||||
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
|
||||
0, false);
|
||||
history[i] = new signalVector(dnsampler->len());
|
||||
|
||||
synthesis->resetBuffer(i);
|
||||
}
|
||||
|
||||
outerSendBuffer = new signalVector(synthesis->outputLen());
|
||||
outerRecvBuffer = new signalVector(channelizer->inputLen());
|
||||
|
||||
convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
|
||||
convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
|
||||
|
||||
/* Configure channels */
|
||||
switch (mChans) {
|
||||
case 1:
|
||||
active[0] = true;
|
||||
break;
|
||||
case 2:
|
||||
active[0] = true;
|
||||
active[3] = true;
|
||||
break;
|
||||
case 3:
|
||||
active[0] = true;
|
||||
active[1] = true;
|
||||
active[3] = true;
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported channel combination";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterfaceMulti::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
size_t num;
|
||||
float *buf;
|
||||
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
num = mRadio->readSamples(convertRecvBuffer,
|
||||
outerRecvBuffer->size(),
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
if (num != channelizer->inputLen()) {
|
||||
LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
|
||||
return;
|
||||
}
|
||||
|
||||
convert_short_float((float *) outerRecvBuffer->begin(),
|
||||
convertRecvBuffer[0], 2 * outerRecvBuffer->size());
|
||||
|
||||
underrun |= local_underrun;
|
||||
readTimestamp += num;
|
||||
|
||||
channelizer->rotate((float *) outerRecvBuffer->begin(),
|
||||
outerRecvBuffer->size());
|
||||
|
||||
for (size_t pchan = 0; pchan < MCHANS; pchan++) {
|
||||
if (!active[pchan])
|
||||
continue;
|
||||
|
||||
int lchan = getLogicalChan(pchan, mChans);
|
||||
if (lchan < 0) {
|
||||
LOG(ALERT) << "Invalid logical channel " << pchan;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update history by writing into the head portion of the
|
||||
* channelizer output buffer. For this to work, filter length of
|
||||
* the polyphase channelizer partition filter should be equal to
|
||||
* or larger than the resampling filter.
|
||||
*/
|
||||
buf = channelizer->outputBuffer(pchan);
|
||||
size_t cLen = channelizer->outputLen();
|
||||
size_t hLen = dnsampler->len();
|
||||
size_t hSize = 2 * hLen * sizeof(float);
|
||||
|
||||
memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
|
||||
memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
|
||||
|
||||
float *wr_segment = recvBuffer[lchan]->getWriteSegment();
|
||||
|
||||
/* Write to the end of the inner receive buffer */
|
||||
if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
|
||||
channelizer->outputLen(),
|
||||
wr_segment,
|
||||
recvBuffer[lchan]->getSegmentLen())) {
|
||||
LOG(ALERT) << "Sample rate upsampling error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send a timestamped chunk to the device */
|
||||
bool RadioInterfaceMulti::pushBuffer()
|
||||
{
|
||||
if (sendBuffer[0]->getAvailSegments() <= 0)
|
||||
return false;
|
||||
|
||||
for (size_t pchan = 0; pchan < MCHANS; pchan++) {
|
||||
if (!active[pchan]) {
|
||||
synthesis->resetBuffer(pchan);
|
||||
continue;
|
||||
}
|
||||
|
||||
int lchan = getLogicalChan(pchan, mChans);
|
||||
if (lchan < 0) {
|
||||
LOG(ALERT) << "Invalid logical channel " << pchan;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
|
||||
sendBuffer[lchan]->getSegmentLen(),
|
||||
synthesis->inputBuffer(pchan),
|
||||
synthesis->inputLen())) {
|
||||
LOG(ALERT) << "Sample rate downsampling error";
|
||||
}
|
||||
}
|
||||
|
||||
synthesis->rotate((float *) outerSendBuffer->begin(),
|
||||
outerSendBuffer->size());
|
||||
|
||||
convert_float_short(convertSendBuffer[0],
|
||||
(float *) outerSendBuffer->begin(),
|
||||
1.0 / (float) mChans, 2 * outerSendBuffer->size());
|
||||
|
||||
size_t num = mRadio->writeSamples(convertSendBuffer,
|
||||
outerSendBuffer->size(),
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
if (num != outerSendBuffer->size()) {
|
||||
LOG(ALERT) << "Transmit error " << num;
|
||||
}
|
||||
|
||||
writeTimestamp += num;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Frequency comparison limit */
|
||||
#define FREQ_DELTA_LIMIT 10.0
|
||||
|
||||
static bool fltcmp(double a, double b)
|
||||
{
|
||||
return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
|
||||
}
|
||||
|
||||
bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
|
||||
{
|
||||
if (chan >= mChans)
|
||||
return false;
|
||||
|
||||
double shift = (double) getFreqShift(mChans);
|
||||
|
||||
if (!chan)
|
||||
return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
|
||||
|
||||
double center = mRadio->getTxFreq();
|
||||
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
|
||||
LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
|
||||
<< freq / 1e6 << " MHz";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
|
||||
{
|
||||
if (chan >= mChans)
|
||||
return false;
|
||||
|
||||
double shift = (double) getFreqShift(mChans);
|
||||
|
||||
if (!chan)
|
||||
return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
|
||||
|
||||
double center = mRadio->getRxFreq();
|
||||
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
|
||||
LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
|
||||
<< freq / 1e6 << " MHz";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double RadioInterfaceMulti::setRxGain(double db, size_t chan)
|
||||
{
|
||||
if (!chan)
|
||||
return mRadio->setRxGain(db);
|
||||
else
|
||||
return mRadio->getRxGain();
|
||||
}
|
||||
234
Transceiver52M/radioInterfaceResamp.cpp
Normal file
234
Transceiver52M/radioInterfaceResamp.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Radio device interface with sample rate conversion
|
||||
*
|
||||
* Copyright (C) 2011-2014 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* Author: Tom Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <radioInterface.h>
|
||||
#include <Logger.h>
|
||||
|
||||
#include "Resampler.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
/* Resampling parameters for 64 MHz clocking */
|
||||
#define RESAMP_64M_INRATE 65
|
||||
#define RESAMP_64M_OUTRATE 96
|
||||
|
||||
/* Resampling parameters for 100 MHz clocking */
|
||||
#define RESAMP_100M_INRATE 52
|
||||
#define RESAMP_100M_OUTRATE 75
|
||||
|
||||
/* Universal resampling parameters */
|
||||
#define NUMCHUNKS 24
|
||||
|
||||
/*
|
||||
* Resampling filter bandwidth scaling factor
|
||||
* This narrows the filter cutoff relative to the output bandwidth
|
||||
* of the polyphase resampler. At 4 samples-per-symbol using the
|
||||
* 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
|
||||
* RMS phase error at the resampler output.
|
||||
*/
|
||||
#define RESAMP_TX4_FILTER 0.45
|
||||
|
||||
static Resampler *upsampler = NULL;
|
||||
static Resampler *dnsampler = NULL;
|
||||
static size_t resamp_inrate = 0;
|
||||
static size_t resamp_inchunk = 0;
|
||||
static size_t resamp_outrate = 0;
|
||||
static size_t resamp_outchunk = 0;
|
||||
|
||||
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
|
||||
size_t tx_sps, size_t rx_sps)
|
||||
: RadioInterface(wRadio, tx_sps, rx_sps, 1),
|
||||
outerSendBuffer(NULL), outerRecvBuffer(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
RadioInterfaceResamp::~RadioInterfaceResamp()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void RadioInterfaceResamp::close()
|
||||
{
|
||||
delete outerSendBuffer;
|
||||
delete outerRecvBuffer;
|
||||
|
||||
delete upsampler;
|
||||
delete dnsampler;
|
||||
|
||||
outerSendBuffer = NULL;
|
||||
outerRecvBuffer = NULL;
|
||||
|
||||
upsampler = NULL;
|
||||
dnsampler = NULL;
|
||||
|
||||
if (sendBuffer.size())
|
||||
sendBuffer[0] = NULL;
|
||||
if (recvBuffer.size())
|
||||
recvBuffer[0] = NULL;
|
||||
|
||||
RadioInterface::close();
|
||||
}
|
||||
|
||||
/* Initialize I/O specific objects */
|
||||
bool RadioInterfaceResamp::init(int type)
|
||||
{
|
||||
float cutoff = 1.0f;
|
||||
|
||||
close();
|
||||
|
||||
sendBuffer.resize(1);
|
||||
recvBuffer.resize(1);
|
||||
convertSendBuffer.resize(1);
|
||||
convertRecvBuffer.resize(1);
|
||||
mReceiveFIFO.resize(1);
|
||||
powerScaling.resize(1);
|
||||
|
||||
switch (type) {
|
||||
case RadioDevice::RESAMP_64M:
|
||||
resamp_inrate = RESAMP_64M_INRATE;
|
||||
resamp_outrate = RESAMP_64M_OUTRATE;
|
||||
break;
|
||||
case RadioDevice::RESAMP_100M:
|
||||
resamp_inrate = RESAMP_100M_INRATE;
|
||||
resamp_outrate = RESAMP_100M_OUTRATE;
|
||||
break;
|
||||
case RadioDevice::NORMAL:
|
||||
default:
|
||||
LOG(ALERT) << "Invalid device configuration";
|
||||
return false;
|
||||
}
|
||||
|
||||
resamp_inchunk = resamp_inrate * 4 * mSPSRx;
|
||||
resamp_outchunk = resamp_outrate * 4 * mSPSRx;
|
||||
|
||||
if (mSPSTx == 4)
|
||||
cutoff = RESAMP_TX4_FILTER;
|
||||
|
||||
dnsampler = new Resampler(resamp_inrate, resamp_outrate);
|
||||
if (!dnsampler->init()) {
|
||||
LOG(ALERT) << "Rx resampler failed to initialize";
|
||||
return false;
|
||||
}
|
||||
|
||||
upsampler = new Resampler(resamp_outrate, resamp_inrate);
|
||||
if (!upsampler->init(cutoff)) {
|
||||
LOG(ALERT) << "Tx resampler failed to initialize";
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate high and low rate buffers. The high rate receive
|
||||
* buffer and low rate transmit vectors feed into the resampler
|
||||
* and requires headroom equivalent to the filter length. Low
|
||||
* rate buffers are allocated in the main radio interface code.
|
||||
*/
|
||||
sendBuffer[0] = new RadioBuffer(NUMCHUNKS, resamp_inchunk,
|
||||
upsampler->len(), true);
|
||||
recvBuffer[0] = new RadioBuffer(NUMCHUNKS * 20, resamp_inchunk, 0, false);
|
||||
|
||||
outerSendBuffer =
|
||||
new signalVector(NUMCHUNKS * resamp_outchunk);
|
||||
outerRecvBuffer =
|
||||
new signalVector(resamp_outchunk, dnsampler->len());
|
||||
|
||||
convertSendBuffer[0] = new short[outerSendBuffer->size() * 2];
|
||||
convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterfaceResamp::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
int rc, num_recv;
|
||||
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
num_recv = mRadio->readSamples(convertRecvBuffer,
|
||||
resamp_outchunk,
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
if (num_recv != (int) resamp_outchunk) {
|
||||
LOG(ALERT) << "Receive error " << num_recv;
|
||||
return;
|
||||
}
|
||||
|
||||
convert_short_float((float *) outerRecvBuffer->begin(),
|
||||
convertRecvBuffer[0], 2 * resamp_outchunk);
|
||||
|
||||
underrun |= local_underrun;
|
||||
readTimestamp += (TIMESTAMP) resamp_outchunk;
|
||||
|
||||
/* Write to the end of the inner receive buffer */
|
||||
rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
|
||||
resamp_outchunk,
|
||||
recvBuffer[0]->getWriteSegment(),
|
||||
resamp_inchunk);
|
||||
if (rc < 0) {
|
||||
LOG(ALERT) << "Sample rate upsampling error";
|
||||
}
|
||||
|
||||
/* Set history for the next chunk */
|
||||
outerRecvBuffer->updateHistory();
|
||||
}
|
||||
|
||||
/* Send a timestamped chunk to the device */
|
||||
bool RadioInterfaceResamp::pushBuffer()
|
||||
{
|
||||
int rc;
|
||||
size_t numSent;
|
||||
|
||||
if (sendBuffer[0]->getAvailSegments() <= 0)
|
||||
return false;
|
||||
|
||||
/* Always send from the beginning of the buffer */
|
||||
rc = upsampler->rotate(sendBuffer[0]->getReadSegment(),
|
||||
resamp_inchunk,
|
||||
(float *) outerSendBuffer->begin(),
|
||||
resamp_outchunk);
|
||||
if (rc < 0) {
|
||||
LOG(ALERT) << "Sample rate downsampling error";
|
||||
}
|
||||
|
||||
convert_float_short(convertSendBuffer[0],
|
||||
(float *) outerSendBuffer->begin(),
|
||||
powerScaling[0], 2 * resamp_outchunk);
|
||||
|
||||
numSent = mRadio->writeSamples(convertSendBuffer,
|
||||
resamp_outchunk,
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
if (numSent != resamp_outchunk) {
|
||||
LOG(ALERT) << "Transmit error " << numSent;
|
||||
}
|
||||
|
||||
writeTimestamp += resamp_outchunk;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* Written by Thomas Tsou <ttsou@vt.edu>
|
||||
* Based on code by Harvind S Samra <hssamra@kestrelsp.com>
|
||||
*
|
||||
* Copyright 2011, 2012 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -21,9 +21,24 @@
|
||||
|
||||
#include "radioVector.h"
|
||||
|
||||
radioVector::radioVector(const signalVector& wVector, GSM::Time& wTime)
|
||||
: signalVector(wVector), mTime(wTime)
|
||||
radioVector::radioVector(GSM::Time &time, size_t size,
|
||||
size_t start, size_t chans)
|
||||
: vectors(chans), mTime(time)
|
||||
{
|
||||
for (size_t i = 0; i < vectors.size(); i++)
|
||||
vectors[i] = new signalVector(size, start);
|
||||
}
|
||||
|
||||
radioVector::radioVector(GSM::Time& wTime, signalVector *vector)
|
||||
: vectors(1), mTime(wTime)
|
||||
{
|
||||
vectors[0] = vector;
|
||||
}
|
||||
|
||||
radioVector::~radioVector()
|
||||
{
|
||||
for (size_t i = 0; i < vectors.size(); i++)
|
||||
delete vectors[i];
|
||||
}
|
||||
|
||||
GSM::Time radioVector::getTime() const
|
||||
@@ -41,6 +56,52 @@ bool radioVector::operator>(const radioVector& other) const
|
||||
return mTime > other.mTime;
|
||||
}
|
||||
|
||||
signalVector *radioVector::getVector(size_t chan) const
|
||||
{
|
||||
if (chan >= vectors.size())
|
||||
return NULL;
|
||||
|
||||
return vectors[chan];
|
||||
}
|
||||
|
||||
bool radioVector::setVector(signalVector *vector, size_t chan)
|
||||
{
|
||||
if (chan >= vectors.size())
|
||||
return false;
|
||||
|
||||
vectors[chan] = vector;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
noiseVector::noiseVector(size_t size)
|
||||
: std::vector<float>(size), itr(0)
|
||||
{
|
||||
}
|
||||
|
||||
float noiseVector::avg() const
|
||||
{
|
||||
float val = 0.0;
|
||||
|
||||
for (size_t i = 0; i < size(); i++)
|
||||
val += (*this)[i];
|
||||
|
||||
return val / (float) size();
|
||||
}
|
||||
|
||||
bool noiseVector::insert(float val)
|
||||
{
|
||||
if (!size())
|
||||
return false;
|
||||
|
||||
if (itr >= this->size())
|
||||
itr = 0;
|
||||
|
||||
(*this)[itr++] = val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GSM::Time VectorQueue::nextTime() const
|
||||
{
|
||||
GSM::Time retVal;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Written by Thomas Tsou <ttsou@vt.edu>
|
||||
* Based on code by Harvind S Samra <hssamra@kestrelsp.com>
|
||||
*
|
||||
* Copyright 2011, 2012 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -26,20 +26,38 @@
|
||||
#include "GSMCommon.h"
|
||||
#include "Interthread.h"
|
||||
|
||||
class radioVector : public signalVector {
|
||||
class radioVector {
|
||||
public:
|
||||
radioVector(const signalVector& wVector, GSM::Time& wTime);
|
||||
radioVector(GSM::Time& wTime, size_t size = 0,
|
||||
size_t start = 0, size_t chans = 1);
|
||||
|
||||
radioVector(GSM::Time& wTime, signalVector *vector);
|
||||
~radioVector();
|
||||
|
||||
GSM::Time getTime() const;
|
||||
void setTime(const GSM::Time& wTime);
|
||||
bool operator>(const radioVector& other) const;
|
||||
|
||||
signalVector *getVector(size_t chan = 0) const;
|
||||
bool setVector(signalVector *vector, size_t chan = 0);
|
||||
size_t chans() const { return vectors.size(); }
|
||||
private:
|
||||
std::vector<signalVector *> vectors;
|
||||
GSM::Time mTime;
|
||||
};
|
||||
|
||||
class VectorFIFO : public InterthreadQueue<radioVector> {
|
||||
class noiseVector : std::vector<float> {
|
||||
public:
|
||||
noiseVector(size_t size = 0);
|
||||
bool insert(float val);
|
||||
float avg() const;
|
||||
|
||||
private:
|
||||
size_t itr;
|
||||
};
|
||||
|
||||
class VectorFIFO : public InterthreadQueue<radioVector> { };
|
||||
|
||||
class VectorQueue : public InterthreadPriorityQueue<radioVector> {
|
||||
public:
|
||||
GSM::Time nextTime() const;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Transceiver.h"
|
||||
#include "radioDevice.h"
|
||||
#include "DummyLoad.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <GSMCommon.h>
|
||||
#include <Logger.h>
|
||||
#include <Configuration.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
|
||||
|
||||
|
||||
volatile bool gbShutdown = false;
|
||||
static void ctrlCHandler(int signo)
|
||||
{
|
||||
cout << "Received shutdown signal" << endl;;
|
||||
gbShutdown = true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
std::string deviceArgs;
|
||||
std::string txAntenna, rxAntenna;
|
||||
|
||||
if (argc == 3)
|
||||
{
|
||||
deviceArgs = std::string(argv[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceArgs = "";
|
||||
}
|
||||
|
||||
if ( signal( SIGINT, ctrlCHandler ) == SIG_ERR )
|
||||
{
|
||||
cerr << "Couldn't install signal handler for SIGINT" << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( signal( SIGTERM, ctrlCHandler ) == SIG_ERR )
|
||||
{
|
||||
cerr << "Couldn't install signal handler for SIGTERM" << endl;
|
||||
exit(1);
|
||||
}
|
||||
// Configure logger.
|
||||
gLogInit("transceiver",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7);
|
||||
|
||||
int numARFCN=1;
|
||||
|
||||
LOG(NOTICE) << "starting transceiver with " << numARFCN << " ARFCNs (argc=" << argc << ")";
|
||||
|
||||
srandom(time(NULL));
|
||||
|
||||
RadioDevice *usrp = RadioDevice::make(SAMPSPERSYM);
|
||||
int radioType = usrp->open(deviceArgs);
|
||||
if (radioType < 0) {
|
||||
LOG(ALERT) << "Transceiver exiting..." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (gConfig.defines("GSM.Radio.TxAntenna"))
|
||||
txAntenna = gConfig.getStr("GSM.Radio.TxAntenna").c_str();
|
||||
if (gConfig.defines("GSM.Radio.RxAntenna"))
|
||||
rxAntenna = gConfig.getStr("GSM.Radio.RxAntenna").c_str();
|
||||
|
||||
if (txAntenna != "")
|
||||
usrp->setTxAntenna(txAntenna);
|
||||
if (rxAntenna != "")
|
||||
usrp->setRxAntenna(rxAntenna);
|
||||
|
||||
LOG(INFO) << "transceiver using transmit antenna " << usrp->getRxAntenna();
|
||||
LOG(INFO) << "transceiver using receive antenna " << usrp->getTxAntenna();
|
||||
|
||||
RadioInterface* radio;
|
||||
switch (radioType) {
|
||||
case RadioDevice::NORMAL:
|
||||
radio = new RadioInterface(usrp, 3, SAMPSPERSYM, false);
|
||||
break;
|
||||
case RadioDevice::RESAMP:
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported configuration";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int port = gConfig.getNum("TRX.Port");
|
||||
const char *addr = gConfig.getStr("TRX.IP").c_str();
|
||||
DriveLoop *drive = new DriveLoop(SAMPSPERSYM,GSM::Time(3,0),radio);
|
||||
Transceiver *trx = new Transceiver(port, addr, SAMPSPERSYM, radio, drive, 0);
|
||||
radio->activateChan(0);
|
||||
|
||||
/*
|
||||
signalVector *gsmPulse = generateGSMPulse(2,1);
|
||||
BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000";
|
||||
BitVector normalBurst(BitVector(normalBurstSeg,gTrainingSequence[0]),normalBurstSeg);
|
||||
signalVector *modBurst = modulateBurst(normalBurst,*gsmPulse,8,1);
|
||||
signalVector *modBurst9 = modulateBurst(normalBurst,*gsmPulse,9,1);
|
||||
signalVector *interpolationFilter = createLPF(0.6/mOversamplingRate,6*mOversamplingRate,1);
|
||||
signalVector totalBurst1(*modBurst,*modBurst9);
|
||||
signalVector totalBurst2(*modBurst,*modBurst);
|
||||
signalVector totalBurst(totalBurst1,totalBurst2);
|
||||
scaleVector(totalBurst,usrp->fullScaleInputValue());
|
||||
double beaconFreq = -1.0*(numARFCN-1)*200e3;
|
||||
signalVector finalVec(625*mOversamplingRate);
|
||||
for (int j = 0; j < numARFCN; j++) {
|
||||
signalVector *frequencyShifter = new signalVector(625*mOversamplingRate);
|
||||
frequencyShifter->fill(1.0);
|
||||
frequencyShift(frequencyShifter,frequencyShifter,2.0*M_PI*(beaconFreq+j*400e3)/(1625.0e3/6.0*mOversamplingRate));
|
||||
signalVector *interpVec = polyphaseResampleVector(totalBurst,mOversamplingRate,1,interpolationFilter);
|
||||
multVector(*interpVec,*frequencyShifter);
|
||||
addVector(finalVec,*interpVec);
|
||||
}
|
||||
signalVector::iterator itr = finalVec.begin();
|
||||
short finalVecShort[2*finalVec.size()];
|
||||
short *shortItr = finalVecShort;
|
||||
while (itr < finalVec.end()) {
|
||||
*shortItr++ = (short) (itr->real());
|
||||
*shortItr++ = (short) (itr->imag());
|
||||
itr++;
|
||||
}
|
||||
usrp->loadBurst(finalVecShort,finalVec.size());
|
||||
*/
|
||||
trx->start();
|
||||
|
||||
while(!gbShutdown) { sleep(1); }//i++; if (i==60) break;}
|
||||
|
||||
cout << "Shutting down transceiver..." << endl;
|
||||
trx->shutdown();
|
||||
|
||||
delete trx;
|
||||
delete drive;
|
||||
delete radio;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -18,74 +18,49 @@
|
||||
#include "Vector.h"
|
||||
#include "Complex.h"
|
||||
#include "BitVector.h"
|
||||
#include "PRBS.h"
|
||||
#include "signalVector.h"
|
||||
|
||||
/** Indicated signalVector symmetry */
|
||||
enum Symmetry {
|
||||
NONE = 0,
|
||||
ABSSYM = 1
|
||||
};
|
||||
/* Burst lengths */
|
||||
#define NORMAL_BURST_NBITS 148
|
||||
#define EDGE_BURST_NBITS 444
|
||||
#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
|
||||
|
||||
/** Convolution type indicator */
|
||||
enum ConvType {
|
||||
FULL_SPAN = 0,
|
||||
OVERLAP_ONLY = 1,
|
||||
START_ONLY = 2,
|
||||
WITH_TAIL = 3,
|
||||
NO_DELAY = 4,
|
||||
CUSTOM = 5,
|
||||
UNDEFINED = 255
|
||||
START_ONLY,
|
||||
NO_DELAY,
|
||||
CUSTOM,
|
||||
UNDEFINED,
|
||||
};
|
||||
|
||||
/** the core data structure of the Transceiver */
|
||||
class signalVector: public Vector<complex>
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
Symmetry symmetry; ///< the symmetry of the vector
|
||||
bool realOnly; ///< true if vector is real-valued, not complex-valued
|
||||
|
||||
public:
|
||||
|
||||
/** Constructors */
|
||||
signalVector(int dSize=0, Symmetry wSymmetry = NONE):
|
||||
Vector<complex>(dSize),
|
||||
realOnly(false)
|
||||
{
|
||||
symmetry = wSymmetry;
|
||||
};
|
||||
|
||||
signalVector(complex* wData, size_t start,
|
||||
size_t span, Symmetry wSymmetry = NONE):
|
||||
Vector<complex>(NULL,wData+start,wData+start+span),
|
||||
realOnly(false)
|
||||
{
|
||||
symmetry = wSymmetry;
|
||||
};
|
||||
|
||||
signalVector(const signalVector &vec1, const signalVector &vec2):
|
||||
Vector<complex>(vec1,vec2),
|
||||
realOnly(false)
|
||||
{
|
||||
symmetry = vec1.symmetry;
|
||||
};
|
||||
|
||||
signalVector(const signalVector &wVector):
|
||||
Vector<complex>(wVector.size()),
|
||||
realOnly(false)
|
||||
{
|
||||
wVector.copyTo(*this);
|
||||
symmetry = wVector.getSymmetry();
|
||||
};
|
||||
|
||||
/** symmetry operators */
|
||||
Symmetry getSymmetry() const { return symmetry;};
|
||||
void setSymmetry(Symmetry wSymmetry) { symmetry = wSymmetry;};
|
||||
|
||||
/** real-valued operators */
|
||||
bool isRealOnly() const { return realOnly;};
|
||||
void isRealOnly(bool wOnly) { realOnly = wOnly;};
|
||||
/** Codes for burst types of received bursts*/
|
||||
enum CorrType{
|
||||
OFF, ///< timeslot is off
|
||||
TSC, ///< timeslot should contain a normal burst
|
||||
RACH, ///< timeslot should contain an access burst
|
||||
EDGE, ///< timeslot should contain an EDGE burst
|
||||
IDLE ///< timeslot is an idle (or dummy) burst
|
||||
};
|
||||
std::string corrTypeToString(CorrType corr);
|
||||
std::ostream& operator<<(std::ostream& os, CorrType corr);
|
||||
|
||||
enum SignalError {
|
||||
SIGERR_NONE,
|
||||
SIGERR_BOUNDS,
|
||||
SIGERR_CLIP,
|
||||
SIGERR_UNSUPPORTED,
|
||||
SIGERR_INTERNAL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Burst detection threshold
|
||||
*
|
||||
* Decision threshold value for burst gating on peak-to-average value of
|
||||
* correlated synchronization sequences. Lower values pass more bursts up
|
||||
* to upper layers but will increase the false detection rate.
|
||||
*/
|
||||
#define BURST_THRESH 4.0
|
||||
|
||||
/** Convert a linear number to a dB value */
|
||||
float dB(float x);
|
||||
@@ -100,83 +75,89 @@ float vectorNorm2(const signalVector &x);
|
||||
float vectorPower(const signalVector &x);
|
||||
|
||||
/** Setup the signal processing library */
|
||||
void sigProcLibSetup(int samplesPerSymbol);
|
||||
bool sigProcLibSetup();
|
||||
|
||||
/** Destroy the signal processing library */
|
||||
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.
|
||||
/**
|
||||
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,
|
||||
unsigned startIx = 0,
|
||||
unsigned len = 0);
|
||||
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);
|
||||
|
||||
/**
|
||||
Generate the GSM pulse.
|
||||
@param samplesPerSymbol The number of samples per GSM symbol.
|
||||
@param symbolLength The size of the pulse.
|
||||
@return The GSM pulse.
|
||||
*/
|
||||
signalVector* generateGSMPulse(int samplesPerSymbol,
|
||||
int symbolLength);
|
||||
|
||||
/**
|
||||
/**
|
||||
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.
|
||||
@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);
|
||||
signalVector *x,
|
||||
float freq = 0.0,
|
||||
float startPhase = 0.0,
|
||||
float *finalPhase=NULL);
|
||||
|
||||
/**
|
||||
Correlate two vectors.
|
||||
/**
|
||||
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,
|
||||
signalVector *b,
|
||||
signalVector *c,
|
||||
ConvType spanType,
|
||||
bool bReversedConjugated = false,
|
||||
unsigned startIx = 0,
|
||||
unsigned len = 0);
|
||||
unsigned startIx = 0,
|
||||
unsigned len = 0);
|
||||
|
||||
/** Operate soft slicer on real-valued portion of vector */
|
||||
bool vectorSlicer(signalVector *x);
|
||||
/** Operate soft slicer on a soft-bit vector */
|
||||
bool vectorSlicer(SoftVector *x);
|
||||
|
||||
/** GMSK modulate a GSM burst of bits */
|
||||
signalVector *modulateBurst(const BitVector &wBurst,
|
||||
const signalVector &gsmPulse,
|
||||
int guardPeriodLength,
|
||||
int samplesPerSymbol);
|
||||
int guardPeriodLength,
|
||||
int sps, bool emptyPulse = false);
|
||||
|
||||
/** 8-PSK modulate a burst of bits */
|
||||
signalVector *modulateEdgeBurst(const BitVector &bits,
|
||||
int sps, bool emptyPulse = false);
|
||||
|
||||
/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
|
||||
signalVector *generateEdgeBurst(int tsc);
|
||||
|
||||
/** Generate an empty burst - 4 or 1 SPS */
|
||||
signalVector *generateEmptyBurst(int sps, int tn);
|
||||
|
||||
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
|
||||
signalVector *genRandNormalBurst(int tsc, int sps, int tn, PRBS &prbs);
|
||||
|
||||
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
|
||||
signalVector *genRandAccessBurst(int delay, int sps, int tn);
|
||||
|
||||
/** Generate a dummy GSM burst - 4 or 1 SPS */
|
||||
signalVector *generateDummyBurst(int sps, int tn);
|
||||
|
||||
/** Sinc function */
|
||||
float sinc(float x);
|
||||
|
||||
/** Delay a vector */
|
||||
void delayVector(signalVector &wBurst,
|
||||
float delay);
|
||||
signalVector *delayVector(const signalVector *in, signalVector *out, float delay);
|
||||
|
||||
/** Add two vectors in-place */
|
||||
bool addVector(signalVector &x,
|
||||
signalVector &y);
|
||||
signalVector &y);
|
||||
|
||||
/** Multiply two vectors in-place*/
|
||||
bool multVector(signalVector &x,
|
||||
@@ -188,24 +169,24 @@ signalVector *gaussianNoise(int length,
|
||||
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.
|
||||
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);
|
||||
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.
|
||||
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);
|
||||
float *peakIndex,
|
||||
float *avgPwr);
|
||||
|
||||
/**
|
||||
Apply a scalar to a vector.
|
||||
@@ -213,174 +194,140 @@ complex peakDetect(const signalVector &rxBurst,
|
||||
@param scale The scalar.
|
||||
*/
|
||||
void scaleVector(signalVector &x,
|
||||
complex scale);
|
||||
|
||||
/**
|
||||
Add a constant offset to a vecotr.
|
||||
@param x The vector of interest.
|
||||
@param offset The offset.
|
||||
*/
|
||||
void offsetVector(signalVector &x,
|
||||
complex offset);
|
||||
complex scale);
|
||||
|
||||
/**
|
||||
Generate a modulated GSM midamble, stored within the library.
|
||||
@param gsmPulse The GSM pulse used for modulation.
|
||||
@param samplesPerSymbol The number of samples per GSM symbol.
|
||||
@param TSC The training sequence [0..7]
|
||||
@return Success.
|
||||
*/
|
||||
bool generateMidamble(signalVector &gsmPulse,
|
||||
int samplesPerSymbol,
|
||||
int TSC);
|
||||
/**
|
||||
Generate a modulated RACH sequence, stored within the library.
|
||||
@param gsmPulse The GSM pulse used for modulation.
|
||||
@param samplesPerSymbol The number of samples per GSM symbol.
|
||||
@return Success.
|
||||
*/
|
||||
bool generateRACHSequence(signalVector &gsmPulse,
|
||||
int samplesPerSymbol);
|
||||
|
||||
/**
|
||||
Energy detector, checks to see if received burst energy is above a threshold.
|
||||
@param rxBurst The received GSM burst of interest.
|
||||
Rough energy estimator.
|
||||
@param rxBurst A GSM burst.
|
||||
@param windowLength The number of burst samples used to compute burst energy
|
||||
@param detectThreshold The detection threshold, a linear value.
|
||||
@param avgPwr The average power of the received burst.
|
||||
@return True if burst energy is above threshold.
|
||||
@return The average power of the received burst.
|
||||
*/
|
||||
bool energyDetect(signalVector &rxBurst,
|
||||
unsigned windowLength,
|
||||
float detectThreshold,
|
||||
float *avgPwr = NULL);
|
||||
float energyDetect(const signalVector &rxBurst,
|
||||
unsigned windowLength);
|
||||
|
||||
/**
|
||||
RACH correlator/detector.
|
||||
@param rxBurst The received GSM burst of interest.
|
||||
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
|
||||
@param samplesPerSymbol The number of samples per GSM symbol.
|
||||
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.
|
||||
@return True if burst SNR is larger that the detectThreshold value.
|
||||
@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
|
||||
*/
|
||||
bool detectRACHBurst(signalVector &rxBurst,
|
||||
float detectThreshold,
|
||||
int samplesPerSymbol,
|
||||
complex *amplitude,
|
||||
float* TOA);
|
||||
int detectRACHBurst(const signalVector &burst,
|
||||
float threshold,
|
||||
int sps,
|
||||
complex &litude,
|
||||
float &toa,
|
||||
unsigned max_toa);
|
||||
|
||||
/**
|
||||
Normal burst correlator, detector, channel estimator.
|
||||
GMSK Normal Burst correlator/detector.
|
||||
@param rxBurst The received GSM burst of interest.
|
||||
|
||||
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
|
||||
@param samplesPerSymbol The number of samples per GSM symbol.
|
||||
@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 maxTOA The maximum expected time-of-arrival
|
||||
@param requestChannel Set to true if channel estimation is desired.
|
||||
@param channelResponse The estimated channel.
|
||||
@param channelResponseOffset The time offset b/w the first sample of the channel response and the reported TOA.
|
||||
@return True if burst SNR is larger that the detectThreshold value.
|
||||
@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
|
||||
*/
|
||||
bool analyzeTrafficBurst(signalVector &rxBurst,
|
||||
unsigned TSC,
|
||||
float detectThreshold,
|
||||
int samplesPerSymbol,
|
||||
complex *amplitude,
|
||||
float *TOA,
|
||||
unsigned maxTOA,
|
||||
bool requestChannel = false,
|
||||
signalVector** channelResponse = NULL,
|
||||
float *channelResponseOffset = NULL);
|
||||
int analyzeTrafficBurst(const signalVector &burst,
|
||||
unsigned tsc,
|
||||
float threshold,
|
||||
int sps,
|
||||
complex &litude,
|
||||
float &toa,
|
||||
unsigned max_toa);
|
||||
|
||||
/**
|
||||
Decimate a vector.
|
||||
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
|
||||
@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 (in symbols).
|
||||
@param max_toa The maximum expected time-of-arrival (in symbols).
|
||||
@return positive value (CorrType) if threshold value is reached,
|
||||
negative value (-SignalError) on error,
|
||||
zero (SIGERR_NONE) if no burst is detected
|
||||
*/
|
||||
int detectAnyBurst(const signalVector &burst,
|
||||
unsigned tsc,
|
||||
float threshold,
|
||||
int sps,
|
||||
CorrType type,
|
||||
complex &,
|
||||
float &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 decimationFactor The amount of decimation, i.e. the decimation factor.
|
||||
@param factor Decimation factor.
|
||||
@return The decimated signal vector.
|
||||
*/
|
||||
signalVector *decimateVector(signalVector &wVector,
|
||||
int decimationFactor);
|
||||
signalVector *decimateVector(signalVector &wVector, size_t factor);
|
||||
|
||||
/**
|
||||
Demodulates a received burst using a soft-slicer.
|
||||
@param rxBurst The burst to be demodulated.
|
||||
Demodulates a GMSK burst using a soft-slicer.
|
||||
@param rxBurst The burst to be demodulated.
|
||||
@param gsmPulse The GSM pulse.
|
||||
@param samplesPerSymbol The number of samples per GSM symbol.
|
||||
@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 *demodulateBurst(signalVector &rxBurst,
|
||||
const signalVector &gsmPulse,
|
||||
int samplesPerSymbol,
|
||||
complex channel,
|
||||
float TOA);
|
||||
SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps,
|
||||
complex channel, float TOA);
|
||||
|
||||
/**
|
||||
Creates a simple Kaiser-windowed low-pass FIR filter.
|
||||
@param cutoffFreq The digital 3dB bandwidth of the filter.
|
||||
@param filterLen The number of taps in the filter.
|
||||
@param gainDC The DC gain of the filter.
|
||||
@return The desired LPF
|
||||
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.
|
||||
*/
|
||||
signalVector *createLPF(float cutoffFreq,
|
||||
int filterLen,
|
||||
float gainDC = 1.0);
|
||||
SoftVector *demodEdgeBurst(const signalVector &rxBurst, int sps,
|
||||
complex channel, float TOA);
|
||||
|
||||
/**
|
||||
Change sampling rate of a vector via polyphase resampling.
|
||||
@param wVector The vector to be resampled.
|
||||
@param P The numerator, i.e. the amount of upsampling.
|
||||
@param Q The denominator, i.e. the amount of downsampling.
|
||||
@param LPF An optional low-pass filter used in the resampling process.
|
||||
@return A vector resampled at P/Q of the original sampling rate.
|
||||
*/
|
||||
signalVector *polyphaseResampleVector(signalVector &wVector,
|
||||
int P, int Q,
|
||||
signalVector *LPF);
|
||||
|
||||
/**
|
||||
Change the sampling rate of a vector via linear interpolation.
|
||||
@param wVector The vector to be resampled.
|
||||
@param expFactor Ratio of new sampling rate/original sampling rate.
|
||||
@param endPoint ???
|
||||
@return A vector resampled a expFactor*original sampling rate.
|
||||
*/
|
||||
signalVector *resampleVector(signalVector &wVector,
|
||||
float expFactor,
|
||||
complex endPoint);
|
||||
|
||||
/**
|
||||
Design the necessary filters for a decision-feedback equalizer.
|
||||
@param channelResponse The multipath channel that we're mitigating.
|
||||
@param SNRestimate The signal-to-noise estimate of the channel, a linear value
|
||||
@param Nf The number of taps in the feedforward filter.
|
||||
@param feedForwardFilter The designed feed forward filter.
|
||||
@param feedbackFilter The designed feedback filter.
|
||||
@return True if DFE can be designed.
|
||||
*/
|
||||
bool designDFE(signalVector &channelResponse,
|
||||
float SNRestimate,
|
||||
int Nf,
|
||||
signalVector **feedForwardFilter,
|
||||
signalVector **feedbackFilter);
|
||||
|
||||
/**
|
||||
Equalize/demodulate a received burst via a decision-feedback equalizer.
|
||||
@param rxBurst The received burst to be demodulated.
|
||||
@param TOA The time-of-arrival of the received burst.
|
||||
@param samplesPerSymbol The number of samples per GSM symbol.
|
||||
@param w The feed forward filter of the DFE.
|
||||
@param b The feedback filter of the DFE.
|
||||
@return The demodulated bit sequence.
|
||||
*/
|
||||
SoftVector *equalizeBurst(signalVector &rxBurst,
|
||||
float TOA,
|
||||
int samplesPerSymbol,
|
||||
signalVector &w,
|
||||
signalVector &b);
|
||||
/** Demodulate burst basde on type and output soft bits */
|
||||
SoftVector *demodAnyBurst(const signalVector &burst, int sps,
|
||||
complex amp, float toa, CorrType type);
|
||||
|
||||
#endif /* SIGPROCLIB_H */
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2008, 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Contributors:
|
||||
Harvind S. Samra, hssamra@kestrelsp.com
|
||||
*/
|
||||
|
||||
|
||||
#include "sigProcLib.h"
|
||||
//#include "radioInterface.h"
|
||||
#include <Logger.h>
|
||||
#include <Configuration.h>
|
||||
#include <GSMCommon.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
gLogInit("sigProcLibTest","DEBUG");
|
||||
|
||||
int samplesPerSymbol = 1;
|
||||
|
||||
int TSC = 2;
|
||||
|
||||
sigProcLibSetup(samplesPerSymbol);
|
||||
|
||||
signalVector *gsmPulse = generateGSMPulse(2,samplesPerSymbol);
|
||||
cout << *gsmPulse << endl;
|
||||
|
||||
BitVector RACHBurstStart = "01010101";
|
||||
BitVector RACHBurstRest = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
BitVector RACHBurst(BitVector(RACHBurstStart,gRACHSynchSequence),RACHBurstRest);
|
||||
|
||||
|
||||
signalVector *RACHSeq = modulateBurst(RACHBurst,
|
||||
*gsmPulse,
|
||||
9,
|
||||
samplesPerSymbol);
|
||||
|
||||
generateRACHSequence(*gsmPulse,samplesPerSymbol);
|
||||
|
||||
complex a; float t;
|
||||
detectRACHBurst(*RACHSeq, 5, samplesPerSymbol,&a,&t);
|
||||
|
||||
//cout << *RACHSeq << endl;
|
||||
//signalVector *autocorr = correlate(RACHSeq,RACHSeq,NULL,NO_DELAY);
|
||||
|
||||
//cout << *autocorr;
|
||||
|
||||
//exit(1);
|
||||
|
||||
|
||||
/*signalVector x(6500);
|
||||
x.fill(1.0);
|
||||
|
||||
frequencyShift(&x,&x,0.48*M_PI);
|
||||
|
||||
signalVector *y = polyphaseResampleVector(x,96,65,NULL);
|
||||
|
||||
cout << *y << endl;
|
||||
|
||||
exit(1);*/
|
||||
|
||||
//CommSig normalBurstSeg = "0000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000";
|
||||
|
||||
BitVector normalBurst(BitVector(normalBurstSeg,gTrainingSequence[TSC]),normalBurstSeg);
|
||||
|
||||
|
||||
generateMidamble(*gsmPulse,samplesPerSymbol,TSC);
|
||||
|
||||
|
||||
signalVector *modBurst = modulateBurst(normalBurst,*gsmPulse,
|
||||
0,samplesPerSymbol);
|
||||
|
||||
|
||||
//delayVector(*rsVector2,6.932);
|
||||
|
||||
complex ampl = 1;
|
||||
float TOA = 0;
|
||||
|
||||
//modBurst = rsVector2;
|
||||
//delayVector(*modBurst,0.8);
|
||||
|
||||
/*
|
||||
signalVector channelResponse(4);
|
||||
signalVector::iterator c=channelResponse.begin();
|
||||
*c = (complex) 9000.0; c++;
|
||||
*c = (complex) 0.4*9000.0; c++; c++;
|
||||
*c = (complex) -1.2*0;
|
||||
|
||||
signalVector *guhBurst = convolve(modBurst,&channelResponse,NULL,NO_DELAY);
|
||||
delete modBurst; modBurst = guhBurst;
|
||||
*/
|
||||
|
||||
signalVector *chanResp;
|
||||
/*
|
||||
double noisePwr = 0.001/sqrtf(2);
|
||||
signalVector *noise = gaussianNoise(modBurst->size(),noisePwr);
|
||||
*/
|
||||
float chanRespOffset;
|
||||
analyzeTrafficBurst(*modBurst,TSC,8.0,samplesPerSymbol,&l,&TOA,1,true,&chanResp,&chanRespOffset);
|
||||
//addVector(*modBurst,*noise);
|
||||
|
||||
cout << "ampl:" << ampl << endl;
|
||||
cout << "TOA: " << TOA << endl;
|
||||
//cout << "chanResp: " << *chanResp << endl;
|
||||
SoftVector *demodBurst = demodulateBurst(*modBurst,*gsmPulse,samplesPerSymbol,(complex) ampl, TOA);
|
||||
|
||||
cout << *demodBurst << endl;
|
||||
|
||||
/*
|
||||
COUT("chanResp: " << *chanResp);
|
||||
|
||||
signalVector *w,*b;
|
||||
designDFE(*chanResp,1.0/noisePwr,7,&w,&b);
|
||||
COUT("w: " << *w);
|
||||
COUT("b: " << *b);
|
||||
|
||||
|
||||
SoftSig *DFEBurst = equalizeBurst(*modBurst,TOA-chanRespOffset,samplesPerSymbol,*w,*b);
|
||||
COUT("DFEBurst: " << *DFEBurst);
|
||||
|
||||
delete gsmPulse;
|
||||
delete RACHSeq;
|
||||
delete modBurst;
|
||||
delete sendLPF;
|
||||
delete rcvLPF;
|
||||
delete rsVector;
|
||||
//delete rsVector2;
|
||||
delete autocorr;
|
||||
delete chanResp;
|
||||
delete noise;
|
||||
delete demodBurst;
|
||||
delete w;
|
||||
delete b;
|
||||
delete DFEBurst;
|
||||
*/
|
||||
|
||||
sigProcLibDestroy();
|
||||
|
||||
}
|
||||
95
Transceiver52M/signalVector.cpp
Normal file
95
Transceiver52M/signalVector.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "signalVector.h"
|
||||
|
||||
signalVector::signalVector(size_t size)
|
||||
: Vector<complex>(size),
|
||||
real(false), aligned(false), symmetry(NONE)
|
||||
{
|
||||
}
|
||||
|
||||
signalVector::signalVector(size_t size, size_t start)
|
||||
: Vector<complex>(size + start),
|
||||
real(false), aligned(false), symmetry(NONE)
|
||||
{
|
||||
mStart = mData + start;
|
||||
}
|
||||
|
||||
signalVector::signalVector(complex *data, size_t start, size_t span)
|
||||
: Vector<complex>(NULL, data + start, data + start + span),
|
||||
real(false), aligned(false), symmetry(NONE)
|
||||
{
|
||||
}
|
||||
|
||||
signalVector::signalVector(const signalVector &vector)
|
||||
: Vector<complex>(vector.size() + vector.getStart()), aligned(false)
|
||||
{
|
||||
mStart = mData + vector.getStart();
|
||||
vector.copyTo(*this);
|
||||
symmetry = vector.getSymmetry();
|
||||
real = vector.isReal();
|
||||
};
|
||||
|
||||
signalVector::signalVector(const signalVector &vector,
|
||||
size_t start, size_t tail)
|
||||
: Vector<complex>(start + vector.size() + tail), aligned(false)
|
||||
{
|
||||
mStart = mData + start;
|
||||
vector.copyTo(*this);
|
||||
symmetry = vector.getSymmetry();
|
||||
real = vector.isReal();
|
||||
};
|
||||
|
||||
void signalVector::operator=(const signalVector& vector)
|
||||
{
|
||||
resize(vector.size() + vector.getStart());
|
||||
memcpy(mData, vector.mData, bytes());
|
||||
mStart = mData + vector.getStart();
|
||||
}
|
||||
|
||||
signalVector signalVector::segment(size_t start, size_t span)
|
||||
{
|
||||
return signalVector(mData, start, span);
|
||||
}
|
||||
|
||||
size_t signalVector::getStart() const
|
||||
{
|
||||
return mStart - mData;
|
||||
}
|
||||
|
||||
size_t signalVector::updateHistory()
|
||||
{
|
||||
size_t num = getStart();
|
||||
|
||||
memmove(mData, mStart + this->size() - num, num * sizeof(complex));
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
Symmetry signalVector::getSymmetry() const
|
||||
{
|
||||
return symmetry;
|
||||
}
|
||||
|
||||
void signalVector::setSymmetry(Symmetry symmetry)
|
||||
{
|
||||
this->symmetry = symmetry;
|
||||
}
|
||||
|
||||
bool signalVector::isReal() const
|
||||
{
|
||||
return real;
|
||||
}
|
||||
|
||||
void signalVector::isReal(bool wOnly)
|
||||
{
|
||||
real = wOnly;
|
||||
}
|
||||
|
||||
bool signalVector::isAligned() const
|
||||
{
|
||||
return aligned;
|
||||
}
|
||||
|
||||
void signalVector::setAligned(bool aligned)
|
||||
{
|
||||
this->aligned = aligned;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user