mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-01 20:53:35 +00:00
Compare commits
203 Commits
fairwaves/
...
0.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71637150a1 | ||
|
|
2393964b77 | ||
|
|
9ee4df8760 | ||
|
|
c92dad32dd | ||
|
|
61837c0420 | ||
|
|
f83e11fefd | ||
|
|
01aff88ce9 | ||
|
|
11d50d950c | ||
|
|
8bd111c942 | ||
|
|
3808e479aa | ||
|
|
bd45a979f8 | ||
|
|
b7095c7bc5 | ||
|
|
77ce99ac67 | ||
|
|
f58cd8ac83 | ||
|
|
99eb07e232 | ||
|
|
89be118a3b | ||
|
|
6fafd33b13 | ||
|
|
6e55d51747 | ||
|
|
6cae1d7b4b | ||
|
|
28ce315a32 | ||
|
|
10d76b6863 | ||
|
|
708b8b44ae | ||
|
|
cb0fc9b21a | ||
|
|
8639fee504 | ||
|
|
ca46896cfe | ||
|
|
4a25d6b8f6 | ||
|
|
79baee3a8f | ||
|
|
c2ba427b52 | ||
|
|
611212676b | ||
|
|
4ebb289c90 | ||
|
|
2f376a3edf | ||
|
|
2edbe4d366 | ||
|
|
a3694bd303 | ||
|
|
2652f2bc39 | ||
|
|
93d9b114b7 | ||
|
|
2ac788b2c3 | ||
|
|
d36ef2f57b | ||
|
|
caf2abc58f | ||
|
|
de1685f6d7 | ||
|
|
f3837d26f9 | ||
|
|
ddf4743306 | ||
|
|
82f83ced73 | ||
|
|
cff4ed9b4c | ||
|
|
6ec26bb788 | ||
|
|
a1ff991402 | ||
|
|
d09843c692 | ||
|
|
e5448ff972 | ||
|
|
e48c1367dc | ||
|
|
aa60dda99a | ||
|
|
1468a5c3dc | ||
|
|
b0e1bd8c22 | ||
|
|
78e1cd20e2 | ||
|
|
db9c1b54cb | ||
|
|
099a44abfb | ||
|
|
8c80095017 | ||
|
|
d49a6aa136 | ||
|
|
81486e053c | ||
|
|
28d8081e25 | ||
|
|
87ed77b937 | ||
|
|
f9d996813d | ||
|
|
aa5acc953c | ||
|
|
934da48618 | ||
|
|
7c405a0c1f | ||
|
|
4cafb0fa15 | ||
|
|
f611569018 | ||
|
|
354741326c | ||
|
|
d2e5c5694e | ||
|
|
a3dce85ffc | ||
|
|
bb0c68ae61 | ||
|
|
87d158cc2d | ||
|
|
7278a87767 | ||
|
|
63eef9faf2 | ||
|
|
d67bd603e9 | ||
|
|
988a464d5d | ||
|
|
1b6ab7d7ee | ||
|
|
980525c8a9 | ||
|
|
70134a01eb | ||
|
|
1fb0ce67d8 | ||
|
|
8ca237b5c2 | ||
|
|
082bbbf8fe | ||
|
|
3bd763d2a1 | ||
|
|
ee57357682 | ||
|
|
8537b90dbe | ||
|
|
038fd7fd70 | ||
|
|
0cd246c27a | ||
|
|
61fbf2ec95 | ||
|
|
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 |
51
.gitignore
vendored
Normal file
51
.gitignore
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# build results
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
Transceiver52M/osmo-trx
|
||||
|
||||
# tests
|
||||
tests/CommonLibs/BitVectorTest
|
||||
tests/CommonLibs/F16Test
|
||||
tests/CommonLibs/InterthreadTest
|
||||
tests/CommonLibs/LogTest
|
||||
tests/CommonLibs/RegexpTest
|
||||
tests/CommonLibs/SocketsTest
|
||||
tests/CommonLibs/TimevalTest
|
||||
tests/CommonLibs/URLEncodeTest
|
||||
tests/CommonLibs/VectorTest
|
||||
tests/CommonLibs/PRBSTest
|
||||
tests/Transceiver52M/convolve_test
|
||||
|
||||
# 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
|
||||
tests/package.m4
|
||||
tests/testsuite
|
||||
tests/atconfig
|
||||
tests/testsuite.dir
|
||||
tests/testsuite.log
|
||||
|
||||
# vim
|
||||
*.sw?
|
||||
3
.gitreview
Normal file
3
.gitreview
Normal file
@@ -0,0 +1,3 @@
|
||||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=osmo-trx
|
||||
83
AUTHORS
83
AUTHORS
@@ -1,18 +1,18 @@
|
||||
#
|
||||
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
#
|
||||
#
|
||||
# This file is part of GNU Radio
|
||||
#
|
||||
#
|
||||
# GNU Radio 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, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
#
|
||||
# GNU Radio 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, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
@@ -23,34 +23,17 @@ David A. Burgess, dburgess@kestrelsp.com:
|
||||
CLI/CLI.h
|
||||
CommonLibs/Assert.h
|
||||
CommonLibs/BitVector.cpp
|
||||
CommonLibs/BitVectorTest.cpp
|
||||
CommonLibs/Configuration.cpp
|
||||
CommonLibs/Configuration.h
|
||||
CommonLibs/ConfigurationTest.cpp
|
||||
CommonLibs/Interthread.h
|
||||
CommonLibs/InterthreadTest.cpp
|
||||
CommonLibs/LinkedLists.cpp
|
||||
CommonLibs/LinkedLists.h
|
||||
CommonLibs/Regexp.h
|
||||
CommonLibs/RegexpTest.cpp
|
||||
CommonLibs/Sockets.cpp
|
||||
CommonLibs/Sockets.h
|
||||
CommonLibs/SocketsTest.cpp
|
||||
CommonLibs/Threads.cpp
|
||||
CommonLibs/Threads.h
|
||||
CommonLibs/Timeval.cpp
|
||||
CommonLibs/Timeval.h
|
||||
CommonLibs/TimevalTest.cpp
|
||||
CommonLibs/Vector.h
|
||||
CommonLibs/VectorTest.cpp
|
||||
Control/CallControl.cpp
|
||||
Control/ControlCommon.cpp
|
||||
Control/ControlCommon.h
|
||||
Control/FACCHDispatch.cpp
|
||||
Control/MobilityManagement.cpp
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
Control/SDCCHDispatch.cpp
|
||||
GSM/GSM610Tables.cpp
|
||||
GSM/GSM610Tables.h
|
||||
GSM/GSMCommon.cpp
|
||||
@@ -82,29 +65,15 @@ David A. Burgess, dburgess@kestrelsp.com:
|
||||
GSM/GSMTransfer.cpp
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
SIP/SIPEngine.h
|
||||
SIP/SIPInterface.h
|
||||
SMS/SMSMessages.cpp
|
||||
SMS/SMSMessages.h
|
||||
SMS/SMSTransfer.cpp
|
||||
SMS/SMSTransfer.h
|
||||
TRXManager/TRXManager.cpp
|
||||
Transceiver/Complex.h
|
||||
apps/OpenBTS900.cpp
|
||||
apps/OpenBTS850.cpp
|
||||
apps/OpenBTS25c3.cpp
|
||||
tests/AGCHTest.cpp
|
||||
tests/BeaconTest.cpp
|
||||
tests/CallTest.cpp
|
||||
tests/CallTest2.cpp
|
||||
tests/LAPDmTest.cpp
|
||||
tests/LoopbackTest.cpp
|
||||
tests/RegistrationTest.cpp
|
||||
tests/TRXSimulator.cpp
|
||||
tests/CommonLibs/BitVectorTest.cpp
|
||||
tests/CommonLibs/InterthreadTest.cpp
|
||||
tests/CommonLibs/SocketsTest.cpp
|
||||
tests/CommonLibs/TimevalTest.cpp
|
||||
tests/CommonLibs/VectorTest.cpp
|
||||
|
||||
Harvind S. Samra, hssamra@kestrelsp.com:
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
@@ -126,13 +95,6 @@ Harvind S. Samra, hssamra@kestrelsp.com:
|
||||
Transceiver/testRadio.cpp
|
||||
|
||||
Raffi Sevlian, raffisev@gmail.com:
|
||||
Control/CallControl.cpp
|
||||
Control/ControlCommon.cpp
|
||||
Control/ControlCommon.h
|
||||
Control/FACCHDispatch.cpp
|
||||
Control/MobilityManagement.cpp
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
GSM/GSMCommon.h
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSML1FEC.h
|
||||
@@ -157,36 +119,9 @@ Raffi Sevlian, raffisev@gmail.com:
|
||||
GSM/GSMSAPMux.h
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
SIP/SIPEngine.cpp
|
||||
SIP/SIPInterface.cpp
|
||||
SIP/SIPInterface.h
|
||||
SIP/SIPMessage.cpp
|
||||
SIP/SIPMessage.h
|
||||
SIP/SIPUtility.cpp
|
||||
SIP/SIPUtility.h
|
||||
SMS/CMMessage.cpp
|
||||
SMS/CMMessage.h
|
||||
SMS/CMProcessor.cpp
|
||||
SMS/CMProcessor.h
|
||||
SMS/CMTest.cpp
|
||||
SMS/RLMessage.cpp
|
||||
SMS/RLMessage.h
|
||||
SMS/RLProcessor.cpp
|
||||
SMS/RLProcessor.h
|
||||
SMS/SMSMessages.cpp
|
||||
SMS/SMSMessages.h
|
||||
SMS/SMSProcessors.cpp
|
||||
SMS/SMSProcessors.h
|
||||
SMS/SMSTransfer.cpp
|
||||
SMS/SMSTransfer.h
|
||||
SMS/TLMessage.cpp
|
||||
SMS/TLMessage.h
|
||||
SMS/TLProcessor.cpp
|
||||
SMS/TLProcessor.h
|
||||
TRXManager/TRXManager.h
|
||||
|
||||
Alon Levy, alonlevy1@gmail.com
|
||||
RRLPMessages.cpp
|
||||
RRLPMessages.h
|
||||
RRLPTest.cpp
|
||||
|
||||
|
||||
28
COPYING
28
COPYING
@@ -673,16 +673,16 @@ on the AGPLv3 text.
|
||||
=========================================================================
|
||||
|
||||
|
||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS
|
||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX
|
||||
|
||||
|
||||
Permissive Terms Supplementing the License
|
||||
|
||||
1. Remote Interaction Through IP Networks.
|
||||
|
||||
OpenBTS includes an implementation of the GSM network cellular air interface,
|
||||
OsmoTRX is an implementation of the GSM network cellular air interface,
|
||||
as well as other interfaces to IP networks. The interaction of cellular
|
||||
handsets with the OpenBTS software is considered "remote network interaction"
|
||||
handsets with the OsmoTRX software is considered "remote network interaction"
|
||||
for the purposes of the Affero General Public License and cellular users are
|
||||
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
|
||||
Network Interaction; Use with the GNU General Public License").
|
||||
@@ -694,17 +694,6 @@ interfaces other than the GSM air interface from the requirements of Section 13
|
||||
is an additional permission granted to you.
|
||||
|
||||
|
||||
Non-Permissive Terms Supplementing The License
|
||||
|
||||
1. Trademarks.
|
||||
|
||||
"OpenBTS" is a trademark of Range Networks, Inc., registered with
|
||||
the US Patent and Trademark Office. Your use of OpenBTS software under a GPL
|
||||
license does not include the right to use the OpenBTS trademark in commerce.
|
||||
This additional non-permissive term is consistent with Section 7 of the AGPLv3
|
||||
license.
|
||||
|
||||
|
||||
END OF ADDITIONAL TERMS
|
||||
|
||||
|
||||
@@ -712,13 +701,8 @@ END OF ADDITIONAL TERMS
|
||||
How to comply with Section 13 of the AGPLv3 license.
|
||||
|
||||
The recommended method for compliance with Section 13 of the AGPLv3 license is
|
||||
to deliver a text message to each handset that attaches to the OpenBTS cellular
|
||||
network. At a minimum, that text message should include the string "OpenBTS
|
||||
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
|
||||
to deliver a text message to each handset that attaches to the cellular
|
||||
network which uses OsmoTRX. At a minimum, that text message should include the string
|
||||
"OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This
|
||||
message need not be delivered to handsets that are denied registration with the
|
||||
network, since those handsets have been denied service.
|
||||
|
||||
In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message"
|
||||
feature. See the OpenBTS.config.example file for more information on the use of
|
||||
this feature for AGPLv3 compliance.
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -1,88 +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 "BitVector.h"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
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);
|
||||
int r3 = v5.peekField(4,8);
|
||||
cout << r1 << ' ' << r2 << ' ' << r3 << endl;
|
||||
cout << v5 << endl;
|
||||
v5.fillField(0,0xa,4);
|
||||
int r4 = v5.peekField(0,8);
|
||||
cout << v5 << endl;
|
||||
cout << r4 << endl;
|
||||
|
||||
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);
|
||||
cout << "ts=" << ts << endl;
|
||||
tp.unpack(ts);
|
||||
cout << "tp=" << tp << endl;
|
||||
tp.pack(ts);
|
||||
cout << "ts=" << ts << endl;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011, 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 CONFIGURATION_H
|
||||
#define CONFIGURATION_H
|
||||
|
||||
|
||||
#include "sqlite3util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <Threads.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/** A class for configuration file errors. */
|
||||
class ConfigurationTableError {};
|
||||
extern char gCmdName[]; // Gotta be global, gotta be char*, gotta love it.
|
||||
|
||||
/** An exception thrown when a given config key isn't found. */
|
||||
class ConfigurationTableKeyNotFound : public ConfigurationTableError {
|
||||
|
||||
private:
|
||||
|
||||
std::string mKey;
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationTableKeyNotFound(const std::string& wKey)
|
||||
:mKey(wKey)
|
||||
{ }
|
||||
|
||||
const std::string& key() const { return mKey; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ConfigurationRecord {
|
||||
|
||||
private:
|
||||
|
||||
std::string mValue;
|
||||
long mNumber;
|
||||
bool mDefined;
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationRecord(bool wDefined=true):
|
||||
mDefined(wDefined)
|
||||
{ }
|
||||
|
||||
ConfigurationRecord(const std::string& wValue):
|
||||
mValue(wValue),
|
||||
mNumber(strtol(wValue.c_str(),NULL,0)),
|
||||
mDefined(true)
|
||||
{ }
|
||||
|
||||
ConfigurationRecord(const char* wValue):
|
||||
mValue(std::string(wValue)),
|
||||
mNumber(strtol(wValue,NULL,0)),
|
||||
mDefined(true)
|
||||
{ }
|
||||
|
||||
|
||||
const std::string& value() const { return mValue; }
|
||||
long number() const { return mNumber; }
|
||||
bool defined() const { return mDefined; }
|
||||
|
||||
float floatNumber() const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** A string class that uses a hash function for comparison. */
|
||||
class HashString : public std::string {
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
uint64_t mHash;
|
||||
|
||||
void computeHash();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
HashString(const char* src)
|
||||
:std::string(src)
|
||||
{
|
||||
computeHash();
|
||||
}
|
||||
|
||||
HashString(const std::string& src)
|
||||
:std::string(src)
|
||||
{
|
||||
computeHash();
|
||||
}
|
||||
|
||||
HashString()
|
||||
{
|
||||
mHash=0;
|
||||
}
|
||||
|
||||
HashString& operator=(std::string& src)
|
||||
{
|
||||
std::string::operator=(src);
|
||||
computeHash();
|
||||
return *this;
|
||||
}
|
||||
|
||||
HashString& operator=(const char* src)
|
||||
{
|
||||
std::string::operator=(src);
|
||||
computeHash();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const HashString& other)
|
||||
{
|
||||
return mHash==other.mHash;
|
||||
}
|
||||
|
||||
bool operator<(const HashString& other)
|
||||
{
|
||||
return mHash<other.mHash;
|
||||
}
|
||||
|
||||
bool operator>(const HashString& other)
|
||||
{
|
||||
return mHash<other.mHash;
|
||||
}
|
||||
|
||||
uint64_t hash() const { return mHash; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap;
|
||||
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
|
||||
class ConfigurationKey;
|
||||
typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap;
|
||||
|
||||
/**
|
||||
A class for maintaining a configuration key-value table,
|
||||
based on sqlite3 and a local map-based cache.
|
||||
Thread-safe, too.
|
||||
*/
|
||||
class ConfigurationTable {
|
||||
|
||||
private:
|
||||
|
||||
sqlite3* mDB; ///< database connection
|
||||
ConfigurationMap mCache; ///< cache of recently access configuration values
|
||||
mutable Mutex mLock; ///< control for multithreaded access to the cache
|
||||
std::vector<std::string> (*mCrossCheck)(const std::string&); ///< cross check callback pointer
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic
|
||||
|
||||
ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap());
|
||||
|
||||
/** Generate an up-to-date example sql file for new installs. */
|
||||
std::string getDefaultSQL(const std::string& program, const std::string& version);
|
||||
|
||||
/** Generate an up-to-date TeX snippet. */
|
||||
std::string getTeX(const std::string& program, const std::string& version);
|
||||
|
||||
/** Return true if the key is used in the table. */
|
||||
bool defines(const std::string& key);
|
||||
|
||||
/** Return true if the application's schema knows about this key. */
|
||||
bool keyDefinedInSchema(const std::string& name);
|
||||
|
||||
/** Return true if the provided value validates correctly against the defined schema. */
|
||||
bool isValidValue(const std::string& name, const std::string& val);
|
||||
|
||||
/** Return true if the provided value validates correctly against the defined schema. */
|
||||
bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); }
|
||||
|
||||
/** Return a map of all similar keys in the defined schema. */
|
||||
ConfigurationKeyMap getSimilarKeys(const std::string& snippet);
|
||||
|
||||
/** Return true if this key is identified as static. */
|
||||
bool isStatic(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a string parameter from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
std::string getStr(const std::string& key);
|
||||
|
||||
|
||||
/**
|
||||
Get a boolean from the table.
|
||||
Return false if NULL or 0, true otherwise.
|
||||
*/
|
||||
bool getBool(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a numeric parameter from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
long getNum(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a vector of strings from the table.
|
||||
*/
|
||||
std::vector<std::string> getVectorOfStrings(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a float from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
float getFloat(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a numeric vector from the table.
|
||||
*/
|
||||
std::vector<unsigned> getVector(const std::string& key);
|
||||
|
||||
/** Get length of a vector */
|
||||
unsigned getVectorLength(const std::string &key)
|
||||
{ return getVector(key).size(); }
|
||||
|
||||
/** Set or change a value in the table. */
|
||||
bool set(const std::string& key, const std::string& value);
|
||||
|
||||
/** Set or change a value in the table. */
|
||||
bool set(const std::string& key, long value);
|
||||
|
||||
/** Create an entry in the table, no value though. */
|
||||
bool set(const std::string& key);
|
||||
|
||||
/**
|
||||
Remove an entry from the table.
|
||||
Will not alter required values.
|
||||
@param key The key of the item to be removed.
|
||||
@return true if anything was actually removed.
|
||||
*/
|
||||
bool remove(const std::string& key);
|
||||
|
||||
/** Search the table, dumping to a stream. */
|
||||
void find(const std::string& pattern, std::ostream&) const;
|
||||
|
||||
/** Return all key/value pairs stored in the ConfigurationTable */
|
||||
ConfigurationRecordMap getAllPairs() const;
|
||||
|
||||
/** Define the callback to purge the cache whenever the database changes. */
|
||||
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
|
||||
|
||||
/** Define the callback for cross checking. */
|
||||
void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&));
|
||||
|
||||
/** Execute the application specific value cross checking logic. */
|
||||
std::vector<std::string> crossCheck(const std::string& key);
|
||||
|
||||
/** purege cache if it exceeds a certain age */
|
||||
void checkCacheAge();
|
||||
|
||||
/** Delete all records from the cache. */
|
||||
void purge();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
Attempt to lookup a record, cache if needed.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
Caller should hold mLock because the returned reference points into the cache.
|
||||
*/
|
||||
const ConfigurationRecord& lookup(const std::string& key);
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<HashString, std::string> HashStringMap;
|
||||
|
||||
class SimpleKeyValue {
|
||||
|
||||
protected:
|
||||
|
||||
HashStringMap mMap;
|
||||
|
||||
public:
|
||||
|
||||
/** Take a C string "A=B" and set map["A"]="B". */
|
||||
void addItem(const char*);
|
||||
|
||||
/** Take a C string "A=B C=D E=F ..." and add all of the pairs to the map. */
|
||||
void addItems(const char*s);
|
||||
|
||||
/** Return a reference to the string at map["key"]. */
|
||||
const char* get(const char*) const;
|
||||
};
|
||||
|
||||
|
||||
class ConfigurationKey {
|
||||
|
||||
public:
|
||||
|
||||
enum VisibilityLevel
|
||||
{
|
||||
CUSTOMER,
|
||||
CUSTOMERSITE,
|
||||
CUSTOMERTUNE,
|
||||
CUSTOMERWARN,
|
||||
DEVELOPER,
|
||||
FACTORY
|
||||
};
|
||||
|
||||
enum Type
|
||||
{
|
||||
BOOLEAN,
|
||||
CHOICE_OPT,
|
||||
CHOICE,
|
||||
CIDR_OPT,
|
||||
CIDR,
|
||||
FILEPATH_OPT,
|
||||
FILEPATH,
|
||||
IPADDRESS_OPT,
|
||||
IPADDRESS,
|
||||
IPANDPORT,
|
||||
MIPADDRESS_OPT,
|
||||
MIPADDRESS,
|
||||
PORT_OPT,
|
||||
PORT,
|
||||
REGEX_OPT,
|
||||
REGEX,
|
||||
STRING_OPT,
|
||||
STRING,
|
||||
VALRANGE
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
std::string mName;
|
||||
std::string mDefaultValue;
|
||||
std::string mUnits;
|
||||
VisibilityLevel mVisibility;
|
||||
Type mType;
|
||||
std::string mValidValues;
|
||||
bool mIsStatic;
|
||||
std::string mDescription;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription):
|
||||
mName(wName),
|
||||
mDefaultValue(wDefaultValue),
|
||||
mUnits(wUnits),
|
||||
mVisibility(wVisibility),
|
||||
mType(wType),
|
||||
mValidValues(wValidValues),
|
||||
mIsStatic(wIsStatic),
|
||||
mDescription(wDescription)
|
||||
{ }
|
||||
|
||||
ConfigurationKey()
|
||||
{ }
|
||||
|
||||
const std::string& getName() const { return mName; }
|
||||
const std::string& getDefaultValue() const { return mDefaultValue; }
|
||||
void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; }
|
||||
void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); }
|
||||
const std::string& getUnits() const { return mUnits; }
|
||||
const VisibilityLevel& getVisibility() const { return mVisibility; }
|
||||
const Type& getType() const { return mType; }
|
||||
const std::string& getValidValues() const { return mValidValues; }
|
||||
bool isStatic() const { return mIsStatic; }
|
||||
const std::string& getDescription() const { return mDescription; }
|
||||
|
||||
static bool isValidIP(const std::string& ip);
|
||||
static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping);
|
||||
template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger);
|
||||
static const std::string visibilityLevelToString(const VisibilityLevel& visibility);
|
||||
static const std::string typeToString(const ConfigurationKey::Type& type);
|
||||
static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os);
|
||||
static void printDescription(const ConfigurationKey &key, std::ostream& os);
|
||||
static const std::string getARFCNsString();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 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 "Configuration.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ConfigurationKeyMap getConfigurationKeys();
|
||||
ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys());
|
||||
|
||||
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
|
||||
{
|
||||
//cout << "update hook" << endl;
|
||||
gConfig.purge();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
gConfig.setUpdateHook(purgeConfig);
|
||||
|
||||
char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
gConfig.set(keys[i],i);
|
||||
}
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
cout << "table[" << keys[i] << "]=" << gConfig.getStr(keys[i]) << endl;
|
||||
cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl;
|
||||
}
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl;
|
||||
}
|
||||
|
||||
gConfig.set("key5","100 200 300 400 ");
|
||||
std::vector<unsigned> vect = gConfig.getVector("key5");
|
||||
cout << "vect length " << vect.size() << ": ";
|
||||
for (unsigned i=0; i<vect.size(); i++) cout << " " << vect[i];
|
||||
cout << endl;
|
||||
std::vector<string> svect = gConfig.getVectorOfStrings("key5");
|
||||
cout << "vect length " << svect.size() << ": ";
|
||||
for (unsigned i=0; i<svect.size(); i++) cout << " " << svect[i] << ":";
|
||||
cout << endl;
|
||||
|
||||
cout << "bool " << gConfig.getBool("booltest") << endl;
|
||||
gConfig.set("booltest",1);
|
||||
cout << "bool " << gConfig.getBool("booltest") << endl;
|
||||
gConfig.set("booltest",0);
|
||||
cout << "bool " << gConfig.getBool("booltest") << endl;
|
||||
|
||||
gConfig.getStr("newstring");
|
||||
gConfig.getNum("numnumber");
|
||||
|
||||
|
||||
SimpleKeyValue pairs;
|
||||
pairs.addItems(" a=1 b=34 dd=143 ");
|
||||
cout<< pairs.get("a") << endl;
|
||||
cout<< pairs.get("b") << endl;
|
||||
cout<< pairs.get("dd") << endl;
|
||||
|
||||
gConfig.set("fkey","123.456");
|
||||
float fval = gConfig.getFloat("fkey");
|
||||
cout << "fkey " << fval << endl;
|
||||
|
||||
cout << "search fkey:" << endl;
|
||||
gConfig.find("fkey",cout);
|
||||
cout << "search fkey:" << endl;
|
||||
gConfig.find("fkey",cout);
|
||||
gConfig.remove("fkey");
|
||||
cout << "search fkey:" << endl;
|
||||
gConfig.find("fkey",cout);
|
||||
|
||||
try {
|
||||
gConfig.getNum("supposedtoabort");
|
||||
} catch (ConfigurationTableKeyNotFound) {
|
||||
cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurationKeyMap getConfigurationKeys()
|
||||
{
|
||||
ConfigurationKeyMap map;
|
||||
ConfigurationKey *tmp;
|
||||
|
||||
tmp = new ConfigurationKey("booltest","0",
|
||||
"",
|
||||
ConfigurationKey::DEVELOPER,
|
||||
ConfigurationKey::BOOLEAN,
|
||||
"",
|
||||
false,
|
||||
""
|
||||
);
|
||||
map[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
|
||||
tmp = new ConfigurationKey("numnumber","42",
|
||||
"",
|
||||
ConfigurationKey::DEVELOPER,
|
||||
ConfigurationKey::VALRANGE,
|
||||
"0-100",
|
||||
false,
|
||||
""
|
||||
);
|
||||
map[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
|
||||
tmp = new ConfigurationKey("newstring","new string value",
|
||||
"",
|
||||
ConfigurationKey::DEVELOPER,
|
||||
ConfigurationKey::STRING,
|
||||
"",
|
||||
false,
|
||||
""
|
||||
);
|
||||
map[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
|
||||
return map;
|
||||
}
|
||||
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,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 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 <iostream>
|
||||
#include <iterator>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Configuration.h"
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
//ConfigurationTable gConfig("example.config");
|
||||
|
||||
void printAlarms()
|
||||
{
|
||||
std::ostream_iterator<std::string> output( std::cout, "\n" );
|
||||
std::list<std::string> alarms = gGetLoggerAlarms();
|
||||
std::cout << "# alarms = " << alarms.size() << std::endl;
|
||||
std::copy( alarms.begin(), alarms.end(), output );
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
gLogInit("LogTest","NOTICE",LOG_LOCAL7);
|
||||
|
||||
LOG(EMERG) << " testing the logger.";
|
||||
LOG(ALERT) << " testing the logger.";
|
||||
LOG(CRIT) << " testing the logger.";
|
||||
LOG(ERR) << " testing the logger.";
|
||||
LOG(WARNING) << " testing the logger.";
|
||||
LOG(NOTICE) << " testing the logger.";
|
||||
LOG(INFO) << " testing the logger.";
|
||||
LOG(DEBUG) << " testing the logger.";
|
||||
std::cout << "\n\n\n";
|
||||
std::cout << "testing Alarms\n";
|
||||
std::cout << "you should see three lines:" << std::endl;
|
||||
printAlarms();
|
||||
std::cout << "----------- generating 20 alarms ----------" << std::endl;
|
||||
for (int i = 0 ; i < 20 ; ++i) {
|
||||
LOG(ALERT) << i;
|
||||
}
|
||||
std::cout << "you should see ten lines with the numbers 10..19:" << std::endl;
|
||||
printAlarms();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,47 +30,23 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h> // For gettimeofday
|
||||
|
||||
#include "Configuration.h"
|
||||
#include "Logger.h"
|
||||
#include "Threads.h" // pat added
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Reference to a global config table, used all over the system.
|
||||
extern ConfigurationTable gConfig;
|
||||
|
||||
|
||||
/**@ The global alarms table. */
|
||||
//@{
|
||||
Mutex alarmsLock;
|
||||
list<string> alarmsList;
|
||||
void addAlarm(const string&);
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
// (pat) If Log messages are printed before the classes in this module are inited
|
||||
// (which happens when static classes have constructors that do work)
|
||||
// the OpenBTS just crashes.
|
||||
// Prevent that by setting sLoggerInited to true when this module is inited.
|
||||
static bool sLoggerInited = 0;
|
||||
static struct CheckLoggerInitStatus {
|
||||
CheckLoggerInitStatus() { sLoggerInited = 1; }
|
||||
} sCheckloggerInitStatus;
|
||||
|
||||
Mutex gLogToLock;
|
||||
|
||||
// Global log level threshold:
|
||||
int config_log_level;
|
||||
|
||||
/** Names of the logging levels. */
|
||||
const char *levelNames[] = {
|
||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||
};
|
||||
int numLevels = 8;
|
||||
bool gLogToConsole = true;
|
||||
bool gLogToSyslog = false;
|
||||
FILE *gLogToFile = NULL;
|
||||
Mutex gLogToLock;
|
||||
|
||||
|
||||
int levelStringToInt(const string& name)
|
||||
@@ -91,144 +67,48 @@ int levelStringToInt(const string& name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Given a string, return the corresponding level name. */
|
||||
int lookupLevel(const string& key)
|
||||
static std::string format(const char *fmt, ...)
|
||||
{
|
||||
string val = gConfig.getStr(key);
|
||||
int level = levelStringToInt(val);
|
||||
|
||||
if (level == -1) {
|
||||
string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
|
||||
level = levelStringToInt(defaultLevel);
|
||||
_LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
|
||||
gConfig.set(key, defaultLevel);
|
||||
}
|
||||
|
||||
return level;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
int getLoggingLevel(const char* filename)
|
||||
const std::string timestr()
|
||||
{
|
||||
// Default level?
|
||||
if (!filename) return lookupLevel("Log.Level");
|
||||
|
||||
// This can afford to be inefficient since it is not called that often.
|
||||
const string keyName = string("Log.Level.") + string(filename);
|
||||
if (gConfig.defines(keyName)) return lookupLevel(keyName);
|
||||
return lookupLevel("Log.Level");
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int gGetLoggingLevel(const char* filename)
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
|
||||
{
|
||||
// This is called a lot and needs to be efficient.
|
||||
|
||||
static Mutex sLogCacheLock;
|
||||
static map<uint64_t,int> sLogCache;
|
||||
static unsigned sCacheCount;
|
||||
static const unsigned sCacheRefreshCount = 1000;
|
||||
|
||||
if (filename==NULL) return gGetLoggingLevel("");
|
||||
|
||||
HashString hs(filename);
|
||||
uint64_t key = hs.hash();
|
||||
|
||||
sLogCacheLock.lock();
|
||||
// Time for a cache flush?
|
||||
if (sCacheCount>sCacheRefreshCount) {
|
||||
sLogCache.clear();
|
||||
sCacheCount=0;
|
||||
}
|
||||
// Is it cached already?
|
||||
map<uint64_t,int>::const_iterator where = sLogCache.find(key);
|
||||
sCacheCount++;
|
||||
if (where!=sLogCache.end()) {
|
||||
int retVal = where->second;
|
||||
sLogCacheLock.unlock();
|
||||
return retVal;
|
||||
}
|
||||
// Look it up in the config table and cache it.
|
||||
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
|
||||
// (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
|
||||
sLogCacheLock.unlock();
|
||||
int level = getLoggingLevel(filename);
|
||||
sLogCacheLock.lock();
|
||||
sLogCache.insert(pair<uint64_t,int>(key,level));
|
||||
sLogCacheLock.unlock();
|
||||
return level;
|
||||
return os << ss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// copies the alarm list and returns it. list supposed to be small.
|
||||
list<string> gGetLoggerAlarms()
|
||||
{
|
||||
alarmsLock.lock();
|
||||
list<string> ret;
|
||||
// excuse the "complexity", but to use std::copy with a list you need
|
||||
// an insert_iterator - copy technically overwrites, doesn't insert.
|
||||
insert_iterator< list<string> > ii(ret, ret.begin());
|
||||
copy(alarmsList.begin(), alarmsList.end(), ii);
|
||||
alarmsLock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Add an alarm to the alarm list. */
|
||||
void addAlarm(const string& s)
|
||||
{
|
||||
alarmsLock.lock();
|
||||
alarmsList.push_back(s);
|
||||
unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
|
||||
while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
|
||||
alarmsLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
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_ERR) {
|
||||
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||
cerr << mStream.str() << endl;
|
||||
}
|
||||
// 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');
|
||||
ScopedLock lock(gLogToLock);
|
||||
if (gLogToConsole) {
|
||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||
// so just use std::cout.
|
||||
std::cout << mStream.str();
|
||||
if (neednl) std::cout<<"\n";
|
||||
}
|
||||
if (gLogToFile) {
|
||||
fputs(mStream.str().c_str(),gLogToFile);
|
||||
if (neednl) {fputc('\n',gLogToFile);}
|
||||
fflush(gLogToFile);
|
||||
}
|
||||
}
|
||||
|
||||
int mlen = mStream.str().size();
|
||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||
ScopedLock lock(gLogToLock);
|
||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||
// so just use std::cout.
|
||||
std::cout << mStream.str();
|
||||
if (neednl) std::cout<<"\n";
|
||||
}
|
||||
|
||||
|
||||
Log::Log(const char* name, const char* level, int facility)
|
||||
{
|
||||
mDummyInit = true;
|
||||
gLogInit(name, level, facility);
|
||||
}
|
||||
|
||||
|
||||
ostringstream& Log::get()
|
||||
{
|
||||
assert(mPriority<numLevels);
|
||||
@@ -238,41 +118,11 @@ ostringstream& Log::get()
|
||||
|
||||
|
||||
|
||||
void gLogInit(const char* name, const char* level, int facility)
|
||||
void gLogInit(const char* level, char *fn)
|
||||
{
|
||||
// Set the level if one has been specified.
|
||||
if (level) {
|
||||
gConfig.set("Log.Level",level);
|
||||
}
|
||||
|
||||
// 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==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.
|
||||
if (gLogToFile) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
fprintf(gLogToFile,"Starting at %s",ctime(&now));
|
||||
fflush(gLogToFile);
|
||||
std::cout << "Logging to file: " << fn << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the log connection.
|
||||
openlog(name,0,facility);
|
||||
}
|
||||
|
||||
|
||||
void gLogEarly(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsyslog(level | LOG_USER, fmt, args);
|
||||
va_end(args);
|
||||
if (level)
|
||||
config_log_level = levelStringToInt(level);
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
|
||||
@@ -32,19 +32,27 @@
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <syslog.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
extern int config_log_level;
|
||||
|
||||
#define LOG_EMERG 0 /* system is unusable */
|
||||
#define LOG_ALERT 1 /* action must be taken immediately */
|
||||
#define LOG_CRIT 2 /* critical conditions */
|
||||
#define LOG_ERR 3 /* error conditions */
|
||||
#define LOG_WARNING 4 /* warning conditions */
|
||||
#define LOG_NOTICE 5 /* normal but significant condition */
|
||||
#define LOG_INFO 6 /* informational */
|
||||
#define LOG_DEBUG 7 /* debug-level messages */
|
||||
|
||||
#define _LOG(level) \
|
||||
Log(LOG_##level).get() << pthread_self() \
|
||||
<< timestr() << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
|
||||
|
||||
#define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel)
|
||||
#define IS_LOG_LEVEL(wLevel) (config_log_level>=LOG_##wLevel)
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define LOG(wLevel) \
|
||||
@@ -54,41 +62,8 @@
|
||||
if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
|
||||
#endif
|
||||
|
||||
// pat: And for your edification here are the 'levels' as defined in syslog.h:
|
||||
// LOG_EMERG 0 system is unusable
|
||||
// LOG_ALERT 1 action must be taken immediately
|
||||
// LOG_CRIT 2 critical conditions
|
||||
// LOG_ERR 3 error conditions
|
||||
// LOG_WARNING 4 warning conditions
|
||||
// LOG_NOTICE 5 normal, but significant, condition
|
||||
// LOG_INFO 6 informational message
|
||||
// LOG_DEBUG 7 debug-level message
|
||||
|
||||
// (pat) added - print out a var and its name.
|
||||
// Use like this: int descriptive_name; LOG(INFO)<<LOGVAR(descriptive_name);
|
||||
#define LOGVAR2(name,val) " " << name << "=" << (val)
|
||||
#define LOGVAR(var) (" " #var "=") << var
|
||||
#define LOGHEX(var) (" " #var "=0x") << hex << ((unsigned)var) << dec
|
||||
#define LOGHEX2(name,val) " " << name << "=0x" << hex << ((unsigned)(val)) << dec
|
||||
// These are kind of cheesy, but you can use for bitvector
|
||||
#define LOGBV2(name,val) " " << name << "=(" << val<<" size:"<<val.size()<<")"
|
||||
#define LOGBV(bv) LOGBV2(#bv,bv)
|
||||
#define LOGVARRANGE(name,cur,lo,hi) " "<<name <<"=("<<(cur) << " range:"<<(lo) << " to "<<(hi) <<")"
|
||||
|
||||
|
||||
#define OBJLOG(wLevel) \
|
||||
LOG(wLevel) << "obj: " << this << ' '
|
||||
|
||||
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
|
||||
|
||||
|
||||
#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.
|
||||
Derived from Dr. Dobb's Sept. 2007 issue.
|
||||
Updated to use syslog.
|
||||
This object is NOT the global logger;
|
||||
every log record is an object of this class.
|
||||
*/
|
||||
@@ -100,38 +75,27 @@ class Log {
|
||||
|
||||
std::ostringstream mStream; ///< This is where we buffer up the log entry.
|
||||
int mPriority; ///< Priority of current report.
|
||||
bool mDummyInit;
|
||||
|
||||
public:
|
||||
|
||||
Log(int wPriority)
|
||||
:mPriority(wPriority), mDummyInit(false)
|
||||
:mPriority(wPriority)
|
||||
{ }
|
||||
|
||||
Log(const char* name, const char* level=NULL, int facility=LOG_USER);
|
||||
|
||||
// Most of the work is in the destructor.
|
||||
/** The destructor actually generates the log entry. */
|
||||
~Log();
|
||||
|
||||
std::ostringstream& get();
|
||||
};
|
||||
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. */
|
||||
//@{
|
||||
/** Initialize the global logging system. */
|
||||
void gLogInit(const char* name, const char* level=NULL, int facility=LOG_USER);
|
||||
/** Get the logging level associated with a given file. */
|
||||
int gGetLoggingLevel(const char *filename=NULL);
|
||||
/** Allow early logging when still in constructors */
|
||||
void gLogEarly(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
void gLogInit(const char* level=NULL, char* fn=NULL);
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
|
||||
AM_CXXFLAGS = -Wall -O3 -g -lpthread
|
||||
|
||||
EXTRA_DIST = \
|
||||
example.config \
|
||||
@@ -36,78 +36,15 @@ libcommon_la_SOURCES = \
|
||||
Sockets.cpp \
|
||||
Threads.cpp \
|
||||
Timeval.cpp \
|
||||
Reporting.cpp \
|
||||
Logger.cpp \
|
||||
Configuration.cpp \
|
||||
sqlite3util.cpp \
|
||||
URLEncode.cpp \
|
||||
Utils.cpp
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
BitVectorTest \
|
||||
InterthreadTest \
|
||||
SocketsTest \
|
||||
TimevalTest \
|
||||
RegexpTest \
|
||||
VectorTest \
|
||||
ConfigurationTest \
|
||||
LogTest \
|
||||
URLEncodeTest \
|
||||
F16Test
|
||||
|
||||
# ReportingTest
|
||||
Logger.cpp
|
||||
|
||||
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)
|
||||
|
||||
InterthreadTest_SOURCES = InterthreadTest.cpp
|
||||
InterthreadTest_LDADD = libcommon.la
|
||||
InterthreadTest_LDFLAGS = -lpthread
|
||||
|
||||
SocketsTest_SOURCES = SocketsTest.cpp
|
||||
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
|
||||
|
||||
ConfigurationTest_SOURCES = ConfigurationTest.cpp
|
||||
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
# ReportingTest_SOURCES = ReportingTest.cpp
|
||||
# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
LogTest_SOURCES = LogTest.cpp
|
||||
LogTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
F16Test_SOURCES = F16Test.cpp
|
||||
|
||||
MOSTLYCLEANFILES += testSource testDestination
|
||||
|
||||
|
||||
Logger.h
|
||||
|
||||
@@ -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
|
||||
@@ -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,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
|
||||
@@ -187,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);
|
||||
@@ -218,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;
|
||||
}
|
||||
|
||||
@@ -227,18 +223,18 @@ int DatagramSocket::read(char* buffer, unsigned timeout)
|
||||
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort)
|
||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort);
|
||||
open(wSrcPort, wSrcIP);
|
||||
}
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort,
|
||||
const char * wDestIP, unsigned short wDestPort )
|
||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
|
||||
const char *wDestIP, unsigned short wDestPort)
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort);
|
||||
open(wSrcPort, wSrcIP);
|
||||
destination(wDestPort, wDestIP);
|
||||
}
|
||||
|
||||
@@ -250,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
|
||||
}
|
||||
|
||||
|
||||
void UDPSocket::open(unsigned short localPort)
|
||||
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
||||
@@ -269,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 = inet_addr(wlocalIP);
|
||||
address.sin_port = htons(localPort);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
@@ -288,50 +284,4 @@ unsigned short UDPSocket::port() const
|
||||
return ntohs(name.sin_port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UDDSocket::UDDSocket(const char* localPath, const char* remotePath)
|
||||
:DatagramSocket()
|
||||
{
|
||||
if (localPath!=NULL) open(localPath);
|
||||
if (remotePath!=NULL) destination(remotePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDDSocket::open(const char* localPath)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0);
|
||||
if (mSocketFD<0) {
|
||||
perror("socket() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
|
||||
// bind
|
||||
struct sockaddr_un address;
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sun_family = AF_UNIX;
|
||||
strcpy(address.sun_path,localPath);
|
||||
unlink(localPath);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDDSocket::destination(const char* remotePath)
|
||||
{
|
||||
struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination;
|
||||
strcpy(unAddr->sun_path,remotePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim:ts=4:sw=4
|
||||
|
||||
@@ -108,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.
|
||||
@@ -116,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. */
|
||||
@@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket {
|
||||
public:
|
||||
|
||||
/** Open a USP socket with an OS-assigned port and no default destination. */
|
||||
UDPSocket( unsigned short localPort=0);
|
||||
UDPSocket(const char *localIP, unsigned short localPort);
|
||||
|
||||
/** Given a full specification, open the socket and set the dest address. */
|
||||
UDPSocket( unsigned short localPort,
|
||||
const char * remoteIP, unsigned short remotePort);
|
||||
UDPSocket(const char *localIP, unsigned short localPort,
|
||||
const char *remoteIP, unsigned short remotePort);
|
||||
|
||||
/** Set the destination port. */
|
||||
void destination( unsigned short wDestPort, const char * wDestIP );
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
unsigned short port() const;
|
||||
|
||||
/** Open and bind the UDP socket to a local port. */
|
||||
void open(unsigned short localPort=0);
|
||||
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
||||
@@ -166,26 +166,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** Unix Domain Datagram Socket */
|
||||
class UDDSocket : public DatagramSocket {
|
||||
|
||||
public:
|
||||
|
||||
UDDSocket(const char* localPath=NULL, const char* remotePath=NULL);
|
||||
|
||||
void destination(const char* remotePath);
|
||||
|
||||
void open(const char* localPath);
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; }
|
||||
|
||||
size_t addressSize() const { return sizeof(struct sockaddr_un); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -111,8 +118,8 @@ template <class T> class Vector {
|
||||
/** Build an empty Vector of a given size. */
|
||||
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
||||
|
||||
/** Build a Vector by shifting the data block. */
|
||||
Vector(Vector<T>& other)
|
||||
/** Build a Vector by moving another. */
|
||||
Vector(Vector<T>&& other)
|
||||
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
||||
{ other.mData=NULL; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqlite3util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
// Wrappers to sqlite operations.
|
||||
// These will eventually get moved to commonlibs.
|
||||
|
||||
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query)
|
||||
{
|
||||
int src = SQLITE_BUSY;
|
||||
while (src==SQLITE_BUSY) {
|
||||
src = sqlite3_prepare_v2(DB,query,strlen(query),stmt,NULL);
|
||||
if (src==SQLITE_BUSY) {
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
if (src) {
|
||||
fprintf(stderr,"sqlite3_prepare_v2 failed for \"%s\": %s\n",query,sqlite3_errmsg(DB));
|
||||
sqlite3_finalize(*stmt);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt)
|
||||
{
|
||||
int src = SQLITE_BUSY;
|
||||
while (src==SQLITE_BUSY) {
|
||||
src = sqlite3_step(stmt);
|
||||
if (src==SQLITE_BUSY) {
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
if ((src!=SQLITE_DONE) && (src!=SQLITE_ROW)) {
|
||||
fprintf(stderr,"sqlite3_run_query failed: %s: %s\n", sqlite3_sql(stmt), sqlite3_errmsg(DB));
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
bool sqlite3_exists(sqlite3* DB, const char *tableName,
|
||||
const char* keyName, const char* keyData)
|
||||
{
|
||||
size_t stringSize = 100 + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
||||
char query[stringSize];
|
||||
sprintf(query,"SELECT * FROM %s WHERE %s == \"%s\"",tableName,keyName,keyData);
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
// Anything there?
|
||||
return (src == SQLITE_ROW);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
|
||||
const char* keyName, const char* keyData,
|
||||
const char* valueName, unsigned &valueData)
|
||||
{
|
||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
||||
char query[stringSize];
|
||||
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
bool retVal = false;
|
||||
if (src == SQLITE_ROW) {
|
||||
valueData = (unsigned)sqlite3_column_int64(stmt,0);
|
||||
retVal = true;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
// This function returns an allocated string that must be free'd by the caller.
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, const char* keyData,
|
||||
const char* valueName, char* &valueData)
|
||||
{
|
||||
valueData=NULL;
|
||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
||||
char query[stringSize];
|
||||
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
bool retVal = false;
|
||||
if (src == SQLITE_ROW) {
|
||||
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
|
||||
if (ptr) valueData = strdup(ptr);
|
||||
retVal = true;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
// This function returns an allocated string that must be free'd by tha caller.
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, unsigned keyData,
|
||||
const char* valueName, char* &valueData)
|
||||
{
|
||||
valueData=NULL;
|
||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + 20;
|
||||
char query[stringSize];
|
||||
sprintf(query,"SELECT %s FROM %s WHERE %s == %u",valueName,tableName,keyName,keyData);
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
bool retVal = false;
|
||||
if (src == SQLITE_ROW) {
|
||||
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
|
||||
if (ptr) valueData = strdup(ptr);
|
||||
retVal = true;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool sqlite3_command(sqlite3* DB, const char* query)
|
||||
{
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Run the query.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return src==SQLITE_DONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef SQLITE3UTIL_H
|
||||
#define SQLITE3UTIL_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query);
|
||||
|
||||
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt);
|
||||
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
|
||||
const char* keyName, const char* keyData,
|
||||
const char* valueName, unsigned &valueData);
|
||||
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, const char* keyData,
|
||||
const char* valueName, char* &valueData);
|
||||
|
||||
// This function returns an allocated string that must be free'd by the caller.
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, unsigned keyData,
|
||||
const char* valueName, char* &valueData);
|
||||
|
||||
bool sqlite3_exists(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, const char* keyData);
|
||||
|
||||
/** Run a query, ignoring the result; return true on success. */
|
||||
bool sqlite3_command(sqlite3* DB, const char* query);
|
||||
|
||||
#endif
|
||||
@@ -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. */
|
||||
|
||||
22
INSTALLATION
22
INSTALLATION
@@ -2,32 +2,18 @@ Installation Requirements
|
||||
|
||||
|
||||
|
||||
OpenBTS compiles to a simple Unix binary and does not require special
|
||||
osmo-trx compiles to a simple Unix binary and does not require special
|
||||
installation.
|
||||
|
||||
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
|
||||
running configure.
|
||||
|
||||
To run OpenBTS, the following should be installed:
|
||||
|
||||
Asterisk (http://www.asterisk.org), running SIP on port 5060.
|
||||
|
||||
libosip2 (http://www.gnu.org/software/osip/)
|
||||
|
||||
libortp (http://freshmeat.net/projects/ortp/)
|
||||
|
||||
libusrp (http://gnuradio.org).
|
||||
To run osmo-trx, the following should be installed:
|
||||
libuhd (https://gnuradio.org).
|
||||
This is part of the GNURadio installation.
|
||||
It is the only part used by OpenBTS.
|
||||
|
||||
|
||||
OpenBTS logs to syslogd as facility LOG_LOCAL7. Please set your /etc/syslog.conf
|
||||
accordingly.
|
||||
|
||||
|
||||
For information on specific executables, see tests/README.tests and
|
||||
apps/README.apps.
|
||||
|
||||
See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more
|
||||
See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
|
||||
information.
|
||||
|
||||
|
||||
20
LEGAL
20
LEGAL
@@ -1,5 +1,8 @@
|
||||
OpenBTS
|
||||
|
||||
The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details.
|
||||
|
||||
The related copyrights:
|
||||
Most parts copyright 2008-2011 Free Software Foundation.
|
||||
Some parts copyright 2010 Kestrel Signal Processing, Inc.
|
||||
Some parts copyright 2011 Range Networks, Inc.
|
||||
@@ -12,17 +15,9 @@ patented technologies. The user of this software is required to take whatever
|
||||
actions are necessary to avoid patent infringement.
|
||||
|
||||
|
||||
Trademark
|
||||
|
||||
"OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a
|
||||
California corporation. Range reserves the right to control the use of this
|
||||
trademark. Do not use this trademark in commerce without permission and do not
|
||||
rebrand OpenBTS under a different trademark.
|
||||
|
||||
|
||||
Telecom and Radio Spectrum Laws
|
||||
|
||||
The primary function of OpenBTS is the provision of telecommunications service
|
||||
The primary function of OsmoTRX is the provision of telecommunications service
|
||||
over a radio link. This activity is heavily regulated nearly everywhere in
|
||||
the world. Users of this software are expected to comply with local and national
|
||||
regulations in the jurisdictions where this sortware is used with radio equipment.
|
||||
@@ -39,7 +34,7 @@ The legal restrictions listed here are not necessarily exhaustive.
|
||||
|
||||
Note to US Government Users
|
||||
|
||||
The OpenBTS software applications and associated documentation are "Commercial
|
||||
The OsmoTRX software applications and associated documentation are "Commercial
|
||||
Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of
|
||||
"Commercial Computer Software" and "Commercial Computer Software Documentation,"
|
||||
as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
|
||||
@@ -54,13 +49,12 @@ and AGPLv3.
|
||||
Note to US Government Contractors
|
||||
|
||||
GPL is not compatible with "government purpose rights" (GPR). If you receive
|
||||
OpenBTS software under a GPL and deliver it under GPR, you will be in violation
|
||||
OsmoTRX software under a GPL and deliver it under GPR, you will be in violation
|
||||
of GPL and possibly subject to enforcement actions by the original authors and
|
||||
copyright holders, including the Free Software Foundation, Inc.
|
||||
|
||||
|
||||
Software Licensing and Distribution
|
||||
|
||||
A subset of OpenBTS is distributed publicly under AGPLv3. Range reserves the right to
|
||||
distribute most of this source code other licenses as well. See the COPYING file
|
||||
The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file
|
||||
for more information on the license for this distribution.
|
||||
|
||||
13
Makefile.am
13
Makefile.am
@@ -22,16 +22,16 @@ include $(top_srcdir)/Makefile.common
|
||||
|
||||
ACLOCAL_AMFLAGS = -I config
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -pthread -ldl
|
||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
AM_CXXFLAGS = -Wall -pthread
|
||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
|
||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
|
||||
|
||||
# Order must be preserved
|
||||
SUBDIRS = \
|
||||
sqlite3 \
|
||||
CommonLibs \
|
||||
GSM \
|
||||
Transceiver52M
|
||||
Transceiver52M \
|
||||
tests
|
||||
|
||||
EXTRA_DIST = \
|
||||
autogen.sh \
|
||||
@@ -40,6 +40,9 @@ EXTRA_DIST = \
|
||||
COPYING \
|
||||
README
|
||||
|
||||
.PHONY: release
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
dox: FORCE
|
||||
doxygen doxconfig
|
||||
|
||||
@@ -23,16 +23,19 @@ 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
|
||||
|
||||
if ARCH_ARM
|
||||
ARCH_LA = $(top_builddir)/Transceiver52M/arm/libarch.la
|
||||
else
|
||||
ARCH_LA = $(top_builddir)/Transceiver52M/x86/libarch.la
|
||||
endif
|
||||
|
||||
MOSTLYCLEANFILES = *~
|
||||
|
||||
107
Transceiver52M/Channelizer.cpp
Normal file
107
Transceiver52M/Channelizer.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 "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_ */
|
||||
@@ -21,18 +21,12 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I./common
|
||||
AM_CXXFLAGS = -ldl -lpthread
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common
|
||||
AM_CXXFLAGS = -lpthread
|
||||
|
||||
SUBDIRS = arm x86
|
||||
|
||||
if ARCH_ARM
|
||||
ARCH_LA = arm/libarch.la
|
||||
else
|
||||
ARCH_LA = x86/libarch.la
|
||||
endif
|
||||
|
||||
if USRP1
|
||||
if USRP1
|
||||
AM_CPPFLAGS += $(USRP_CFLAGS)
|
||||
else
|
||||
AM_CPPFLAGS += $(UHD_CFLAGS)
|
||||
@@ -54,15 +48,20 @@ COMMON_SOURCES = \
|
||||
radioInterface.cpp \
|
||||
radioVector.cpp \
|
||||
radioClock.cpp \
|
||||
radioBuffer.cpp \
|
||||
sigProcLib.cpp \
|
||||
signalVector.cpp \
|
||||
Transceiver.cpp
|
||||
Transceiver.cpp \
|
||||
ChannelizerBase.cpp \
|
||||
Channelizer.cpp \
|
||||
Synthesis.cpp \
|
||||
common/fft.c
|
||||
|
||||
libtransceiver_la_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
Resampler.cpp \
|
||||
radioInterfaceResamp.cpp \
|
||||
radioInterfaceDiversity.cpp
|
||||
radioInterfaceMulti.cpp
|
||||
|
||||
bin_PROGRAMS = osmo-trx
|
||||
|
||||
@@ -72,24 +71,30 @@ noinst_HEADERS = \
|
||||
radioVector.h \
|
||||
radioClock.h \
|
||||
radioDevice.h \
|
||||
radioBuffer.h \
|
||||
sigProcLib.h \
|
||||
signalVector.h \
|
||||
Transceiver.h \
|
||||
USRPDevice.h \
|
||||
Resampler.h \
|
||||
ChannelizerBase.h \
|
||||
Channelizer.h \
|
||||
Synthesis.h \
|
||||
common/convolve.h \
|
||||
common/convert.h \
|
||||
common/scale.h \
|
||||
common/mult.h
|
||||
common/mult.h \
|
||||
common/fft.h
|
||||
|
||||
osmo_trx_SOURCES = osmo-trx.cpp
|
||||
osmo_trx_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
$(COMMON_LA) \
|
||||
$(FFTWF_LIBS)
|
||||
|
||||
if USRP1
|
||||
if USRP1
|
||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||
osmo_trx_LDADD += $(USRP_LIBS)
|
||||
else
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Resampler.h"
|
||||
|
||||
@@ -35,6 +36,8 @@ extern "C" {
|
||||
|
||||
#define MAX_OUTPUT_LEN 4096
|
||||
|
||||
using namespace std;
|
||||
|
||||
static float sinc(float x)
|
||||
{
|
||||
if (x == 0.0)
|
||||
@@ -43,32 +46,19 @@ static float sinc(float x)
|
||||
return sin(M_PI * x) / (M_PI * x);
|
||||
}
|
||||
|
||||
bool Resampler::initFilters(float bw)
|
||||
void Resampler::initFilters(float bw)
|
||||
{
|
||||
size_t proto_len = p * filt_len;
|
||||
float *proto, val, cutoff;
|
||||
float 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) {
|
||||
free(proto);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < p; i++) {
|
||||
partitions[i] = (float *)
|
||||
memalign(16, filt_len * 2 * sizeof(float));
|
||||
}
|
||||
auto proto = vector<float>(p * filt_len);
|
||||
for (auto &part : partitions)
|
||||
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
|
||||
|
||||
/*
|
||||
* Generate the prototype filter with a Blackman-harris window.
|
||||
@@ -85,47 +75,26 @@ bool Resampler::initFilters(float bw)
|
||||
else
|
||||
cutoff = (float) q;
|
||||
|
||||
for (size_t i = 0; i < proto_len; i++) {
|
||||
float midpt = (proto.size() - 1) / 2.0;
|
||||
for (size_t i = 0; i < proto.size(); i++) {
|
||||
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
||||
proto[i] *= 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));
|
||||
a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
|
||||
a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
|
||||
a3 * cos(6 * M_PI * i / (proto.size() - 1));
|
||||
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 (size_t n = 0; n < p; n++)
|
||||
partitions[n][i] = complex<float>(proto[i * p + n] * scale);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* Store filter taps in reverse */
|
||||
for (auto &part : partitions)
|
||||
reverse(&part[0], &part[filt_len]);
|
||||
}
|
||||
|
||||
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||
@@ -159,24 +128,12 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resampler::computePath()
|
||||
{
|
||||
for (int i = 0; i < MAX_OUTPUT_LEN; i++) {
|
||||
in_index[i] = (q * i) / p;
|
||||
out_path[i] = (q * i) % p;
|
||||
}
|
||||
}
|
||||
|
||||
int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
|
||||
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
|
||||
{
|
||||
int n, path;
|
||||
int hist_len = filt_len - 1;
|
||||
|
||||
if (!check_vec_len(in_len, out_len, p, q))
|
||||
return -1;
|
||||
|
||||
/* Insert history */
|
||||
memcpy(&in[-2 * hist_len], history, hist_len * 2 * sizeof(float));
|
||||
return -1;
|
||||
|
||||
/* Generate output from precomputed input/output paths */
|
||||
for (size_t i = 0; i < out_len; i++) {
|
||||
@@ -184,34 +141,28 @@ int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
|
||||
path = out_path[i];
|
||||
|
||||
convolve_real(in, in_len,
|
||||
partitions[path], filt_len,
|
||||
&out[2 * i], out_len - i,
|
||||
reinterpret_cast<float *>(partitions[path]),
|
||||
filt_len, &out[2 * i], out_len - i,
|
||||
n, 1, 1, 0);
|
||||
}
|
||||
|
||||
/* Save history */
|
||||
memcpy(history, &in[2 * (in_len - hist_len)],
|
||||
hist_len * 2 * sizeof(float));
|
||||
|
||||
return out_len;
|
||||
}
|
||||
|
||||
bool Resampler::init(float bw)
|
||||
{
|
||||
size_t hist_len = filt_len - 1;
|
||||
if (p == 0 || q == 0 || filt_len == 0) return false;
|
||||
|
||||
/* Filterbank filter internals */
|
||||
if (initFilters(bw) < 0)
|
||||
return false;
|
||||
|
||||
/* History buffer */
|
||||
history = new float[2 * hist_len];
|
||||
memset(history, 0, 2 * hist_len * sizeof(float));
|
||||
initFilters(bw);
|
||||
|
||||
/* Precompute filterbank paths */
|
||||
in_index = new size_t[MAX_OUTPUT_LEN];
|
||||
out_path = new size_t[MAX_OUTPUT_LEN];
|
||||
computePath();
|
||||
int i = 0;
|
||||
for (auto &index : in_index)
|
||||
index = (q * i++) / p;
|
||||
i = 0;
|
||||
for (auto &path : out_path)
|
||||
path = (q * i++) % p;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -222,7 +173,7 @@ size_t Resampler::len()
|
||||
}
|
||||
|
||||
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||
: in_index(NULL), out_path(NULL), partitions(NULL), history(NULL)
|
||||
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
|
||||
{
|
||||
this->p = p;
|
||||
this->q = q;
|
||||
@@ -231,9 +182,6 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||
|
||||
Resampler::~Resampler()
|
||||
{
|
||||
releaseFilters();
|
||||
|
||||
delete history;
|
||||
delete in_index;
|
||||
delete out_path;
|
||||
for (auto &part : partitions)
|
||||
free(part);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#ifndef _RESAMPLER_H_
|
||||
#define _RESAMPLER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
|
||||
class Resampler {
|
||||
public:
|
||||
/* Constructor for rational sample rate conversion
|
||||
@@ -52,7 +55,7 @@ public:
|
||||
* Input and output vector lengths must of be equal multiples of the
|
||||
* rational conversion rate denominator and numerator respectively.
|
||||
*/
|
||||
int rotate(float *in, size_t in_len, float *out, size_t out_len);
|
||||
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
|
||||
@@ -63,15 +66,11 @@ private:
|
||||
size_t p;
|
||||
size_t q;
|
||||
size_t filt_len;
|
||||
size_t *in_index;
|
||||
size_t *out_path;
|
||||
std::vector<size_t> in_index;
|
||||
std::vector<size_t> out_path;
|
||||
std::vector<std::complex<float> *> partitions;
|
||||
|
||||
float **partitions;
|
||||
float *history;
|
||||
|
||||
bool initFilters(float bw);
|
||||
void releaseFilters();
|
||||
void computePath();
|
||||
void initFilters(float bw);
|
||||
};
|
||||
|
||||
#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 <iostream>
|
||||
|
||||
#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_ */
|
||||
@@ -71,88 +71,60 @@ TransceiverState::~TransceiverState()
|
||||
}
|
||||
}
|
||||
|
||||
static BitVector *genRandNormalBurst(size_t tsc)
|
||||
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
|
||||
{
|
||||
if (tsc > 7)
|
||||
return NULL;
|
||||
|
||||
BitVector *bits = new BitVector(148);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 4; i++)
|
||||
(*bits)[i] = 0;
|
||||
|
||||
/* Random bits */
|
||||
for (; i < 61; i++)
|
||||
(*bits)[i] = rand() % 2;
|
||||
|
||||
/* Training sequence */
|
||||
for (int j = 0; i < 87; i++, j++)
|
||||
(*bits)[i] = GSM::gTrainingSequence[tsc][j];
|
||||
|
||||
/* Random bits */
|
||||
for (; i < 144; i++)
|
||||
(*bits)[i] = rand() % 2;
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 148; i++)
|
||||
(*bits)[i] = 0;
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
|
||||
{
|
||||
BitVector *bits;
|
||||
signalVector *burst;
|
||||
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return false;
|
||||
|
||||
for (size_t n = 0; n < 8; n++) {
|
||||
size_t guard = 8 + !(n % 4);
|
||||
size_t len = sps == 4 ? 625 : 148 + guard;
|
||||
|
||||
for (size_t i = 0; i < 102; i++) {
|
||||
switch (filler) {
|
||||
case Transceiver::FILLER_DUMMY:
|
||||
burst = modulateBurst(gDummyBurst, guard, sps);
|
||||
burst = generateDummyBurst(sps, n);
|
||||
break;
|
||||
case Transceiver::FILLER_RAND:
|
||||
bits = genRandNormalBurst(rtsc);
|
||||
burst = modulateBurst(*bits, guard, sps);
|
||||
delete bits;
|
||||
case Transceiver::FILLER_NORM_RAND:
|
||||
burst = genRandNormalBurst(rtsc, sps, n);
|
||||
break;
|
||||
case Transceiver::FILLER_EDGE_RAND:
|
||||
burst = generateEdgeBurst(rtsc);
|
||||
break;
|
||||
case Transceiver::FILLER_ACCESS_RAND:
|
||||
burst = genRandAccessBurst(rach_delay, sps, n);
|
||||
break;
|
||||
case Transceiver::FILLER_ZERO:
|
||||
default:
|
||||
burst = new signalVector(len);
|
||||
burst = generateEmptyBurst(sps, n);
|
||||
}
|
||||
|
||||
scaleVector(*burst, scale);
|
||||
fillerTable[i][n] = burst;
|
||||
}
|
||||
|
||||
if (filler == Transceiver::FILLER_RAND)
|
||||
chanType[n] = Transceiver::TSC;
|
||||
if ((filler == Transceiver::FILLER_NORM_RAND) ||
|
||||
(filler == Transceiver::FILLER_EDGE_RAND)) {
|
||||
chanType[n] = TSC;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Transceiver::Transceiver(int wBasePort,
|
||||
const char *wTRXAddress,
|
||||
size_t wSPS, size_t wChans,
|
||||
const char *TRXAddress,
|
||||
const char *GSMcoreAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
double wRssiOffset)
|
||||
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
||||
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
||||
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
|
||||
mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
|
||||
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||
rssiOffset(wRssiOffset),
|
||||
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
|
||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0), mWriteBurstToDiskMask(0)
|
||||
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
|
||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
||||
mWriteBurstToDiskMask(0)
|
||||
{
|
||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||
@@ -189,7 +161,7 @@ Transceiver::~Transceiver()
|
||||
* are still expected to report clock indications through control channel
|
||||
* activity.
|
||||
*/
|
||||
bool Transceiver::init(int filler, size_t rtsc)
|
||||
bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
|
||||
{
|
||||
int d_srcport, d_dstport, c_srcport, c_dstport;
|
||||
|
||||
@@ -198,11 +170,13 @@ bool Transceiver::init(int filler, size_t rtsc)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sigProcLibSetup(mSPSTx)) {
|
||||
if (!sigProcLibSetup()) {
|
||||
LOG(ALERT) << "Failed to initialize signal processing library";
|
||||
return false;
|
||||
}
|
||||
|
||||
mEdge = edge;
|
||||
|
||||
mDataSockets.resize(mChans);
|
||||
mCtrlSockets.resize(mChans);
|
||||
mControlServiceLoopThreads.resize(mChans);
|
||||
@@ -224,8 +198,8 @@ bool Transceiver::init(int filler, size_t rtsc)
|
||||
d_srcport = mBasePort + 2 * i + 2;
|
||||
d_dstport = mBasePort + 2 * i + 102;
|
||||
|
||||
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
|
||||
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
||||
mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
|
||||
mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
|
||||
}
|
||||
|
||||
/* Randomize the central clock */
|
||||
@@ -245,7 +219,7 @@ bool Transceiver::init(int filler, size_t rtsc)
|
||||
if (i && filler == FILLER_DUMMY)
|
||||
filler = FILLER_ZERO;
|
||||
|
||||
mStates[i].init(filler, mSPSTx, txFullScale, rtsc);
|
||||
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -300,7 +274,7 @@ bool Transceiver::start()
|
||||
TxUpperLoopAdapter, (void*) chan);
|
||||
}
|
||||
|
||||
writeClockInterface();
|
||||
mForceClockInterface = true;
|
||||
mOn = true;
|
||||
return true;
|
||||
}
|
||||
@@ -324,6 +298,10 @@ void Transceiver::stop()
|
||||
LOG(NOTICE) << "Stopping the transceiver";
|
||||
mTxLowerLoopThread->cancel();
|
||||
mRxLowerLoopThread->cancel();
|
||||
mTxLowerLoopThread->join();
|
||||
mRxLowerLoopThread->join();
|
||||
delete mTxLowerLoopThread;
|
||||
delete mRxLowerLoopThread;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
mRxServiceLoopThreads[i]->cancel();
|
||||
@@ -342,11 +320,6 @@ void Transceiver::stop()
|
||||
mTxPriorityQueues[i].clear();
|
||||
}
|
||||
|
||||
mTxLowerLoopThread->join();
|
||||
mRxLowerLoopThread->join();
|
||||
delete mTxLowerLoopThread;
|
||||
delete mRxLowerLoopThread;
|
||||
|
||||
mOn = false;
|
||||
LOG(NOTICE) << "Transceiver stopped";
|
||||
}
|
||||
@@ -367,7 +340,12 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
||||
return;
|
||||
}
|
||||
|
||||
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
|
||||
/* Use the number of bits as the EDGE burst indicator */
|
||||
if (bits.size() == EDGE_BURST_NBITS)
|
||||
burst = modulateEdgeBurst(bits, mSPSTx);
|
||||
else
|
||||
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
|
||||
|
||||
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
|
||||
|
||||
radio_burst = new radioVector(wTime, burst);
|
||||
@@ -452,7 +430,7 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
|
||||
case V:
|
||||
state->fillerModulus[timeslot] = 51;
|
||||
break;
|
||||
//case V:
|
||||
//case V:
|
||||
case VII:
|
||||
state->fillerModulus[timeslot] = 102;
|
||||
break;
|
||||
@@ -465,8 +443,8 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
|
||||
}
|
||||
|
||||
|
||||
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||
size_t chan)
|
||||
CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||
size_t chan)
|
||||
{
|
||||
static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
|
||||
static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
|
||||
@@ -555,92 +533,6 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect RACH synchronization sequence within a burst. No equalization
|
||||
* is used or available on the RACH channel.
|
||||
*/
|
||||
int Transceiver::detectRACH(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, float &toa)
|
||||
{
|
||||
float threshold = 6.0;
|
||||
|
||||
return detectRACHBurst(burst, threshold, mSPSRx, amp, toa);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect normal burst training sequence midamble. Update equalization
|
||||
* state information and channel estimate if necessary. Equalization
|
||||
* is currently disabled.
|
||||
*/
|
||||
int Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
||||
complex &, float &toa, GSM::Time &time)
|
||||
{
|
||||
int success;
|
||||
int tn = time.TN();
|
||||
float chanOffset, threshold = 5.0;
|
||||
bool needDFE = false, estimateChan = false;
|
||||
double elapsed = time - state->chanEstimateTime[tn];
|
||||
signalVector *chanResp;
|
||||
|
||||
/* Check equalization update state */
|
||||
if (needDFE && ((elapsed > 50) || (!state->chanResponse[tn]))) {
|
||||
delete state->DFEForward[tn];
|
||||
delete state->DFEFeedback[tn];
|
||||
state->DFEForward[tn] = NULL;
|
||||
state->DFEFeedback[tn] = NULL;
|
||||
|
||||
estimateChan = true;
|
||||
}
|
||||
|
||||
/* Detect normal burst midambles */
|
||||
success = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, amp,
|
||||
toa, mMaxExpectedDelay, estimateChan,
|
||||
&chanResp, &chanOffset);
|
||||
if (success <= 0) {
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Set equalizer if unabled */
|
||||
if (needDFE && estimateChan) {
|
||||
float noise = state->mNoiseLev;
|
||||
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
|
||||
|
||||
state->chanResponse[tn] = chanResp;
|
||||
state->chanRespOffset[tn] = chanOffset;
|
||||
state->chanRespAmplitude[tn] = amp;
|
||||
|
||||
scaleVector(*chanResp, complex(1.0, 0.0) / amp);
|
||||
|
||||
designDFE(*chanResp, state->SNRestimate[tn],
|
||||
7, &state->DFEForward[tn], &state->DFEFeedback[tn]);
|
||||
|
||||
state->chanEstimateTime[tn] = time;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Demodulate GMSK burst using equalization if requested. Otherwise
|
||||
* demodulate by direct rotation and soft slicing.
|
||||
*/
|
||||
SoftVector *Transceiver::demodulate(TransceiverState *state,
|
||||
signalVector &burst, complex amp,
|
||||
float toa, size_t tn, bool equalize)
|
||||
{
|
||||
if (equalize) {
|
||||
scaleVector(burst, complex(1.0, 0.0) / amp);
|
||||
return equalizeBurst(burst,
|
||||
toa - state->chanRespOffset[tn],
|
||||
mSPSRx,
|
||||
*state->DFEForward[tn],
|
||||
*state->DFEFeedback[tn]);
|
||||
}
|
||||
|
||||
return demodulateBurst(burst, mSPSRx, amp, toa);
|
||||
}
|
||||
|
||||
void writeToFile(radioVector *radio_burst, size_t chan)
|
||||
{
|
||||
GSM::Time time = radio_burst->getTime();
|
||||
@@ -653,16 +545,15 @@ void writeToFile(radioVector *radio_burst, size_t chan)
|
||||
|
||||
/*
|
||||
* Pull bursts from the FIFO and handle according to the slot
|
||||
* and burst correlation type. Equalzation is currently disabled.
|
||||
* and burst correlation type. Equalzation is currently disabled.
|
||||
*/
|
||||
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
||||
double &timingOffset, double &noise,
|
||||
size_t chan)
|
||||
{
|
||||
int success;
|
||||
bool equalize = false;
|
||||
int rc;
|
||||
complex amp;
|
||||
float toa, pow, max = -1.0, avg = 0.0;
|
||||
float toa, max = -1.0, avg = 0.0;
|
||||
int max_i = -1;
|
||||
signalVector *burst;
|
||||
SoftVector *bits = NULL;
|
||||
@@ -678,6 +569,10 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
||||
GSM::Time time = radio_burst->getTime();
|
||||
CorrType type = expectedCorrType(time, chan);
|
||||
|
||||
/* Enable 8-PSK burst detection if EDGE is enabled */
|
||||
if (mEdge && (type == TSC))
|
||||
type = EDGE;
|
||||
|
||||
/* Debug: dump bursts to disk */
|
||||
/* bits 0-7 - chan 0 timeslots
|
||||
* bits 8-15 - chan 1 timeslots */
|
||||
@@ -693,7 +588,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
||||
|
||||
/* Select the diversity channel with highest energy */
|
||||
for (size_t i = 0; i < radio_burst->chans(); i++) {
|
||||
energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow);
|
||||
float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
|
||||
if (pow > max) {
|
||||
max = pow;
|
||||
max_i = i;
|
||||
@@ -731,16 +626,15 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
||||
}
|
||||
|
||||
/* Detect normal or RACH bursts */
|
||||
if (type == TSC)
|
||||
success = detectTSC(state, *burst, amp, toa, time);
|
||||
else
|
||||
success = detectRACH(state, *burst, amp, toa);
|
||||
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa,
|
||||
(type==RACH)?mMaxExpectedDelayAB:mMaxExpectedDelayNB);
|
||||
|
||||
/* Alert an error and exit */
|
||||
if (success <= 0) {
|
||||
if (success == -SIGERR_CLIP) {
|
||||
if (rc > 0) {
|
||||
type = (CorrType) rc;
|
||||
} else if (rc <= 0) {
|
||||
if (rc == -SIGERR_CLIP) {
|
||||
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
|
||||
} else if (success != SIGERR_NONE) {
|
||||
} else if (rc != SIGERR_NONE) {
|
||||
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
|
||||
}
|
||||
|
||||
@@ -748,13 +642,9 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
||||
return NULL;
|
||||
}
|
||||
|
||||
timingOffset = toa / mSPSRx;
|
||||
timingOffset = toa;
|
||||
|
||||
/* Demodulate and set output info */
|
||||
if (equalize && (type != TSC))
|
||||
equalize = false;
|
||||
|
||||
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
|
||||
bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
|
||||
|
||||
delete radio_burst;
|
||||
return bits;
|
||||
@@ -766,7 +656,7 @@ void Transceiver::reset()
|
||||
mTxPriorityQueues[i].clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Transceiver::driveControl(size_t chan)
|
||||
{
|
||||
int MAX_PACKET_LENGTH = 100;
|
||||
@@ -776,7 +666,7 @@ void Transceiver::driveControl(size_t chan)
|
||||
int msgLen = -1;
|
||||
buffer[0] = '\0';
|
||||
|
||||
msgLen = mCtrlSockets[chan]->read(buffer);
|
||||
msgLen = mCtrlSockets[chan]->read(buffer, sizeof(buffer));
|
||||
|
||||
if (msgLen < 1) {
|
||||
return;
|
||||
@@ -788,9 +678,6 @@ void Transceiver::driveControl(size_t chan)
|
||||
|
||||
sscanf(buffer,"%3s %s",cmdcheck,command);
|
||||
|
||||
if (!chan)
|
||||
writeClockInterface();
|
||||
|
||||
if (strcmp(cmdcheck,"CMD")!=0) {
|
||||
LOG(WARNING) << "bogus message on control interface";
|
||||
return;
|
||||
@@ -802,36 +689,42 @@ void Transceiver::driveControl(size_t chan)
|
||||
sprintf(response,"RSP POWEROFF 0");
|
||||
}
|
||||
else if (strcmp(command,"POWERON")==0) {
|
||||
if (!start())
|
||||
if (!start()) {
|
||||
sprintf(response,"RSP POWERON 1");
|
||||
else
|
||||
} else {
|
||||
sprintf(response,"RSP POWERON 0");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 0; j < 8; j++)
|
||||
mHandover[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(command,"HANDOVER")==0){
|
||||
int ts=0,ss=0;
|
||||
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
|
||||
mHandover[ts][ss] = true;
|
||||
LOG(WARNING) << "HANDOVER RACH at timeslot " << ts << " subslot " << ss;
|
||||
sprintf(response,"RSP HANDOVER 0 %d %d",ts,ss);
|
||||
}
|
||||
else if (strcmp(command,"NOHANDOVER")==0){
|
||||
int ts=0,ss=0;
|
||||
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
|
||||
mHandover[ts][ss] = false;
|
||||
LOG(WARNING) << "NOHANDOVER at timeslot " << ts << " subslot " << ss;
|
||||
sprintf(response,"RSP NOHANDOVER 0 %d %d",ts,ss);
|
||||
}
|
||||
else if (strcmp(command,"SETMAXDLY")==0) {
|
||||
//set expected maximum time-of-arrival
|
||||
int maxDelay;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
|
||||
mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
|
||||
}
|
||||
else if (strcmp(command,"SETMAXDLYNB")==0) {
|
||||
//set expected maximum time-of-arrival
|
||||
int maxDelay;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
|
||||
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
|
||||
}
|
||||
else if (strcmp(command,"SETRXGAIN")==0) {
|
||||
//set expected maximum time-of-arrival
|
||||
int newGain;
|
||||
@@ -892,13 +785,11 @@ void Transceiver::driveControl(size_t chan)
|
||||
// set TSC
|
||||
unsigned TSC;
|
||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
|
||||
if (mOn || (TSC < 0) || (TSC > 7))
|
||||
if (TSC > 7) {
|
||||
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
||||
else if (chan && (TSC != mTSC))
|
||||
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
||||
else {
|
||||
} else {
|
||||
LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
|
||||
mTSC = TSC;
|
||||
generateMidamble(mSPSRx, TSC);
|
||||
sprintf(response,"RSP SETTSC 0 %d", TSC);
|
||||
}
|
||||
}
|
||||
@@ -911,7 +802,7 @@ void Transceiver::driveControl(size_t chan)
|
||||
LOG(WARNING) << "bogus message on control interface";
|
||||
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
||||
setModulus(timeslot, chan);
|
||||
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
||||
@@ -935,12 +826,20 @@ void Transceiver::driveControl(size_t chan)
|
||||
|
||||
bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
{
|
||||
char buffer[gSlotLen+50];
|
||||
int burstLen;
|
||||
char buffer[EDGE_BURST_NBITS + 50];
|
||||
|
||||
// check data socket
|
||||
size_t msgLen = mDataSockets[chan]->read(buffer);
|
||||
size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
|
||||
|
||||
if (msgLen!=gSlotLen+1+4+1) {
|
||||
if (msgLen == gSlotLen + 1 + 4 + 1) {
|
||||
burstLen = gSlotLen;
|
||||
} else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
|
||||
if (mSPSTx != 4)
|
||||
return false;
|
||||
|
||||
burstLen = EDGE_BURST_NBITS;
|
||||
} else {
|
||||
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
|
||||
return false;
|
||||
}
|
||||
@@ -951,14 +850,14 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
|
||||
|
||||
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
||||
|
||||
|
||||
int RSSI = (int) buffer[5];
|
||||
static BitVector newBurst(gSlotLen);
|
||||
BitVector newBurst(burstLen);
|
||||
BitVector::iterator itr = newBurst.begin();
|
||||
char *bufferItr = buffer+6;
|
||||
while (itr < newBurst.end())
|
||||
while (itr < newBurst.end())
|
||||
*itr++ = *bufferItr++;
|
||||
|
||||
|
||||
GSM::Time currTime = GSM::Time(frameNum,timeSlot);
|
||||
|
||||
addRadioVector(chan, newBurst, RSSI, currTime);
|
||||
@@ -972,12 +871,26 @@ void Transceiver::driveReceiveRadio()
|
||||
{
|
||||
if (!mRadioInterface->driveReceiveRadio()) {
|
||||
usleep(100000);
|
||||
} else {
|
||||
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
||||
writeClockInterface();
|
||||
} else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
|
||||
mForceClockInterface = false;
|
||||
writeClockInterface();
|
||||
}
|
||||
}
|
||||
|
||||
void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
|
||||
double rssi, double noise, double toa)
|
||||
{
|
||||
LOG(DEBUG) << std::fixed << std::right
|
||||
<< " chan: " << chan
|
||||
<< " time: " << time
|
||||
<< " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
|
||||
<< "dBFS/" << std::setw(6) << -dbm << "dBm"
|
||||
<< " noise: " << std::setw(5) << std::setprecision(1) << noise
|
||||
<< "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm"
|
||||
<< " TOA: " << std::setw(5) << std::setprecision(2) << toa
|
||||
<< " bits: " << *burst;
|
||||
}
|
||||
|
||||
void Transceiver::driveReceiveFIFO(size_t chan)
|
||||
{
|
||||
SoftVector *rxBurst = NULL;
|
||||
@@ -988,44 +901,49 @@ void Transceiver::driveReceiveFIFO(size_t chan)
|
||||
double noise; // noise level in dBFS
|
||||
GSM::Time burstTime;
|
||||
bool isRssiValid; // are RSSI, noise and burstTime valid
|
||||
unsigned nbits = gSlotLen;
|
||||
|
||||
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
|
||||
if (!rxBurst)
|
||||
return;
|
||||
|
||||
if (rxBurst) {
|
||||
dBm = RSSI+rssiOffset;
|
||||
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
|
||||
// Convert -1..+1 soft bits to 0..1 soft bits
|
||||
vectorSlicer(rxBurst);
|
||||
|
||||
LOG(DEBUG) << std::fixed << std::right
|
||||
<< " time: " << burstTime
|
||||
<< " RSSI: " << std::setw(5) << std::setprecision(1) << RSSI << "dBFS/" << std::setw(6) << -dBm << "dBm"
|
||||
<< " noise: " << std::setw(5) << std::setprecision(1) << noise << "dBFS/" << std::setw(6) << -(noise+rssiOffset) << "dBm"
|
||||
<< " TOA: " << std::setw(5) << std::setprecision(2) << TOA
|
||||
<< " bits: " << *rxBurst;
|
||||
/*
|
||||
* EDGE demodulator returns 444 (148 * 3) bits
|
||||
*/
|
||||
if (rxBurst->size() == gSlotLen * 3)
|
||||
nbits = gSlotLen * 3;
|
||||
|
||||
char burstString[gSlotLen+10];
|
||||
burstString[0] = burstTime.TN();
|
||||
for (int i = 0; i < 4; i++)
|
||||
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
|
||||
burstString[5] = (int)dBm;
|
||||
burstString[6] = (TOAint >> 8) & 0x0ff;
|
||||
burstString[7] = TOAint & 0x0ff;
|
||||
SoftVector::iterator burstItr = rxBurst->begin();
|
||||
dBm = RSSI + rssiOffset;
|
||||
logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
|
||||
|
||||
for (unsigned int i = 0; i < gSlotLen; i++) {
|
||||
burstString[8+i] =(char) round((*burstItr++)*255.0);
|
||||
}
|
||||
burstString[gSlotLen+9] = '\0';
|
||||
delete rxBurst;
|
||||
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
|
||||
|
||||
mDataSockets[chan]->write(burstString,gSlotLen+10);
|
||||
}
|
||||
char burstString[nbits + 10];
|
||||
burstString[0] = burstTime.TN();
|
||||
for (int i = 0; i < 4; i++)
|
||||
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
|
||||
burstString[5] = (int)dBm;
|
||||
burstString[6] = (TOAint >> 8) & 0x0ff;
|
||||
burstString[7] = TOAint & 0x0ff;
|
||||
SoftVector::iterator burstItr = rxBurst->begin();
|
||||
|
||||
for (unsigned i = 0; i < nbits; i++)
|
||||
burstString[8 + i] = (char) round((*burstItr++) * 255.0);
|
||||
|
||||
burstString[nbits + 9] = '\0';
|
||||
delete rxBurst;
|
||||
|
||||
mDataSockets[chan]->write(burstString, nbits + 10);
|
||||
}
|
||||
|
||||
void Transceiver::driveTxFIFO()
|
||||
{
|
||||
|
||||
/**
|
||||
Features a carefully controlled latency mechanism, to
|
||||
Features a carefully controlled latency mechanism, to
|
||||
assure that transmit packets arrive at the radio/USRP
|
||||
before they need to be transmitted.
|
||||
|
||||
@@ -1036,7 +954,7 @@ void Transceiver::driveTxFIFO()
|
||||
|
||||
|
||||
RadioClock *radioClock = (mRadioInterface->getClock());
|
||||
|
||||
|
||||
if (mOn) {
|
||||
//radioClock->wait(); // wait until clock updates
|
||||
LOG(DEBUG) << "radio clock " << radioClock->get();
|
||||
|
||||
@@ -54,7 +54,7 @@ struct TransceiverState {
|
||||
~TransceiverState();
|
||||
|
||||
/* Initialize a multiframe slot in the filler table */
|
||||
bool init(int filler, size_t sps, float scale, size_t rtsc);
|
||||
bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
|
||||
|
||||
int chanType[8];
|
||||
|
||||
@@ -89,16 +89,18 @@ struct TransceiverState {
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
class Transceiver {
|
||||
public:
|
||||
/** Transceiver constructor
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX manager, as a string
|
||||
@param TRXAddress IP address of the TRX, as a string
|
||||
@param GSMcoreAddress IP address of the GSM core, as a string
|
||||
@param wSPS number of samples per GSM symbol
|
||||
@param wTransmitLatency initial setting of transmit latency
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
size_t wSPS, size_t chans,
|
||||
const char *GSMcoreAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
double wRssiOffset);
|
||||
@@ -107,7 +109,7 @@ public:
|
||||
~Transceiver();
|
||||
|
||||
/** Start the control loop */
|
||||
bool init(int filler, size_t rtsc);
|
||||
bool init(int filler, size_t rtsc, unsigned rach_delay, bool edge);
|
||||
|
||||
/** attach the radioInterface receive FIFO */
|
||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||
@@ -142,23 +144,18 @@ public:
|
||||
LOOPBACK ///< similar go VII, used in loopback testing
|
||||
} ChannelCombination;
|
||||
|
||||
/** 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;
|
||||
|
||||
enum FillerType {
|
||||
FILLER_DUMMY,
|
||||
FILLER_ZERO,
|
||||
FILLER_RAND,
|
||||
FILLER_NORM_RAND,
|
||||
FILLER_EDGE_RAND,
|
||||
FILLER_ACCESS_RAND,
|
||||
};
|
||||
|
||||
private:
|
||||
int mBasePort;
|
||||
std::string mAddr;
|
||||
std::string mLocalAddr;
|
||||
std::string mRemoteAddr;
|
||||
|
||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||
@@ -175,7 +172,7 @@ private:
|
||||
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||
|
||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||
@@ -208,31 +205,19 @@ private:
|
||||
/** send messages over the clock socket */
|
||||
void writeClockInterface(void);
|
||||
|
||||
/** Detect RACH bursts */
|
||||
int detectRACH(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, float &toa);
|
||||
|
||||
/** Detect normal bursts */
|
||||
int detectTSC(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, float &toa, GSM::Time &time);
|
||||
|
||||
/** Demodulat burst and output soft bits */
|
||||
SoftVector *demodulate(TransceiverState *state,
|
||||
signalVector &burst, complex amp,
|
||||
float toa, size_t tn, bool equalize);
|
||||
|
||||
int mSPSTx; ///< number of samples per Tx symbol
|
||||
int mSPSRx; ///< number of samples per Rx symbol
|
||||
size_t mChans;
|
||||
|
||||
bool mEdge;
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
|
||||
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||
double mTxFreq; ///< the transmit frequency
|
||||
double mRxFreq; ///< the receive frequency
|
||||
unsigned mTSC; ///< the midamble sequence code
|
||||
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
||||
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
|
||||
|
||||
std::vector<TransceiverState> mStates;
|
||||
@@ -279,6 +264,8 @@ protected:
|
||||
/** set priority on current thread */
|
||||
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 *);
|
||||
@@ -292,4 +279,3 @@ void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** transmit queueing thread loop */
|
||||
void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/*
|
||||
* Device support for Ettus Research UHD driver
|
||||
* Written by Thomas Tsou <ttsou@vt.edu>
|
||||
* Device support for Ettus Research UHD driver
|
||||
*
|
||||
* Copyright 2010,2011 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2015 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
|
||||
@@ -19,6 +21,7 @@
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include "radioDevice.h"
|
||||
#include "Threads.h"
|
||||
#include "Logger.h"
|
||||
@@ -26,18 +29,18 @@
|
||||
#include <uhd/property_tree.hpp>
|
||||
#include <uhd/usrp/multi_usrp.hpp>
|
||||
#include <uhd/utils/thread_priority.hpp>
|
||||
#include <uhd/utils/msg.hpp>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#define B2XX_CLK_RT 26e6
|
||||
#define E1XX_CLK_RT 52e6
|
||||
#define B100_BASE_RT 400000
|
||||
#define USRP2_BASE_RT 390625
|
||||
#ifndef USE_UHD_3_11
|
||||
#include <uhd/utils/msg.hpp>
|
||||
#endif
|
||||
|
||||
#define USRP_TX_AMPL 0.3
|
||||
#define UMTRX_TX_AMPL 0.7
|
||||
#define LIMESDR_TX_AMPL 0.3
|
||||
#define SAMPLE_BUF_SZ (1 << 20)
|
||||
|
||||
/*
|
||||
@@ -58,31 +61,28 @@ enum uhd_dev_type {
|
||||
USRP2,
|
||||
B100,
|
||||
B200,
|
||||
B205,
|
||||
B210,
|
||||
B2XX_MCBTS,
|
||||
E1XX,
|
||||
E3XX,
|
||||
X3XX,
|
||||
UMTRX,
|
||||
NUM_USRP_TYPES,
|
||||
};
|
||||
|
||||
struct uhd_dev_offset {
|
||||
enum uhd_dev_type type;
|
||||
int sps;
|
||||
double offset;
|
||||
const std::string desc;
|
||||
LIMESDR,
|
||||
};
|
||||
|
||||
/*
|
||||
* USRP version dependent device timings
|
||||
*/
|
||||
#ifdef USE_UHD_3_9
|
||||
#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11)
|
||||
#define B2XX_TIMING_1SPS 1.7153e-4
|
||||
#define B2XX_TIMING_4SPS 1.1696e-4
|
||||
#define B2XX_TIMING_4_4SPS 6.18462e-5
|
||||
#define B2XX_TIMING_MCBTS 7e-5
|
||||
#else
|
||||
#define B2XX_TIMING_1SPS 9.9692e-5
|
||||
#define B2XX_TIMING_4SPS 6.9248e-5
|
||||
#define B2XX_TIMING_4_4SPS 4.52308e-5
|
||||
#define B2XX_TIMING_MCBTS 6.42452e-5
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -95,125 +95,47 @@ struct uhd_dev_offset {
|
||||
* Notes:
|
||||
* USRP1 with timestamps is not supported by UHD.
|
||||
*/
|
||||
static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
|
||||
{ USRP1, 1, 0.0, "USRP1 not supported" },
|
||||
{ USRP1, 4, 0.0, "USRP1 not supported"},
|
||||
{ USRP2, 1, 1.2184e-4, "N2XX 1 SPS" },
|
||||
{ USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
|
||||
{ B100, 1, 1.2104e-4, "B100 1 SPS" },
|
||||
{ B100, 4, 7.9307e-5, "B100 4 SPS" },
|
||||
{ B200, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
|
||||
{ B200, 4, B2XX_TIMING_4SPS, "B200 4 SPS" },
|
||||
{ B205, 1, B2XX_TIMING_1SPS, "B200-mini 1 SPS" },
|
||||
{ B205, 4, B2XX_TIMING_4SPS, "B200-mini 4 SPS" },
|
||||
{ B210, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
|
||||
{ B210, 4, B2XX_TIMING_4SPS, "B210 4 SPS" },
|
||||
{ E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
|
||||
{ E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
|
||||
{ E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
|
||||
{ E3XX, 4, 1.2740e-4, "E3XX 4 SPS" },
|
||||
{ X3XX, 1, 1.5360e-4, "X3XX 1 SPS"},
|
||||
{ X3XX, 4, 1.1264e-4, "X3XX 4 SPS"},
|
||||
{ UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
|
||||
{ UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
|
||||
|
||||
/* Device Type, Tx-SPS, Rx-SPS */
|
||||
typedef std::tuple<uhd_dev_type, int, int> dev_key;
|
||||
|
||||
/* Device parameter descriptor */
|
||||
struct dev_desc {
|
||||
unsigned channels;
|
||||
double mcr;
|
||||
double rate;
|
||||
double offset;
|
||||
std::string str;
|
||||
};
|
||||
|
||||
static const std::map<dev_key, dev_desc> dev_param_map {
|
||||
{ std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
|
||||
{ std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
|
||||
{ std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
|
||||
{ std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } },
|
||||
{ std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } },
|
||||
{ std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
|
||||
{ std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
|
||||
{ std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
|
||||
{ std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } },
|
||||
{ std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
|
||||
{ std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
|
||||
{ std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } },
|
||||
{ std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } },
|
||||
{ std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } },
|
||||
{ std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } },
|
||||
{ std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } },
|
||||
{ std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } },
|
||||
{ std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } },
|
||||
{ std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } },
|
||||
{ std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} },
|
||||
{ std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
|
||||
{ std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
|
||||
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
|
||||
};
|
||||
|
||||
/*
|
||||
* Offset handling for special cases. Currently used for UmTRX dual channel
|
||||
* diversity receiver only.
|
||||
*/
|
||||
static struct uhd_dev_offset special_offsets[] = {
|
||||
{ UMTRX, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
|
||||
{ UMTRX, 4, 5.2103e-5, "UmTRX diversity, 4 SPS" },
|
||||
};
|
||||
|
||||
static double get_dev_offset(enum uhd_dev_type type,
|
||||
int sps, bool diversity = false)
|
||||
{
|
||||
struct uhd_dev_offset *offset = NULL;
|
||||
|
||||
/* Reject USRP1 */
|
||||
if (type == USRP1) {
|
||||
LOG(ERR) << "Invalid device type";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* Special cases (e.g. diversity receiver) */
|
||||
if (diversity) {
|
||||
if (type != UMTRX) {
|
||||
LOG(ALERT) << "Diversity on UmTRX only";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
switch (sps) {
|
||||
case 1:
|
||||
offset = &special_offsets[0];
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
offset = &special_offsets[1];
|
||||
}
|
||||
} else {
|
||||
/* Search for matching offset value */
|
||||
for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
|
||||
if ((type == uhd_offsets[i].type) &&
|
||||
(sps == uhd_offsets[i].sps)) {
|
||||
offset = &uhd_offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!offset) {
|
||||
LOG(ERR) << "Invalid device configuration";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
std::cout << "-- Setting " << offset->desc << std::endl;
|
||||
|
||||
return offset->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select sample rate based on device type and requested samples-per-symbol.
|
||||
* The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
|
||||
* usable channel spacing of 400 kHz.
|
||||
*/
|
||||
static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
|
||||
{
|
||||
if (diversity && (type == UMTRX)) {
|
||||
return GSMRATE * 4;
|
||||
} else if (diversity) {
|
||||
LOG(ALERT) << "Diversity supported on UmTRX only";
|
||||
return -9999.99;
|
||||
}
|
||||
|
||||
if ((sps != 4) && (sps != 1))
|
||||
return -9999.99;
|
||||
|
||||
switch (type) {
|
||||
case USRP2:
|
||||
case X3XX:
|
||||
return USRP2_BASE_RT * sps;
|
||||
case B100:
|
||||
return B100_BASE_RT * sps;
|
||||
case B200:
|
||||
case B205:
|
||||
case B210:
|
||||
case E1XX:
|
||||
case E3XX:
|
||||
case UMTRX:
|
||||
return GSMRATE * sps;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(ALERT) << "Unknown device type " << type;
|
||||
return -9999.99;
|
||||
}
|
||||
|
||||
/*
|
||||
Sample Buffer - Allows reading and writing of timed samples using OpenBTS
|
||||
Sample Buffer - Allows reading and writing of timed samples using osmo-trx
|
||||
or UHD style timestamps. Time conversions are handled
|
||||
internally or accessable through the static convert calls.
|
||||
*/
|
||||
@@ -221,8 +143,8 @@ class smpl_buf {
|
||||
public:
|
||||
/** Sample buffer constructor
|
||||
@param len number of 32-bit samples the buffer should hold
|
||||
@param rate sample clockrate
|
||||
@param timestamp
|
||||
@param rate sample clockrate
|
||||
@param timestamp
|
||||
*/
|
||||
smpl_buf(size_t len, double rate);
|
||||
~smpl_buf();
|
||||
@@ -250,7 +172,7 @@ public:
|
||||
*/
|
||||
std::string str_status(size_t ts) const;
|
||||
|
||||
/** Formatted error string
|
||||
/** Formatted error string
|
||||
@param code an error code
|
||||
@return a formatted error string
|
||||
*/
|
||||
@@ -285,10 +207,13 @@ private:
|
||||
*/
|
||||
class uhd_device : public RadioDevice {
|
||||
public:
|
||||
uhd_device(size_t sps, size_t chans, bool diversity, double offset);
|
||||
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||
size_t chans, double offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths);
|
||||
~uhd_device();
|
||||
|
||||
int open(const std::string &args, bool extref, bool swap_channels);
|
||||
int open(const std::string &args, int ref, bool swap_channels);
|
||||
bool start();
|
||||
bool stop();
|
||||
bool restart();
|
||||
@@ -306,8 +231,8 @@ public:
|
||||
bool setTxFreq(double wFreq, size_t chan);
|
||||
bool setRxFreq(double wFreq, size_t chan);
|
||||
|
||||
inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
|
||||
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
|
||||
TIMESTAMP initialWriteTimestamp();
|
||||
TIMESTAMP initialReadTimestamp();
|
||||
|
||||
double fullScaleInputValue();
|
||||
double fullScaleOutputValue();
|
||||
@@ -325,6 +250,11 @@ public:
|
||||
double getRxFreq(size_t chan);
|
||||
double getRxFreq();
|
||||
|
||||
bool setRxAntenna(const std::string &ant, size_t chan);
|
||||
std::string getRxAntenna(size_t chan);
|
||||
bool setTxAntenna(const std::string &ant, size_t chan);
|
||||
std::string getTxAntenna(size_t chan);
|
||||
|
||||
inline double getSampleRate() { return tx_rate; }
|
||||
inline double numberRead() { return rx_pkt_cnt; }
|
||||
inline double numberWritten() { return 0; }
|
||||
@@ -348,7 +278,7 @@ private:
|
||||
enum TxWindowType tx_window;
|
||||
enum uhd_dev_type dev_type;
|
||||
|
||||
size_t sps, chans;
|
||||
size_t tx_sps, rx_sps, chans;
|
||||
double tx_rate, rx_rate;
|
||||
|
||||
double tx_gain_min, tx_gain_max;
|
||||
@@ -357,6 +287,7 @@ private:
|
||||
|
||||
std::vector<double> tx_gains, rx_gains;
|
||||
std::vector<double> tx_freqs, rx_freqs;
|
||||
std::vector<std::string> tx_paths, rx_paths;
|
||||
size_t tx_spp, rx_spp;
|
||||
|
||||
bool started;
|
||||
@@ -370,8 +301,9 @@ private:
|
||||
std::vector<smpl_buf *> rx_buffers;
|
||||
|
||||
void init_gains();
|
||||
int set_master_clk(double rate);
|
||||
int set_rates(double tx_rate, double rx_rate);
|
||||
void set_channels(bool swap);
|
||||
void set_rates();
|
||||
bool set_antennas();
|
||||
bool parse_dev_type();
|
||||
bool flush_recv(size_t num_pkts);
|
||||
int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
|
||||
@@ -383,7 +315,7 @@ private:
|
||||
bool set_freq(double freq, size_t chan, bool tx);
|
||||
|
||||
Thread *async_event_thrd;
|
||||
bool diversity;
|
||||
InterfaceType iface;
|
||||
Mutex tune_lock;
|
||||
};
|
||||
|
||||
@@ -399,7 +331,8 @@ void *async_event_loop(uhd_device *dev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
#ifndef USE_UHD_3_11
|
||||
/*
|
||||
Catch and drop underrun 'U' and overrun 'O' messages from stdout
|
||||
since we already report using the logging facility. Direct
|
||||
everything else appropriately.
|
||||
@@ -420,6 +353,7 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void thread_enable_cancel(bool cancel)
|
||||
{
|
||||
@@ -427,17 +361,23 @@ static void thread_enable_cancel(bool cancel)
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
}
|
||||
|
||||
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
|
||||
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
|
||||
InterfaceType iface, size_t chans, double offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths)
|
||||
: tx_gain_min(0.0), tx_gain_max(0.0),
|
||||
rx_gain_min(0.0), rx_gain_max(0.0),
|
||||
tx_spp(0), rx_spp(0),
|
||||
started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
|
||||
prev_ts(0,0), ts_initial(0), ts_offset(0)
|
||||
prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
|
||||
{
|
||||
this->sps = sps;
|
||||
this->tx_sps = tx_sps;
|
||||
this->rx_sps = rx_sps;
|
||||
this->chans = chans;
|
||||
this->offset = offset;
|
||||
this->diversity = diversity;
|
||||
this->iface = iface;
|
||||
this->tx_paths = tx_paths;
|
||||
this->rx_paths = rx_paths;
|
||||
}
|
||||
|
||||
uhd_device::~uhd_device()
|
||||
@@ -496,72 +436,56 @@ void uhd_device::init_gains()
|
||||
|
||||
}
|
||||
|
||||
int uhd_device::set_master_clk(double clk_rate)
|
||||
void uhd_device::set_rates()
|
||||
{
|
||||
double actual, offset, limit = 1.0;
|
||||
dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
|
||||
if (desc.mcr != 0.0)
|
||||
usrp_dev->set_master_clock_rate(desc.mcr);
|
||||
|
||||
try {
|
||||
usrp_dev->set_master_clock_rate(clk_rate);
|
||||
} catch (const std::exception &ex) {
|
||||
LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
|
||||
LOG(ALERT) << ex.what();
|
||||
return -1;
|
||||
}
|
||||
tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
|
||||
rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
|
||||
|
||||
actual = usrp_dev->get_master_clock_rate();
|
||||
offset = fabs(clk_rate - actual);
|
||||
usrp_dev->set_tx_rate(tx_rate);
|
||||
usrp_dev->set_rx_rate(rx_rate);
|
||||
tx_rate = usrp_dev->get_tx_rate();
|
||||
rx_rate = usrp_dev->get_rx_rate();
|
||||
|
||||
if (offset > limit) {
|
||||
LOG(ALERT) << "Failed to set master clock rate";
|
||||
LOG(ALERT) << "Requested clock rate " << clk_rate;
|
||||
LOG(ALERT) << "Actual clock rate " << actual;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
|
||||
LOG(INFO) << "Rates configured for " << desc.str;
|
||||
}
|
||||
|
||||
int uhd_device::set_rates(double tx_rate, double rx_rate)
|
||||
bool uhd_device::set_antennas()
|
||||
{
|
||||
double offset_limit = 1.0;
|
||||
double tx_offset, rx_offset;
|
||||
unsigned int i;
|
||||
|
||||
/* B2XX and E1xx are the only device where we set FPGA clocking */
|
||||
if ((dev_type == B200) || (dev_type == B205) || (dev_type == B210) || (dev_type == E3XX)) {
|
||||
if (set_master_clk(B2XX_CLK_RT) < 0)
|
||||
return -1;
|
||||
}
|
||||
else if (dev_type == E1XX) {
|
||||
if (set_master_clk(E1XX_CLK_RT) < 0)
|
||||
return -1;
|
||||
for (i = 0; i < tx_paths.size(); i++) {
|
||||
if (tx_paths[i] == "")
|
||||
continue;
|
||||
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
|
||||
if (!setTxAntenna(tx_paths[i], i)) {
|
||||
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set sample rates
|
||||
try {
|
||||
usrp_dev->set_tx_rate(tx_rate);
|
||||
usrp_dev->set_rx_rate(rx_rate);
|
||||
} catch (const std::exception &ex) {
|
||||
LOG(ALERT) << "UHD rate setting failed";
|
||||
LOG(ALERT) << ex.what();
|
||||
return -1;
|
||||
for (i = 0; i < rx_paths.size(); i++) {
|
||||
if (rx_paths[i] == "")
|
||||
continue;
|
||||
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
|
||||
if (!setRxAntenna(rx_paths[i], i)) {
|
||||
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->tx_rate = usrp_dev->get_tx_rate();
|
||||
this->rx_rate = usrp_dev->get_rx_rate();
|
||||
|
||||
tx_offset = fabs(this->tx_rate - tx_rate);
|
||||
rx_offset = fabs(this->rx_rate - rx_rate);
|
||||
if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
|
||||
LOG(ALERT) << "Actual sample rate differs from desired rate";
|
||||
LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
|
||||
<< this->rx_rate << ")";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
LOG(INFO) << "Antennas configured successfully";
|
||||
return true;
|
||||
}
|
||||
|
||||
double uhd_device::setTxGain(double db, size_t chan)
|
||||
{
|
||||
if (iface == MULTI_ARFCN)
|
||||
chan = 0;
|
||||
|
||||
if (chan >= tx_gains.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel" << chan;
|
||||
return 0.0f;
|
||||
@@ -608,6 +532,9 @@ double uhd_device::setRxGain(double db, size_t chan)
|
||||
|
||||
double uhd_device::getRxGain(size_t chan)
|
||||
{
|
||||
if (iface == MULTI_ARFCN)
|
||||
chan = 0;
|
||||
|
||||
if (chan >= rx_gains.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return 0.0f;
|
||||
@@ -624,86 +551,106 @@ double uhd_device::getRxGain(size_t chan)
|
||||
*/
|
||||
bool uhd_device::parse_dev_type()
|
||||
{
|
||||
std::string mboard_str, dev_str;
|
||||
uhd::property_tree::sptr prop_tree;
|
||||
size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
|
||||
b100_str, b200_str, b205_str, b210_str, x300_str, x310_str, umtrx_str;
|
||||
uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
|
||||
std::string devString = prop_tree->access<std::string>("/name").get();
|
||||
std::string mboardString = usrp_dev->get_mboard_name();
|
||||
|
||||
prop_tree = usrp_dev->get_device()->get_tree();
|
||||
dev_str = prop_tree->access<std::string>("/name").get();
|
||||
mboard_str = usrp_dev->get_mboard_name();
|
||||
const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
|
||||
{ "B100", { B100, TX_WINDOW_USRP1 } },
|
||||
{ "B200", { B200, TX_WINDOW_USRP1 } },
|
||||
{ "B200mini", { B200, TX_WINDOW_USRP1 } },
|
||||
{ "B205mini", { B200, TX_WINDOW_USRP1 } },
|
||||
{ "B210", { B210, TX_WINDOW_USRP1 } },
|
||||
{ "E100", { E1XX, TX_WINDOW_FIXED } },
|
||||
{ "E110", { E1XX, TX_WINDOW_FIXED } },
|
||||
{ "E310", { E3XX, TX_WINDOW_FIXED } },
|
||||
{ "E3XX", { E3XX, TX_WINDOW_FIXED } },
|
||||
{ "X300", { X3XX, TX_WINDOW_FIXED } },
|
||||
{ "X310", { X3XX, TX_WINDOW_FIXED } },
|
||||
{ "USRP2", { USRP2, TX_WINDOW_FIXED } },
|
||||
{ "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
|
||||
{ "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
|
||||
};
|
||||
|
||||
usrp1_str = dev_str.find("USRP1");
|
||||
usrp2_str = dev_str.find("USRP2");
|
||||
b100_str = mboard_str.find("B100");
|
||||
b200_str = mboard_str.find("B200");
|
||||
b205_str = mboard_str.find("B205");
|
||||
b210_str = mboard_str.find("B210");
|
||||
e100_str = mboard_str.find("E100");
|
||||
e110_str = mboard_str.find("E110");
|
||||
e310_str = mboard_str.find("E310");
|
||||
x300_str = mboard_str.find("X300");
|
||||
x310_str = mboard_str.find("X310");
|
||||
umtrx_str = dev_str.find("UmTRX");
|
||||
// Compare UHD motherboard and device strings */
|
||||
auto mapIter = devStringMap.begin();
|
||||
while (mapIter != devStringMap.end()) {
|
||||
if (devString.find(mapIter->first) != std::string::npos ||
|
||||
mboardString.find(mapIter->first) != std::string::npos) {
|
||||
dev_type = std::get<0>(mapIter->second);
|
||||
tx_window = std::get<1>(mapIter->second);
|
||||
return true;
|
||||
}
|
||||
mapIter++;
|
||||
}
|
||||
|
||||
if (usrp1_str != std::string::npos) {
|
||||
LOG(ALERT) << "USRP1 is not supported using the UHD driver";
|
||||
LOG(ALERT) << "Please compile with GNU Radio libusrp support";
|
||||
dev_type = USRP1;
|
||||
LOG(ALERT) << "Unsupported device " << devString;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for UHD version > 3.9.0 for E3XX support
|
||||
*/
|
||||
static bool uhd_e3xx_version_chk()
|
||||
{
|
||||
std::string ver = uhd::get_version_string();
|
||||
std::string major_str(ver.begin(), ver.begin() + 3);
|
||||
std::string minor_str(ver.begin() + 4, ver.begin() + 7);
|
||||
|
||||
int major_val = atoi(major_str.c_str());
|
||||
int minor_val = atoi(minor_str.c_str());
|
||||
|
||||
if (major_val < 3)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (b100_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_USRP1;
|
||||
dev_type = B100;
|
||||
} else if (b200_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_USRP1;
|
||||
dev_type = B200;
|
||||
} else if (b205_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_USRP1;
|
||||
dev_type = B205;
|
||||
} else if (b210_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_USRP1;
|
||||
dev_type = B210;
|
||||
} else if (e100_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = E1XX;
|
||||
} else if (e110_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = E1XX;
|
||||
} else if (usrp2_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = USRP2;
|
||||
} else if (e310_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = E3XX;
|
||||
} else if (x300_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = X3XX;
|
||||
} else if (x310_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = X3XX;
|
||||
} else if (umtrx_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = UMTRX;
|
||||
} else {
|
||||
LOG(ALERT) << "Unknown UHD device type " << dev_str;
|
||||
if (minor_val < 9)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tx_window == TX_WINDOW_USRP1) {
|
||||
LOG(INFO) << "Using USRP1 type transmit window for "
|
||||
<< dev_str << " " << mboard_str;
|
||||
} else {
|
||||
LOG(INFO) << "Using fixed transmit window for "
|
||||
<< dev_str << " " << mboard_str;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
||||
void uhd_device::set_channels(bool swap)
|
||||
{
|
||||
if (iface == MULTI_ARFCN) {
|
||||
if (dev_type != B200 && dev_type != B210)
|
||||
throw std::invalid_argument("Device does not support MCBTS");
|
||||
dev_type = B2XX_MCBTS;
|
||||
chans = 1;
|
||||
}
|
||||
|
||||
if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
|
||||
throw std::invalid_argument("Device does not support number of requested channels");
|
||||
|
||||
std::string subdev_string;
|
||||
switch (dev_type) {
|
||||
case B210:
|
||||
case E3XX:
|
||||
if (chans == 1)
|
||||
subdev_string = swap ? "A:B" : "A:A";
|
||||
else if (chans == 2)
|
||||
subdev_string = swap ? "A:B A:A" : "A:A A:B";
|
||||
break;
|
||||
case X3XX:
|
||||
case UMTRX:
|
||||
if (chans == 1)
|
||||
subdev_string = swap ? "B:0" : "A:0";
|
||||
else if (chans == 2)
|
||||
subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!subdev_string.empty()) {
|
||||
uhd::usrp::subdev_spec_t spec(subdev_string);
|
||||
usrp_dev->set_tx_subdev_spec(spec);
|
||||
usrp_dev->set_rx_subdev_spec(spec);
|
||||
}
|
||||
}
|
||||
|
||||
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
{
|
||||
const char *refstr;
|
||||
|
||||
// Find UHD devices
|
||||
uhd::device_addr_t addr(args);
|
||||
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
|
||||
@@ -725,14 +672,20 @@ int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
||||
if (!parse_dev_type())
|
||||
return -1;
|
||||
|
||||
// Verify and set channels
|
||||
if ((dev_type == B210) && (chans == 2)) {
|
||||
} else if ((dev_type == UMTRX) && (chans == 2)) {
|
||||
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
|
||||
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
||||
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
||||
} else if (chans != 1) {
|
||||
LOG(ALERT) << "Invalid channel combination for device";
|
||||
if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
|
||||
LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
set_channels(swap_channels);
|
||||
} catch (const std::exception &e) {
|
||||
LOG(ALERT) << "Channel setting failed - " << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!set_antennas()) {
|
||||
LOG(ALERT) << "UHD antenna setting failed";
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -742,29 +695,41 @@ int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
||||
rx_gains.resize(chans);
|
||||
rx_buffers.resize(chans);
|
||||
|
||||
if (extref)
|
||||
usrp_dev->set_clock_source("external");
|
||||
|
||||
// Set rates
|
||||
double _rx_rate;
|
||||
double _tx_rate = select_rate(dev_type, sps);
|
||||
if (diversity)
|
||||
_rx_rate = select_rate(dev_type, 1, true);
|
||||
else
|
||||
_rx_rate = _tx_rate / sps;
|
||||
|
||||
if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
|
||||
switch (ref) {
|
||||
case REF_INTERNAL:
|
||||
refstr = "internal";
|
||||
break;
|
||||
case REF_EXTERNAL:
|
||||
refstr = "external";
|
||||
break;
|
||||
case REF_GPS:
|
||||
refstr = "gpsdo";
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Invalid reference type";
|
||||
return -1;
|
||||
if (set_rates(_tx_rate, _rx_rate) < 0)
|
||||
}
|
||||
|
||||
usrp_dev->set_clock_source(refstr);
|
||||
|
||||
try {
|
||||
set_rates();
|
||||
} catch (const std::exception &e) {
|
||||
LOG(ALERT) << "UHD rate setting failed - " << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set RF frontend bandwidth
|
||||
if (dev_type == UMTRX) {
|
||||
// Setting LMS6002D LPF to 500kHz gives us the best signal quality
|
||||
for (size_t i = 0; i < chans; i++) {
|
||||
usrp_dev->set_tx_bandwidth(500*1000*2, i);
|
||||
if (!diversity)
|
||||
usrp_dev->set_rx_bandwidth(500*1000*2, i);
|
||||
usrp_dev->set_rx_bandwidth(500*1000*2, i);
|
||||
}
|
||||
} else if (dev_type == LIMESDR) {
|
||||
for (size_t i = 0; i < chans; i++) {
|
||||
usrp_dev->set_tx_bandwidth(5e6, i);
|
||||
usrp_dev->set_rx_bandwidth(5e6, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,23 +750,14 @@ int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||
rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
|
||||
|
||||
// Set receive chain sample offset
|
||||
double offset = get_dev_offset(dev_type, sps, diversity);
|
||||
if (offset == 0.0) {
|
||||
LOG(ERR) << "Unsupported configuration, no correction applied";
|
||||
ts_offset = 0;
|
||||
} else {
|
||||
ts_offset = (TIMESTAMP) (offset * rx_rate);
|
||||
}
|
||||
|
||||
// Initialize and shadow gain values
|
||||
// Initialize and shadow gain values
|
||||
init_gains();
|
||||
|
||||
// Print configuration
|
||||
LOG(INFO) << "\n" << usrp_dev->get_pp_string();
|
||||
|
||||
if (diversity)
|
||||
return DIVERSITY;
|
||||
if (iface == MULTI_ARFCN)
|
||||
return MULTI_ARFCN;
|
||||
|
||||
switch (dev_type) {
|
||||
case B100:
|
||||
@@ -810,10 +766,10 @@ int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
||||
case X3XX:
|
||||
return RESAMP_100M;
|
||||
case B200:
|
||||
case B205:
|
||||
case B210:
|
||||
case E1XX:
|
||||
case E3XX:
|
||||
case LIMESDR:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -883,9 +839,10 @@ bool uhd_device::start()
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef USE_UHD_3_11
|
||||
// Register msg handler
|
||||
uhd::msg::register_handler(&uhd_msg_handler);
|
||||
|
||||
#endif
|
||||
// Start asynchronous event (underrun check) loop
|
||||
async_event_thrd = new Thread();
|
||||
async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
|
||||
@@ -928,8 +885,6 @@ void uhd_device::setPriority(float prio)
|
||||
|
||||
int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
|
||||
{
|
||||
uhd::time_spec_t ts;
|
||||
|
||||
if (!num_smpls) {
|
||||
LOG(ERR) << str_code(md);
|
||||
|
||||
@@ -952,18 +907,21 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
|
||||
return ERROR_UNRECOVERABLE;
|
||||
}
|
||||
|
||||
ts = md.time_spec;
|
||||
|
||||
// Monotonicity check
|
||||
if (ts < prev_ts) {
|
||||
if (md.time_spec < prev_ts) {
|
||||
LOG(ALERT) << "UHD: Loss of monotonic time";
|
||||
LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
|
||||
LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
|
||||
<< "Previous time: " << prev_ts.get_real_secs();
|
||||
return ERROR_TIMING;
|
||||
} else {
|
||||
prev_ts = ts;
|
||||
}
|
||||
|
||||
// Workaround for UHD tick rounding bug
|
||||
TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
|
||||
if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
|
||||
md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
|
||||
|
||||
prev_ts = md.time_spec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1013,7 +971,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
|
||||
rx_pkt_cnt++;
|
||||
|
||||
// Check for errors
|
||||
// Check for errors
|
||||
rc = check_rx_md_err(metadata, num_smpls);
|
||||
switch (rc) {
|
||||
case ERROR_UNRECOVERABLE:
|
||||
@@ -1157,7 +1115,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
||||
|
||||
/* Find center frequency between channels */
|
||||
rf_spread = fabs(freqs[!chan] - freq);
|
||||
if (rf_spread > B2XX_CLK_RT) {
|
||||
if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
|
||||
LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
|
||||
return treq;
|
||||
}
|
||||
@@ -1252,8 +1210,100 @@ double uhd_device::getRxFreq(size_t chan)
|
||||
return rx_freqs[chan];
|
||||
}
|
||||
|
||||
bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
|
||||
{
|
||||
std::vector<std::string> avail;
|
||||
if (chan >= rx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
avail = usrp_dev->get_rx_antennas(chan);
|
||||
if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
|
||||
LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
|
||||
LOG(INFO) << "Available Rx antennas: ";
|
||||
for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
|
||||
LOG(INFO) << "- '" << *i << "'";
|
||||
return false;
|
||||
}
|
||||
usrp_dev->set_rx_antenna(ant, chan);
|
||||
rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
|
||||
|
||||
if (ant != rx_paths[chan]) {
|
||||
LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string uhd_device::getRxAntenna(size_t chan)
|
||||
{
|
||||
if (chan >= rx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return "";
|
||||
}
|
||||
return usrp_dev->get_rx_antenna(chan);
|
||||
}
|
||||
|
||||
bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
|
||||
{
|
||||
std::vector<std::string> avail;
|
||||
if (chan >= tx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
avail = usrp_dev->get_tx_antennas(chan);
|
||||
if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
|
||||
LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
|
||||
LOG(INFO) << "Available Tx antennas: ";
|
||||
for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
|
||||
LOG(INFO) << "- '" << *i << "'";
|
||||
return false;
|
||||
}
|
||||
usrp_dev->set_tx_antenna(ant, chan);
|
||||
tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
|
||||
|
||||
if (ant != tx_paths[chan]) {
|
||||
LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string uhd_device::getTxAntenna(size_t chan)
|
||||
{
|
||||
if (chan >= tx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return "";
|
||||
}
|
||||
return usrp_dev->get_tx_antenna(chan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only allow sampling the Rx path lower than Tx and not vice-versa.
|
||||
* Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
|
||||
* combination.
|
||||
*/
|
||||
TIMESTAMP uhd_device::initialWriteTimestamp()
|
||||
{
|
||||
if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
|
||||
return ts_initial;
|
||||
else
|
||||
return ts_initial * tx_sps;
|
||||
}
|
||||
|
||||
TIMESTAMP uhd_device::initialReadTimestamp()
|
||||
{
|
||||
return ts_initial;
|
||||
}
|
||||
|
||||
double uhd_device::fullScaleInputValue()
|
||||
{
|
||||
if (dev_type == LIMESDR)
|
||||
return (double) SHRT_MAX * LIMESDR_TX_AMPL;
|
||||
if (dev_type == UMTRX)
|
||||
return (double) SHRT_MAX * UMTRX_TX_AMPL;
|
||||
else
|
||||
@@ -1517,8 +1567,10 @@ std::string smpl_buf::str_code(ssize_t code)
|
||||
}
|
||||
}
|
||||
|
||||
RadioDevice *RadioDevice::make(size_t sps, size_t chans,
|
||||
bool diversity, double offset)
|
||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||
InterfaceType iface, size_t chans, double offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths)
|
||||
{
|
||||
return new uhd_device(sps, chans, diversity, offset);
|
||||
return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths);
|
||||
}
|
||||
|
||||
@@ -27,17 +27,16 @@
|
||||
Compilation Flags
|
||||
|
||||
SWLOOPBACK compile for software loopback testing
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "Logger.h"
|
||||
#include "Threads.h"
|
||||
#include "USRPDevice.h"
|
||||
|
||||
#include <Logger.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
@@ -59,7 +58,7 @@ const dboardConfigType dboardConfig = TXA_RXB;
|
||||
|
||||
const double USRPDevice::masterClockRate = 52.0e6;
|
||||
|
||||
USRPDevice::USRPDevice(size_t sps, size_t, bool)
|
||||
USRPDevice::USRPDevice(size_t sps)
|
||||
{
|
||||
LOG(INFO) << "creating USRP device...";
|
||||
|
||||
@@ -81,7 +80,7 @@ USRPDevice::USRPDevice(size_t sps, size_t, bool)
|
||||
else
|
||||
pingOffset = 0;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
#ifdef SWLOOPBACK
|
||||
samplePeriod = 1.0e6/actualSampleRate;
|
||||
loopbackBufferSize = 0;
|
||||
gettimeofday(&lastReadTime,NULL);
|
||||
@@ -89,14 +88,14 @@ USRPDevice::USRPDevice(size_t sps, size_t, bool)
|
||||
#endif
|
||||
}
|
||||
|
||||
int USRPDevice::open(const std::string &, bool, bool)
|
||||
int USRPDevice::open(const std::string &, int, bool)
|
||||
{
|
||||
writeLock.unlock();
|
||||
|
||||
LOG(INFO) << "opening USRP device..";
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
string rbf = "std_inband.rbf";
|
||||
//string rbf = "inband_1rxhb_1tx.rbf";
|
||||
//string rbf = "inband_1rxhb_1tx.rbf";
|
||||
m_uRx.reset();
|
||||
if (!skipRx) {
|
||||
try {
|
||||
@@ -145,7 +144,7 @@ int USRPDevice::open(const std::string &, bool, bool)
|
||||
|
||||
if (!skipRx) m_uRx->stop();
|
||||
m_uTx->stop();
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
switch (dboardConfig) {
|
||||
@@ -176,19 +175,19 @@ int USRPDevice::open(const std::string &, bool, bool)
|
||||
samplesRead = 0;
|
||||
samplesWritten = 0;
|
||||
started = false;
|
||||
|
||||
|
||||
return NORMAL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool USRPDevice::start()
|
||||
bool USRPDevice::start()
|
||||
{
|
||||
LOG(INFO) << "starting USRP...";
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx && !skipRx) return false;
|
||||
if (!m_uTx) return false;
|
||||
|
||||
|
||||
if (!skipRx) m_uRx->stop();
|
||||
m_uTx->stop();
|
||||
|
||||
@@ -218,8 +217,8 @@ bool USRPDevice::start()
|
||||
hi32Timestamp = 0;
|
||||
isAligned = false;
|
||||
|
||||
|
||||
if (!skipRx)
|
||||
|
||||
if (!skipRx)
|
||||
started = (m_uRx->start() && m_uTx->start());
|
||||
else
|
||||
started = m_uTx->start();
|
||||
@@ -230,14 +229,14 @@ bool USRPDevice::start()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool USRPDevice::stop()
|
||||
bool USRPDevice::stop()
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx) return false;
|
||||
if (!m_uTx) return false;
|
||||
|
||||
|
||||
delete[] currData;
|
||||
|
||||
|
||||
started = !(m_uRx->stop() && m_uTx->stop());
|
||||
return !started;
|
||||
#else
|
||||
@@ -258,7 +257,7 @@ double USRPDevice::minTxGain()
|
||||
double USRPDevice::maxRxGain()
|
||||
{
|
||||
return m_dbRx->gain_max();
|
||||
}
|
||||
}
|
||||
|
||||
double USRPDevice::minRxGain()
|
||||
{
|
||||
@@ -314,28 +313,68 @@ double USRPDevice::setRxGain(double dB, size_t chan)
|
||||
return dB;
|
||||
}
|
||||
|
||||
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
|
||||
{
|
||||
if (chan >= rx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
LOG(ALERT) << "Not implemented";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string USRPDevice::getRxAntenna(size_t chan)
|
||||
{
|
||||
if (chan >= rx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return "";
|
||||
}
|
||||
LOG(ALERT) << "Not implemented";
|
||||
return "";
|
||||
}
|
||||
|
||||
bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
|
||||
{
|
||||
if (chan >= tx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
LOG(ALERT) << "Not implemented";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string USRPDevice::getTxAntenna(size_t chan)
|
||||
{
|
||||
if (chan >= tx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return "";
|
||||
}
|
||||
LOG(ALERT) << "Not implemented";
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Assumes sequential reads
|
||||
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx)
|
||||
return 0;
|
||||
|
||||
short *buf = bufs[0];
|
||||
|
||||
timestamp += timestampOffset;
|
||||
|
||||
|
||||
if (timestamp + len < timeStart) {
|
||||
memset(buf,0,len*2*sizeof(short));
|
||||
return len;
|
||||
}
|
||||
|
||||
if (underrun) *underrun = false;
|
||||
|
||||
|
||||
uint32_t readBuf[2000];
|
||||
|
||||
|
||||
while (1) {
|
||||
//guestimate USB read size
|
||||
int readLen=0;
|
||||
@@ -345,7 +384,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
|
||||
if (readLen > 8000) readLen= (8000/512)*512;
|
||||
}
|
||||
|
||||
|
||||
// read USRP packets, parse and save A/D data as needed
|
||||
readLen = m_uRx->read((void *)readBuf,readLen,overrun);
|
||||
for(int pktNum = 0; pktNum < (readLen/512); pktNum++) {
|
||||
@@ -382,13 +421,13 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
continue;
|
||||
}
|
||||
if ((word0 >> 28) & 0x04) {
|
||||
if (underrun) *underrun = true;
|
||||
if (underrun) *underrun = true;
|
||||
LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
|
||||
}
|
||||
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
|
||||
|
||||
|
||||
if (!isAligned) continue;
|
||||
|
||||
|
||||
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
|
||||
while (cursorStart*2 > currDataSize) {
|
||||
cursorStart -= currDataSize/2;
|
||||
@@ -401,17 +440,17 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
else {
|
||||
memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
|
||||
}
|
||||
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
|
||||
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
|
||||
timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
|
||||
|
||||
LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// copy desired data to buf
|
||||
unsigned bufStart = dataStart+(timestamp-timeStart);
|
||||
if (bufStart + len < currDataSize/2) {
|
||||
if (bufStart + len < currDataSize/2) {
|
||||
LOG(DEBUG) << "bufStart: " << bufStart;
|
||||
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
|
||||
memset(data+bufStart*2,0,len*2*sizeof(short));
|
||||
@@ -429,21 +468,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
timeStart = timestamp + len;
|
||||
|
||||
return len;
|
||||
|
||||
|
||||
#else
|
||||
if (loopbackBufferSize < 2) return 0;
|
||||
int numSamples = 0;
|
||||
struct timeval currTime;
|
||||
gettimeofday(&currTime,NULL);
|
||||
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
|
||||
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
|
||||
(currTime.tv_usec - lastReadTime.tv_usec);
|
||||
if (timeElapsed < samplePeriod) {return 0;}
|
||||
int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
|
||||
if (numSamplesToRead < len) return 0;
|
||||
|
||||
|
||||
if (numSamplesToRead > len) numSamplesToRead = len;
|
||||
if (numSamplesToRead > loopbackBufferSize/2) {
|
||||
firstRead =false;
|
||||
firstRead =false;
|
||||
numSamplesToRead = loopbackBufferSize/2;
|
||||
}
|
||||
memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
|
||||
@@ -461,7 +500,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
firstRead = true;
|
||||
}
|
||||
samplesRead += numSamples;
|
||||
|
||||
|
||||
return numSamples;
|
||||
#endif
|
||||
}
|
||||
@@ -472,7 +511,7 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
{
|
||||
writeLock.lock();
|
||||
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uTx)
|
||||
return 0;
|
||||
|
||||
@@ -519,14 +558,14 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
|
||||
samplesWritten += retVal;
|
||||
loopbackBufferSize += retVal*2;
|
||||
|
||||
|
||||
return retVal;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
short data[] = {0x00,0x02,0x00,0x00};
|
||||
uint32_t *wordPtr = (uint32_t *) data;
|
||||
*wordPtr = host_to_usrp_u32(*wordPtr);
|
||||
@@ -543,7 +582,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
usrp_tune_result result;
|
||||
@@ -600,7 +639,10 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
|
||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
||||
#endif
|
||||
|
||||
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity, double)
|
||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||
InterfaceType iface, size_t chans, double offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths)
|
||||
{
|
||||
return new USRPDevice(sps, chans, diversity);
|
||||
return new USRPDevice(tx_sps);
|
||||
}
|
||||
|
||||
@@ -83,10 +83,10 @@ private:
|
||||
|
||||
double rxGain;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
#ifdef SWLOOPBACK
|
||||
short loopbackBuffer[1000000];
|
||||
int loopbackBufferSize;
|
||||
double samplePeriod;
|
||||
double samplePeriod;
|
||||
|
||||
struct timeval startTime;
|
||||
struct timeval lastReadTime;
|
||||
@@ -96,10 +96,10 @@ private:
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
USRPDevice(size_t sps, size_t chans = 1, bool diversity = false);
|
||||
USRPDevice(size_t sps);
|
||||
|
||||
/** Instantiate the USRP */
|
||||
int open(const std::string &, bool, bool);
|
||||
int open(const std::string &, int, bool);
|
||||
|
||||
/** Start the USRP */
|
||||
bool start();
|
||||
@@ -179,6 +179,18 @@ private:
|
||||
/** return minimum Rx Gain **/
|
||||
double minTxGain(void);
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
bool setRxAntenna(const std::string &ant, size_t chan = 0);
|
||||
|
||||
/* return the used RX path */
|
||||
std::string getRxAntenna(size_t chan = 0);
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
bool setTxAntenna(const std::string &ant, size_t chan = 0);
|
||||
|
||||
/* return the used RX path */
|
||||
std::string getTxAntenna(size_t chan = 0);
|
||||
|
||||
/** Return internal status values */
|
||||
inline double getTxFreq(size_t chan = 0) { return 0; }
|
||||
inline double getRxFreq(size_t chan = 0) { return 0; }
|
||||
@@ -186,7 +198,7 @@ private:
|
||||
inline double numberRead() { return samplesRead; }
|
||||
inline double numberWritten() { return samplesWritten; }
|
||||
|
||||
std::vector<std::string> tx_paths, rx_paths;
|
||||
};
|
||||
|
||||
#endif // _USRP_DEVICE_H_
|
||||
|
||||
|
||||
@@ -25,25 +25,15 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
void neon_convert_ps_si16_4n(short *, float *, float *, int);
|
||||
void neon_convert_si16_ps_4n(float *, short *, int);
|
||||
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
|
||||
void neon_convert_si16_ps_4n(float *, const short *, int);
|
||||
|
||||
#ifndef HAVE_NEON
|
||||
static void convert_si16_ps(float *out, short *in, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
out[i] = in[i];
|
||||
void convert_init(void) {
|
||||
}
|
||||
|
||||
static void convert_ps_si16(short *out, float *in, float scale, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
out[i] = in[i] * scale;
|
||||
}
|
||||
#else
|
||||
/* 4*N 16-bit signed integer conversion with remainder */
|
||||
static void neon_convert_si16_ps(float *restrict out,
|
||||
short *restrict in,
|
||||
static void neon_convert_si16_ps(float *out,
|
||||
const short *in,
|
||||
int len)
|
||||
{
|
||||
int start = len / 4 * 4;
|
||||
@@ -55,9 +45,9 @@ static void neon_convert_si16_ps(float *restrict out,
|
||||
}
|
||||
|
||||
/* 4*N 16-bit signed integer conversion with remainder */
|
||||
static void neon_convert_ps_si16(short *restrict out,
|
||||
float *restrict in,
|
||||
float *restrict scale,
|
||||
static void neon_convert_ps_si16(short *out,
|
||||
const float *in,
|
||||
const float *scale,
|
||||
int len)
|
||||
{
|
||||
int start = len / 4 * 4;
|
||||
@@ -67,9 +57,8 @@ static void neon_convert_ps_si16(short *restrict out,
|
||||
for (int i = 0; i < len % 4; i++)
|
||||
out[start + i] = (short) (in[start + i] * (*scale));
|
||||
}
|
||||
#endif
|
||||
|
||||
void convert_float_short(short *out, float *in, float scale, int len)
|
||||
void convert_float_short(short *out, const float *in, float scale, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
float q[4] = { scale, scale, scale, scale };
|
||||
@@ -79,11 +68,11 @@ void convert_float_short(short *out, float *in, float scale, int len)
|
||||
else
|
||||
neon_convert_ps_si16_4n(out, in, q, len >> 2);
|
||||
#else
|
||||
convert_ps_si16(out, in, scale, len);
|
||||
base_convert_float_short(out, in, scale, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void convert_short_float(float *out, short *in, int len)
|
||||
void convert_short_float(float *out, const short *in, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if (len % 4)
|
||||
@@ -91,6 +80,6 @@ void convert_short_float(float *out, short *in, int len)
|
||||
else
|
||||
neon_convert_si16_ps_4n(out, in, len >> 2);
|
||||
#else
|
||||
convert_si16_ps(out, in, len);
|
||||
base_convert_short_float(out, in, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -58,6 +58,13 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
|
||||
}
|
||||
#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,
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
#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];
|
||||
}
|
||||
|
||||
@@ -27,4 +27,6 @@ int base_convolve_complex(const float *x, int x_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
void convolve_init(void);
|
||||
|
||||
#endif /* _CONVOLVE_H_ */
|
||||
|
||||
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_ */
|
||||
@@ -27,91 +27,68 @@
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#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 except for
|
||||
* ARM and non-SIMD enabled architectures.
|
||||
* downsampled to 1 sps. Default to 4 sps for all cases.
|
||||
*/
|
||||
#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
|
||||
#define DEFAULT_SPS 1
|
||||
#else
|
||||
#define DEFAULT_SPS 4
|
||||
#endif
|
||||
#define DEFAULT_TX_SPS 4
|
||||
|
||||
/* Default configuration parameters
|
||||
* Note that these values are only used if the particular key does not
|
||||
* exist in the configuration database. IP port and address values will
|
||||
* typically be overwritten by the OpenBTS.db values. Other values will
|
||||
* not be in the database by default.
|
||||
/*
|
||||
* 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_EXTREF false
|
||||
#define DEFAULT_DIVERSITY false
|
||||
#define DEFAULT_CHANS 1
|
||||
|
||||
struct trx_config {
|
||||
std::string log_level;
|
||||
std::string addr;
|
||||
std::string local_addr;
|
||||
std::string remote_addr;
|
||||
std::string dev_args;
|
||||
unsigned port;
|
||||
unsigned sps;
|
||||
unsigned tx_sps;
|
||||
unsigned rx_sps;
|
||||
unsigned chans;
|
||||
unsigned rtsc;
|
||||
unsigned rach_delay;
|
||||
bool extref;
|
||||
bool gpsref;
|
||||
Transceiver::FillerType filler;
|
||||
bool diversity;
|
||||
bool mcbts;
|
||||
double offset;
|
||||
double rssi_offset;
|
||||
bool swap_channels;
|
||||
bool edge;
|
||||
int sched_rr;
|
||||
std::vector<std::string> rx_paths;
|
||||
std::vector<std::string> tx_paths;
|
||||
};
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
volatile bool gshutdown = false;
|
||||
|
||||
/* Run sanity check on configuration table
|
||||
* The global table constructor cannot provide notification in the
|
||||
* event of failure. Make sure that we can access the database,
|
||||
* write to it, and that it contains the bare minimum required keys.
|
||||
*/
|
||||
bool testConfig()
|
||||
{
|
||||
int val = 9999;
|
||||
std::string test = "asldfkjsaldkf";
|
||||
const char *key = "Log.Level";
|
||||
|
||||
/* Attempt to query */
|
||||
try {
|
||||
gConfig.getStr(key);
|
||||
} catch (...) {
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "Config: Failed query required key " << key
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Attempt to set a test value in the global config */
|
||||
if (!gConfig.set(test, val)) {
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "Config: Failed to set test key" << std::endl;
|
||||
return false;
|
||||
} else {
|
||||
gConfig.remove(test);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
@@ -121,48 +98,24 @@ bool testConfig()
|
||||
*/
|
||||
bool trx_setup_config(struct trx_config *config)
|
||||
{
|
||||
std::string refstr, fillstr, divstr;
|
||||
std::string refstr, fillstr, divstr, mcstr, edgestr;
|
||||
std::vector<std::string>::const_iterator si;
|
||||
|
||||
if (!testConfig())
|
||||
if (config->mcbts && config->chans > 5) {
|
||||
std::cout << "Unsupported number of channels" << std::endl;
|
||||
return false;
|
||||
|
||||
if (config->log_level == "")
|
||||
config->log_level = gConfig.getStr("Log.Level");
|
||||
|
||||
if (!config->port) {
|
||||
if (gConfig.defines("TRX.Port"))
|
||||
config->port = gConfig.getNum("TRX.Port");
|
||||
else
|
||||
config->port = DEFAULT_TRX_PORT;
|
||||
}
|
||||
|
||||
if (config->addr == "") {
|
||||
if (gConfig.defines("TRX.IP"))
|
||||
config->addr = gConfig.getStr("TRX.IP");
|
||||
else
|
||||
config->addr = DEFAULT_TRX_IP;
|
||||
}
|
||||
edgestr = config->edge ? "Enabled" : "Disabled";
|
||||
mcstr = config->mcbts ? "Enabled" : "Disabled";
|
||||
|
||||
if (!config->extref) {
|
||||
if (gConfig.defines("TRX.Reference"))
|
||||
config->extref = gConfig.getNum("TRX.Reference");
|
||||
else
|
||||
config->extref = DEFAULT_EXTREF;
|
||||
}
|
||||
if (config->extref)
|
||||
refstr = "External";
|
||||
else if (config->gpsref)
|
||||
refstr = "GPS";
|
||||
else
|
||||
refstr = "Internal";
|
||||
|
||||
if (!config->diversity) {
|
||||
if (gConfig.defines("TRX.Diversity"))
|
||||
config->diversity = gConfig.getNum("TRX.Diversity");
|
||||
else
|
||||
config->diversity = DEFAULT_DIVERSITY;
|
||||
}
|
||||
|
||||
/* Diversity only supported on 2 channels */
|
||||
if (config->diversity)
|
||||
config->chans = 2;
|
||||
|
||||
refstr = config->extref ? "Enabled" : "Disabled";
|
||||
divstr = config->diversity ? "Enabled" : "Disabled";
|
||||
switch (config->filler) {
|
||||
case Transceiver::FILLER_DUMMY:
|
||||
fillstr = "Dummy bursts";
|
||||
@@ -170,9 +123,15 @@ bool trx_setup_config(struct trx_config *config)
|
||||
case Transceiver::FILLER_ZERO:
|
||||
fillstr = "Disabled";
|
||||
break;
|
||||
case Transceiver::FILLER_RAND:
|
||||
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("");
|
||||
@@ -180,17 +139,28 @@ bool trx_setup_config(struct trx_config *config)
|
||||
ost << " Log Level............... " << config->log_level << std::endl;
|
||||
ost << " Device args............. " << config->dev_args << std::endl;
|
||||
ost << " TRX Base Port........... " << config->port << std::endl;
|
||||
ost << " TRX Address............. " << config->addr << std::endl;
|
||||
ost << " TRX Address............. " << config->local_addr << std::endl;
|
||||
ost << " GSM Core Address........." << config->remote_addr << std::endl;
|
||||
ost << " Channels................ " << config->chans << std::endl;
|
||||
ost << " Samples-per-Symbol...... " << config->sps << std::endl;
|
||||
ost << " External Reference...... " << refstr << 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 << " Diversity............... " << divstr << 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;
|
||||
ost << " Tx Antennas.............";
|
||||
for (si = config->tx_paths.begin(); si != config->tx_paths.end(); ++si)
|
||||
ost << " '" << ((*si != "") ? *si : "<default>") << "'";
|
||||
ost << std::endl;
|
||||
ost << " Rx Antennas.............";
|
||||
for (si = config->rx_paths.begin(); si != config->rx_paths.end(); ++si)
|
||||
ost << " '" << ((*si != "") ? *si : "<default>") << "'";
|
||||
ost << std::endl;
|
||||
|
||||
std::cout << ost << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -208,16 +178,17 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
|
||||
|
||||
switch (type) {
|
||||
case RadioDevice::NORMAL:
|
||||
radio = new RadioInterface(usrp, config->sps, config->chans);
|
||||
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->sps, config->chans);
|
||||
radio = new RadioInterfaceResamp(usrp, config->tx_sps,
|
||||
config->rx_sps);
|
||||
break;
|
||||
case RadioDevice::DIVERSITY:
|
||||
radio = new RadioInterfaceDiversity(usrp,
|
||||
config->sps, config->chans);
|
||||
case RadioDevice::MULTI_ARFCN:
|
||||
radio = new RadioInterfaceMulti(usrp, config->tx_sps,
|
||||
config->rx_sps, config->chans);
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported radio interface configuration";
|
||||
@@ -243,9 +214,12 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
|
||||
Transceiver *trx;
|
||||
VectorFIFO *fifo;
|
||||
|
||||
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
|
||||
config->chans, GSM::Time(3,0), radio, config->rssi_offset);
|
||||
if (!trx->init(config->filler, config->rtsc)) {
|
||||
trx = new Transceiver(config->port, config->local_addr.c_str(),
|
||||
config->remote_addr.c_str(), config->tx_sps,
|
||||
config->rx_sps, config->chans, GSM::Time(3,0),
|
||||
radio, config->rssi_offset);
|
||||
if (!trx->init(config->filler, config->rtsc,
|
||||
config->rach_delay, config->edge)) {
|
||||
LOG(ALERT) << "Failed to initialize transceiver";
|
||||
delete trx;
|
||||
return NULL;
|
||||
@@ -282,6 +256,21 @@ static void setup_signal_handlers()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static std::vector<std::string> comma_delimited_to_vector(char* opt) {
|
||||
std::string str = std::string(opt);
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss(str);
|
||||
|
||||
while( ss.good() )
|
||||
{
|
||||
std::string substr;
|
||||
getline(ss, substr, ',');
|
||||
result.push_back(substr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
fprintf(stdout, "Options:\n"
|
||||
@@ -289,35 +278,54 @@ static void print_help()
|
||||
" -a UHD device args\n"
|
||||
" -l Logging level (%s)\n"
|
||||
" -i IP address of GSM core\n"
|
||||
" -j IP address of osmo-trx\n"
|
||||
" -p Base port number\n"
|
||||
" -d Enable dual channel diversity receiver\n"
|
||||
" -e Enable EDGE receiver\n"
|
||||
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
|
||||
" -x Enable external 10 MHz reference\n"
|
||||
" -s Samples-per-symbol (1 or 4)\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 burst test mode with TSC\n"
|
||||
" -r Random Normal Burst test mode with TSC\n"
|
||||
" -A Random Access Burst test mode with delay\n"
|
||||
" -R RSSI to dBm offset in dB (default=0)\n"
|
||||
" -S Swap channels (UmTRX only)\n",
|
||||
" -S Swap channels (UmTRX only)\n"
|
||||
" -t SCHED_RR real-time priority (1..32)\n"
|
||||
" -y comma-delimited list of Tx paths (num elements matches -c)\n"
|
||||
" -z comma-delimited list of Rx paths (num elements matches -c)\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
{
|
||||
int option;
|
||||
bool tx_path_set = false, rx_path_set = false;
|
||||
|
||||
config->port = 0;
|
||||
config->sps = DEFAULT_SPS;
|
||||
config->log_level = "NOTICE";
|
||||
config->local_addr = DEFAULT_TRX_IP;
|
||||
config->remote_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->diversity = false;
|
||||
config->mcbts = false;
|
||||
config->offset = 0.0;
|
||||
config->rssi_offset = 0.0;
|
||||
config->swap_channels = false;
|
||||
config->edge = false;
|
||||
config->sched_rr = -1;
|
||||
config->tx_paths = std::vector<std::string>(DEFAULT_CHANS, "");
|
||||
config->rx_paths = std::vector<std::string>(DEFAULT_CHANS, "");
|
||||
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:S")) != -1) {
|
||||
while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:")) != -1) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
@@ -330,7 +338,10 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
config->log_level = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
config->addr = optarg;
|
||||
config->remote_addr = optarg;
|
||||
break;
|
||||
case 'j':
|
||||
config->local_addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
config->port = atoi(optarg);
|
||||
@@ -338,12 +349,15 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
case 'c':
|
||||
config->chans = atoi(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
config->diversity = true;
|
||||
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;
|
||||
@@ -351,11 +365,18 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
config->offset = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
config->sps = atoi(optarg);
|
||||
config->tx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
config->rx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
config->rtsc = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_RAND;
|
||||
config->filler = Transceiver::FILLER_NORM_RAND;
|
||||
break;
|
||||
case 'A':
|
||||
config->rach_delay = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_ACCESS_RAND;
|
||||
break;
|
||||
case 'R':
|
||||
config->rssi_offset = atof(optarg);
|
||||
@@ -363,35 +384,134 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
case 'S':
|
||||
config->swap_channels = true;
|
||||
break;
|
||||
case 'e':
|
||||
config->edge = true;
|
||||
break;
|
||||
case 't':
|
||||
config->sched_rr = atoi(optarg);
|
||||
break;
|
||||
case 'y':
|
||||
config->tx_paths = comma_delimited_to_vector(optarg);
|
||||
tx_path_set = true;
|
||||
break;
|
||||
case 'z':
|
||||
config->rx_paths = comma_delimited_to_vector(optarg);
|
||||
rx_path_set = true;
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((config->sps != 1) && (config->sps != 4)) {
|
||||
printf("Unsupported samples-per-symbol %i\n\n", config->sps);
|
||||
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_NORM_RAND))
|
||||
config->filler = Transceiver::FILLER_EDGE_RAND;
|
||||
|
||||
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);
|
||||
print_help();
|
||||
exit(0);
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if (config->rach_delay > 68) {
|
||||
printf("RACH delay is too big %i\n\n", config->rach_delay);
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if (!tx_path_set) {
|
||||
config->tx_paths = std::vector<std::string>(config->chans, "");
|
||||
} else if (config->tx_paths.size() != config->chans) {
|
||||
printf("Num of channels and num of Tx Antennas doesn't match\n\n");
|
||||
goto bad_config;
|
||||
}
|
||||
if (!rx_path_set) {
|
||||
config->rx_paths = std::vector<std::string>(config->chans, "");
|
||||
} else if (config->rx_paths.size() != config->chans) {
|
||||
printf("Num of channels and num of Rx Antennas doesn't match\n\n");
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
bad_config:
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int set_sched_rr(int prio)
|
||||
{
|
||||
struct sched_param param;
|
||||
int rc;
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = prio;
|
||||
printf("Setting SCHED_RR priority(%d)\n", param.sched_priority);
|
||||
rc = sched_setscheduler(getpid(), SCHED_RR, ¶m);
|
||||
if (rc != 0) {
|
||||
std::cerr << "Config: Setting SCHED_RR failed" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int type, chans;
|
||||
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");
|
||||
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||
if (__builtin_cpu_supports("sse3"))
|
||||
printf(" and supported by CPU\n");
|
||||
else
|
||||
printf(", but not supported by CPU\n");
|
||||
#else
|
||||
printf(", but runtime SIMD detection disabled\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE4_1
|
||||
printf("Info: SSE4.1 support compiled in");
|
||||
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||
if (__builtin_cpu_supports("sse4.1"))
|
||||
printf(" and supported by CPU\n");
|
||||
else
|
||||
printf(", but not supported by CPU\n");
|
||||
#else
|
||||
printf(", but runtime SIMD detection disabled\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
convolve_init();
|
||||
convert_init();
|
||||
|
||||
handle_options(argc, argv, &config);
|
||||
|
||||
if (config.sched_rr != -1) {
|
||||
if (set_sched_rr(config.sched_rr) < 0)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
setup_signal_handlers();
|
||||
|
||||
/* Check database sanity */
|
||||
@@ -400,14 +520,24 @@ int main(int argc, char *argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
|
||||
gLogInit(config.log_level.c_str());
|
||||
|
||||
srandom(time(NULL));
|
||||
|
||||
/* Create the low level device object */
|
||||
usrp = RadioDevice::make(config.sps, config.chans,
|
||||
config.diversity, config.offset);
|
||||
type = usrp->open(config.dev_args, config.extref, config.swap_channels);
|
||||
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, config.tx_paths, config.rx_paths);
|
||||
type = usrp->open(config.dev_args, ref, config.swap_channels);
|
||||
if (type < 0) {
|
||||
LOG(ALERT) << "Failed to create radio device" << std::endl;
|
||||
goto shutdown;
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -22,7 +22,8 @@
|
||||
#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;
|
||||
@@ -35,13 +36,26 @@ class RadioDevice {
|
||||
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
|
||||
|
||||
/* Radio interface types */
|
||||
enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, DIVERSITY };
|
||||
enum InterfaceType {
|
||||
NORMAL,
|
||||
RESAMP_64M,
|
||||
RESAMP_100M,
|
||||
MULTI_ARFCN,
|
||||
};
|
||||
|
||||
static RadioDevice *make(size_t sps, size_t chans = 1,
|
||||
bool diversity = false, double offset = 0.0);
|
||||
enum ReferenceType {
|
||||
REF_INTERNAL,
|
||||
REF_EXTERNAL,
|
||||
REF_GPS,
|
||||
};
|
||||
|
||||
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||
size_t chans = 1, double offset = 0.0,
|
||||
const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
|
||||
const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
|
||||
|
||||
/** Initialize the USRP */
|
||||
virtual int open(const std::string &args = "", bool extref = false, bool swap_channels = false)=0;
|
||||
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
|
||||
|
||||
virtual ~RadioDevice() { }
|
||||
|
||||
@@ -124,6 +138,18 @@ class RadioDevice {
|
||||
/** return minimum Tx Gain **/
|
||||
virtual double minTxGain(void) = 0;
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0;
|
||||
|
||||
/** return the used RX path */
|
||||
virtual std::string getRxAntenna(size_t chan = 0) = 0;
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0;
|
||||
|
||||
/** return the used RX path */
|
||||
virtual std::string getTxAntenna(size_t chan = 0) = 0;
|
||||
|
||||
/** Return internal status values */
|
||||
virtual double getTxFreq(size_t chan = 0) = 0;
|
||||
virtual double getRxFreq(size_t chan = 0) = 0;
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
/*
|
||||
* Copyright 2008, 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/>.
|
||||
|
||||
*/
|
||||
* 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"
|
||||
@@ -33,12 +30,11 @@ extern "C" {
|
||||
#define CHUNK 625
|
||||
#define NUMCHUNKS 4
|
||||
|
||||
RadioInterface::RadioInterface(RadioDevice *wRadio,
|
||||
size_t sps, size_t chans, size_t diversity,
|
||||
RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
|
||||
size_t rx_sps, size_t chans,
|
||||
int wReceiveOffset, GSM::Time wStartTime)
|
||||
: mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
|
||||
sendCursor(0), recvCursor(0), underrun(false), overrun(false),
|
||||
receiveOffset(wReceiveOffset), mOn(false)
|
||||
: mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
|
||||
underrun(false), overrun(false), receiveOffset(wReceiveOffset), mOn(false)
|
||||
{
|
||||
mClock.set(wStartTime);
|
||||
}
|
||||
@@ -50,7 +46,7 @@ RadioInterface::~RadioInterface(void)
|
||||
|
||||
bool RadioInterface::init(int type)
|
||||
{
|
||||
if ((type != RadioDevice::NORMAL) || (mMIMO > 1) || !mChans) {
|
||||
if ((type != RadioDevice::NORMAL) || !mChans) {
|
||||
LOG(ALERT) << "Invalid configuration";
|
||||
return false;
|
||||
}
|
||||
@@ -65,33 +61,20 @@ bool RadioInterface::init(int type)
|
||||
powerScaling.resize(mChans);
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
sendBuffer[i] = new signalVector(CHUNK * mSPSTx);
|
||||
recvBuffer[i] = new signalVector(NUMCHUNKS * CHUNK * mSPSRx);
|
||||
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
|
||||
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
|
||||
|
||||
convertSendBuffer[i] = new short[sendBuffer[i]->size() * 2];
|
||||
convertRecvBuffer[i] = new short[recvBuffer[i]->size() * 2];
|
||||
convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
|
||||
convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
|
||||
|
||||
powerScaling[i] = 1.0;
|
||||
}
|
||||
|
||||
sendCursor = 0;
|
||||
recvCursor = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RadioInterface::close()
|
||||
{
|
||||
for (size_t i = 0; i < sendBuffer.size(); i++)
|
||||
delete sendBuffer[i];
|
||||
|
||||
for (size_t i = 0; i < recvBuffer.size(); i++)
|
||||
delete recvBuffer[i];
|
||||
|
||||
for (size_t i = 0; i < convertSendBuffer.size(); i++)
|
||||
delete convertSendBuffer[i];
|
||||
|
||||
for (size_t i = 0; i < convertRecvBuffer.size(); i++)
|
||||
delete convertRecvBuffer[i];
|
||||
|
||||
sendBuffer.resize(0);
|
||||
recvBuffer.resize(0);
|
||||
convertSendBuffer.resize(0);
|
||||
@@ -130,35 +113,26 @@ int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
||||
}
|
||||
|
||||
int RadioInterface::radioifyVector(signalVector &wVector,
|
||||
float *retVector,
|
||||
bool zero)
|
||||
size_t chan, bool zero)
|
||||
{
|
||||
if (zero) {
|
||||
memset(retVector, 0, wVector.size() * 2 * sizeof(float));
|
||||
return wVector.size();
|
||||
}
|
||||
|
||||
memcpy(retVector, wVector.begin(), wVector.size() * 2 * sizeof(float));
|
||||
if (zero)
|
||||
sendBuffer[chan]->zero(wVector.size());
|
||||
else
|
||||
sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
|
||||
|
||||
return wVector.size();
|
||||
}
|
||||
|
||||
int RadioInterface::unRadioifyVector(float *floatVector,
|
||||
signalVector& newVector)
|
||||
int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
|
||||
{
|
||||
signalVector::iterator itr = newVector.begin();
|
||||
|
||||
if (newVector.size() > recvCursor) {
|
||||
if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
|
||||
LOG(ALERT) << "Insufficient number of samples in receive buffer";
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < newVector.size(); i++) {
|
||||
*itr++ = Complex<float>(floatVector[2 * i + 0],
|
||||
floatVector[2 * i + 1]);
|
||||
}
|
||||
recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
|
||||
|
||||
return newVector.size();
|
||||
return newVector->size();
|
||||
}
|
||||
|
||||
bool RadioInterface::tuneTx(double freq, size_t chan)
|
||||
@@ -185,8 +159,10 @@ bool RadioInterface::start()
|
||||
if (!mRadio->start())
|
||||
return false;
|
||||
|
||||
recvCursor = 0;
|
||||
sendCursor = 0;
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
sendBuffer[i]->reset();
|
||||
recvBuffer[i]->reset();
|
||||
}
|
||||
|
||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||
readTimestamp = mRadio->initialReadTimestamp();
|
||||
@@ -237,14 +213,10 @@ void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
|
||||
if (!mOn)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
radioifyVector(*bursts[i],
|
||||
(float *) (sendBuffer[i]->begin() + sendCursor), zeros[i]);
|
||||
}
|
||||
for (size_t i = 0; i < mChans; i++)
|
||||
radioifyVector(*bursts[i], i, zeros[i]);
|
||||
|
||||
sendCursor += bursts[0]->size();
|
||||
|
||||
pushBuffer();
|
||||
while (pushBuffer());
|
||||
}
|
||||
|
||||
bool RadioInterface::driveReceiveRadio()
|
||||
@@ -259,10 +231,14 @@ bool RadioInterface::driveReceiveRadio()
|
||||
GSM::Time rcvClock = mClock.get();
|
||||
rcvClock.decTN(receiveOffset);
|
||||
unsigned tN = rcvClock.TN();
|
||||
int recvSz = recvCursor;
|
||||
int readSz = 0;
|
||||
int recvSz = recvBuffer[0]->getAvailSamples();
|
||||
const int symbolsPerSlot = gSlotLen + 8;
|
||||
int burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
|
||||
int burstSize;
|
||||
|
||||
if (mSPSRx == 4)
|
||||
burstSize = 625;
|
||||
else
|
||||
burstSize = symbolsPerSlot + (tN % 4 == 0);
|
||||
|
||||
/*
|
||||
* Pre-allocate head room for the largest correlation size
|
||||
@@ -276,13 +252,8 @@ bool RadioInterface::driveReceiveRadio()
|
||||
*/
|
||||
while (recvSz > burstSize) {
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
burst = new radioVector(rcvClock, burstSize, head, mMIMO);
|
||||
|
||||
for (size_t n = 0; n < mMIMO; n++) {
|
||||
unRadioifyVector((float *)
|
||||
(recvBuffer[mMIMO * i + n]->begin() + readSz),
|
||||
*burst->getVector(n));
|
||||
}
|
||||
burst = new radioVector(rcvClock, burstSize, head);
|
||||
unRadioifyVector(burst->getVector(), i);
|
||||
|
||||
if (mReceiveFIFO[i].size() < 32)
|
||||
mReceiveFIFO[i].write(burst);
|
||||
@@ -292,22 +263,12 @@ bool RadioInterface::driveReceiveRadio()
|
||||
|
||||
mClock.incTN();
|
||||
rcvClock.incTN();
|
||||
readSz += burstSize;
|
||||
recvSz -= burstSize;
|
||||
|
||||
tN = rcvClock.TN();
|
||||
|
||||
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
|
||||
}
|
||||
|
||||
if (readSz > 0) {
|
||||
for (size_t i = 0; i < recvBuffer.size(); i++) {
|
||||
memmove(recvBuffer[i]->begin(),
|
||||
recvBuffer[i]->begin() + readSz,
|
||||
(recvCursor - readSz) * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
recvCursor -= readSz;
|
||||
if (mSPSRx != 4)
|
||||
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -331,74 +292,66 @@ VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
|
||||
|
||||
double RadioInterface::setRxGain(double dB, size_t chan)
|
||||
{
|
||||
if (mRadio)
|
||||
return mRadio->setRxGain(dB, chan);
|
||||
else
|
||||
return -1;
|
||||
return mRadio->setRxGain(dB, chan);
|
||||
}
|
||||
|
||||
double RadioInterface::getRxGain(size_t chan)
|
||||
{
|
||||
if (mRadio)
|
||||
return mRadio->getRxGain(chan);
|
||||
else
|
||||
return -1;
|
||||
return mRadio->getRxGain(chan);
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterface::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
int num_recv;
|
||||
float *output;
|
||||
size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
|
||||
|
||||
if (recvCursor > recvBuffer[0]->size() - CHUNK)
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
num_recv = mRadio->readSamples(convertRecvBuffer,
|
||||
CHUNK,
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
if (num_recv != CHUNK) {
|
||||
LOG(ALERT) << "Receive error " << num_recv;
|
||||
numRecv = mRadio->readSamples(convertRecvBuffer,
|
||||
segmentLen,
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
|
||||
if (numRecv != segmentLen) {
|
||||
LOG(ALERT) << "Receive error " << numRecv;
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
output = (float *) (recvBuffer[i]->begin() + recvCursor);
|
||||
convert_short_float(output, convertRecvBuffer[i], 2 * num_recv);
|
||||
convert_short_float(recvBuffer[i]->getWriteSegment(),
|
||||
convertRecvBuffer[i],
|
||||
segmentLen * 2);
|
||||
}
|
||||
|
||||
underrun |= local_underrun;
|
||||
|
||||
readTimestamp += num_recv;
|
||||
recvCursor += num_recv;
|
||||
readTimestamp += numRecv;
|
||||
}
|
||||
|
||||
/* Send timestamped chunk to the device with arbitrary size */
|
||||
void RadioInterface::pushBuffer()
|
||||
bool RadioInterface::pushBuffer()
|
||||
{
|
||||
int num_sent;
|
||||
size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
|
||||
|
||||
if (sendCursor < CHUNK)
|
||||
return;
|
||||
|
||||
if (sendCursor > sendBuffer[0]->size())
|
||||
LOG(ALERT) << "Send buffer overflow";
|
||||
if (sendBuffer[0]->getAvailSegments() < 1)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
convert_float_short(convertSendBuffer[i],
|
||||
(float *) sendBuffer[i]->begin(),
|
||||
powerScaling[i], 2 * sendCursor);
|
||||
(float *) sendBuffer[i]->getReadSegment(),
|
||||
powerScaling[i],
|
||||
segmentLen * 2);
|
||||
}
|
||||
|
||||
/* Send the all samples in the send buffer */
|
||||
num_sent = mRadio->writeSamples(convertSendBuffer,
|
||||
sendCursor,
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
writeTimestamp += num_sent;
|
||||
sendCursor = 0;
|
||||
/* Send the all samples in the send buffer */
|
||||
numSent = mRadio->writeSamples(convertSendBuffer,
|
||||
segmentLen,
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
writeTimestamp += numSent;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
#include "radioDevice.h"
|
||||
#include "radioVector.h"
|
||||
#include "radioClock.h"
|
||||
#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
|
||||
|
||||
@@ -38,12 +41,9 @@ protected:
|
||||
size_t mSPSTx;
|
||||
size_t mSPSRx;
|
||||
size_t mChans;
|
||||
size_t mMIMO;
|
||||
|
||||
std::vector<signalVector *> sendBuffer;
|
||||
std::vector<signalVector *> recvBuffer;
|
||||
unsigned sendCursor;
|
||||
unsigned recvCursor;
|
||||
std::vector<RadioBuffer *> sendBuffer;
|
||||
std::vector<RadioBuffer *> recvBuffer;
|
||||
|
||||
std::vector<short *> convertRecvBuffer;
|
||||
std::vector<short *> convertSendBuffer;
|
||||
@@ -61,16 +61,14 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
/** format samples to USRP */
|
||||
int radioifyVector(signalVector &wVector,
|
||||
float *floatVector,
|
||||
bool zero);
|
||||
/** format samples to USRP */
|
||||
int radioifyVector(signalVector &wVector, size_t chan, bool zero);
|
||||
|
||||
/** format samples from USRP */
|
||||
int unRadioifyVector(float *floatVector, 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);
|
||||
@@ -86,9 +84,9 @@ public:
|
||||
virtual void close();
|
||||
|
||||
/** constructor */
|
||||
RadioInterface(RadioDevice* wRadio = NULL,
|
||||
size_t sps = 4, size_t chans = 1, size_t diversity = 1,
|
||||
int receiveOffset = 3, GSM::Time wStartTime = GSM::Time(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 */
|
||||
virtual ~RadioInterface();
|
||||
@@ -103,7 +101,7 @@ public:
|
||||
RadioClock* getClock(void) { return &mClock;};
|
||||
|
||||
/** set transmit frequency */
|
||||
bool tuneTx(double freq, size_t chan = 0);
|
||||
virtual bool tuneTx(double freq, size_t chan = 0);
|
||||
|
||||
/** set receive frequency */
|
||||
virtual bool tuneRx(double freq, size_t chan = 0);
|
||||
@@ -151,45 +149,45 @@ void *AlignRadioServiceLoopAdapter(RadioInterface*);
|
||||
#endif
|
||||
|
||||
class RadioInterfaceResamp : public RadioInterface {
|
||||
|
||||
private:
|
||||
signalVector *innerSendBuffer;
|
||||
signalVector *outerSendBuffer;
|
||||
signalVector *innerRecvBuffer;
|
||||
signalVector *outerRecvBuffer;
|
||||
|
||||
void pushBuffer();
|
||||
bool pushBuffer();
|
||||
void pullBuffer();
|
||||
|
||||
public:
|
||||
|
||||
RadioInterfaceResamp(RadioDevice* wRadio, size_t wSPS = 4, size_t chans = 1);
|
||||
|
||||
RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
|
||||
~RadioInterfaceResamp();
|
||||
|
||||
bool init(int type);
|
||||
void close();
|
||||
};
|
||||
|
||||
class RadioInterfaceDiversity : public RadioInterface {
|
||||
public:
|
||||
RadioInterfaceDiversity(RadioDevice* wRadio,
|
||||
size_t sps = 4, size_t chans = 2);
|
||||
class RadioInterfaceMulti : public RadioInterface {
|
||||
private:
|
||||
bool pushBuffer();
|
||||
void pullBuffer();
|
||||
|
||||
~RadioInterfaceDiversity();
|
||||
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);
|
||||
|
||||
private:
|
||||
std::vector<Resampler *> dnsamplers;
|
||||
std::vector<float> phases;
|
||||
signalVector *outerRecvBuffer;
|
||||
|
||||
bool mDiversity;
|
||||
double mFreqSpacing;
|
||||
|
||||
bool setupDiversityChannels();
|
||||
void pullBuffer();
|
||||
double setRxGain(double dB, size_t chan);
|
||||
};
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* SSE Convolution
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <radioInterface.h>
|
||||
#include <Logger.h>
|
||||
|
||||
#include "Resampler.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
/* Resampling parameters for 64 MHz clocking */
|
||||
#define RESAMP_64M_INRATE 20
|
||||
#define RESAMP_64M_OUTRATE 80
|
||||
|
||||
/* Downlink block size */
|
||||
#define CHUNK 625
|
||||
|
||||
/* Universal resampling parameters */
|
||||
#define NUMCHUNKS 48
|
||||
|
||||
/*
|
||||
* 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 size_t resamp_inrate = 0;
|
||||
static size_t resamp_inchunk = 0;
|
||||
static size_t resamp_outrate = 0;
|
||||
static size_t resamp_outchunk = 0;
|
||||
|
||||
RadioInterfaceDiversity::RadioInterfaceDiversity(RadioDevice *wRadio,
|
||||
size_t sps, size_t chans)
|
||||
: RadioInterface(wRadio, sps, chans, 2), outerRecvBuffer(NULL),
|
||||
mDiversity(false), mFreqSpacing(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
RadioInterfaceDiversity::~RadioInterfaceDiversity()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void RadioInterfaceDiversity::close()
|
||||
{
|
||||
delete outerRecvBuffer;
|
||||
|
||||
outerRecvBuffer = NULL;
|
||||
|
||||
for (size_t i = 0; i < dnsamplers.size(); i++) {
|
||||
delete dnsamplers[i];
|
||||
dnsamplers[i] = NULL;
|
||||
}
|
||||
|
||||
if (recvBuffer.size())
|
||||
recvBuffer[0] = NULL;
|
||||
|
||||
RadioInterface::close();
|
||||
}
|
||||
|
||||
bool RadioInterfaceDiversity::setupDiversityChannels()
|
||||
{
|
||||
size_t inner_rx_len;
|
||||
|
||||
/* Inner and outer rates */
|
||||
resamp_inrate = RESAMP_64M_INRATE;
|
||||
resamp_outrate = RESAMP_64M_OUTRATE;
|
||||
resamp_inchunk = resamp_inrate * 4;
|
||||
resamp_outchunk = resamp_outrate * 4;
|
||||
|
||||
/* Buffer lengths */
|
||||
inner_rx_len = NUMCHUNKS * resamp_inchunk;
|
||||
|
||||
/* Inside buffer must hold at least 2 bursts */
|
||||
if (inner_rx_len < 157 * mSPSRx * 2) {
|
||||
LOG(ALERT) << "Invalid inner buffer size " << inner_rx_len;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* One Receive buffer and downsampler per diversity channel */
|
||||
for (size_t i = 0; i < mMIMO * mChans; i++) {
|
||||
dnsamplers[i] = new Resampler(resamp_inrate, resamp_outrate);
|
||||
if (!dnsamplers[i]->init()) {
|
||||
LOG(ALERT) << "Rx resampler failed to initialize";
|
||||
return false;
|
||||
}
|
||||
|
||||
recvBuffer[i] = new signalVector(inner_rx_len);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Initialize I/O specific objects */
|
||||
bool RadioInterfaceDiversity::init(int type)
|
||||
{
|
||||
int tx_len, outer_rx_len;
|
||||
|
||||
if ((mMIMO != 2) || (mChans != 2)) {
|
||||
LOG(ALERT) << "Unsupported channel configuration " << mChans;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Resize for channel combination */
|
||||
sendBuffer.resize(mChans);
|
||||
recvBuffer.resize(mChans * mMIMO);
|
||||
convertSendBuffer.resize(mChans);
|
||||
convertRecvBuffer.resize(mChans);
|
||||
mReceiveFIFO.resize(mChans);
|
||||
dnsamplers.resize(mChans * mMIMO);
|
||||
phases.resize(mChans);
|
||||
|
||||
if (!setupDiversityChannels())
|
||||
return false;
|
||||
|
||||
tx_len = CHUNK * mSPSTx;
|
||||
outer_rx_len = resamp_outchunk;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
/* Full rate float and integer outer receive buffers */
|
||||
convertRecvBuffer[i] = new short[outer_rx_len * 2];
|
||||
|
||||
/* Send buffers (not-resampled) */
|
||||
sendBuffer[i] = new signalVector(tx_len);
|
||||
convertSendBuffer[i] = new short[tx_len * 2];
|
||||
}
|
||||
|
||||
outerRecvBuffer = new signalVector(outer_rx_len, dnsamplers[0]->len());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioInterfaceDiversity::tuneRx(double freq, size_t chan)
|
||||
{
|
||||
double f0, f1;
|
||||
|
||||
if (chan > 1)
|
||||
return false;
|
||||
|
||||
if (!mRadio->setRxFreq(freq, chan))
|
||||
return false;
|
||||
|
||||
f0 = mRadio->getRxFreq(0);
|
||||
f1 = mRadio->getRxFreq(1);
|
||||
|
||||
mFreqSpacing = f1 - f0;
|
||||
|
||||
if (abs(mFreqSpacing) <= 600e3)
|
||||
mDiversity = true;
|
||||
else
|
||||
mDiversity = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterfaceDiversity::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
int rc, num, path0, path1;
|
||||
signalVector *shift, *base;
|
||||
float *in, *out, rate = -mFreqSpacing * 2.0 * M_PI / 1.08333333e6;
|
||||
|
||||
if (recvCursor > recvBuffer[0]->size() - resamp_inchunk)
|
||||
return;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
num = mRadio->readSamples(convertRecvBuffer,
|
||||
resamp_outchunk,
|
||||
&overrun,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
if ((size_t) num != resamp_outchunk) {
|
||||
LOG(ALERT) << "Receive error " << num;
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
convert_short_float((float *) outerRecvBuffer->begin(),
|
||||
convertRecvBuffer[i], 2 * resamp_outchunk);
|
||||
|
||||
if (!i) {
|
||||
path0 = 0;
|
||||
path1 = 2;
|
||||
} else {
|
||||
path0 = 3;
|
||||
path1 = 1;
|
||||
}
|
||||
|
||||
/* Diversity path 1 */
|
||||
base = outerRecvBuffer;
|
||||
in = (float *) base->begin();
|
||||
out = (float *) (recvBuffer[path0]->begin() + recvCursor);
|
||||
|
||||
rc = dnsamplers[2 * i + 0]->rotate(in, resamp_outchunk,
|
||||
out, resamp_inchunk);
|
||||
if (rc < 0) {
|
||||
LOG(ALERT) << "Sample rate downsampling error";
|
||||
}
|
||||
|
||||
/* Enable path 2 if Nyquist bandwidth is sufficient */
|
||||
if (!mDiversity)
|
||||
continue;
|
||||
|
||||
/* Diversity path 2 */
|
||||
shift = new signalVector(base->size(), base->getStart());
|
||||
in = (float *) shift->begin();
|
||||
out = (float *) (recvBuffer[path1]->begin() + recvCursor);
|
||||
|
||||
rate = i ? -rate : rate;
|
||||
if (!frequencyShift(shift, base, rate, phases[i], &phases[i])) {
|
||||
LOG(ALERT) << "Frequency shift failed";
|
||||
}
|
||||
|
||||
rc = dnsamplers[2 * i + 1]->rotate(in, resamp_outchunk,
|
||||
out, resamp_inchunk);
|
||||
if (rc < 0) {
|
||||
LOG(ALERT) << "Sample rate downsampling error";
|
||||
}
|
||||
|
||||
delete shift;
|
||||
}
|
||||
|
||||
underrun |= local_underrun;
|
||||
readTimestamp += (TIMESTAMP) resamp_outchunk;
|
||||
recvCursor += resamp_inchunk;
|
||||
}
|
||||
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();
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
/*
|
||||
* Radio device interface with sample rate conversion
|
||||
* Written by Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
|
||||
* 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
|
||||
@@ -56,10 +58,9 @@ static size_t resamp_outrate = 0;
|
||||
static size_t resamp_outchunk = 0;
|
||||
|
||||
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
|
||||
size_t sps, size_t chans)
|
||||
: RadioInterface(wRadio, sps, chans),
|
||||
innerSendBuffer(NULL), outerSendBuffer(NULL),
|
||||
innerRecvBuffer(NULL), outerRecvBuffer(NULL)
|
||||
size_t tx_sps, size_t rx_sps)
|
||||
: RadioInterface(wRadio, tx_sps, rx_sps, 1),
|
||||
outerSendBuffer(NULL), outerRecvBuffer(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -70,17 +71,13 @@ RadioInterfaceResamp::~RadioInterfaceResamp()
|
||||
|
||||
void RadioInterfaceResamp::close()
|
||||
{
|
||||
delete innerSendBuffer;
|
||||
delete outerSendBuffer;
|
||||
delete innerRecvBuffer;
|
||||
delete outerRecvBuffer;
|
||||
|
||||
delete upsampler;
|
||||
delete dnsampler;
|
||||
|
||||
innerSendBuffer = NULL;
|
||||
outerSendBuffer = NULL;
|
||||
innerRecvBuffer = NULL;
|
||||
outerRecvBuffer = NULL;
|
||||
|
||||
upsampler = NULL;
|
||||
@@ -99,11 +96,6 @@ bool RadioInterfaceResamp::init(int type)
|
||||
{
|
||||
float cutoff = 1.0f;
|
||||
|
||||
if (mChans != 1) {
|
||||
LOG(ALERT) << "Unsupported channel configuration " << mChans;
|
||||
return false;
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
sendBuffer.resize(1);
|
||||
@@ -128,13 +120,8 @@ bool RadioInterfaceResamp::init(int type)
|
||||
return false;
|
||||
}
|
||||
|
||||
resamp_inchunk = resamp_inrate * 4;
|
||||
resamp_outchunk = resamp_outrate * 4;
|
||||
|
||||
if (resamp_inchunk * NUMCHUNKS < 157 * mSPSTx * 2) {
|
||||
LOG(ALERT) << "Invalid inner chunk size " << resamp_inchunk;
|
||||
return false;
|
||||
}
|
||||
resamp_inchunk = resamp_inrate * 4 * mSPSRx;
|
||||
resamp_outchunk = resamp_outrate * 4 * mSPSRx;
|
||||
|
||||
if (mSPSTx == 4)
|
||||
cutoff = RESAMP_TX4_FILTER;
|
||||
@@ -157,21 +144,18 @@ bool RadioInterfaceResamp::init(int type)
|
||||
* and requires headroom equivalent to the filter length. Low
|
||||
* rate buffers are allocated in the main radio interface code.
|
||||
*/
|
||||
innerSendBuffer =
|
||||
new signalVector(NUMCHUNKS * resamp_inchunk, upsampler->len());
|
||||
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());
|
||||
innerRecvBuffer =
|
||||
new signalVector(NUMCHUNKS * resamp_inchunk / mSPSTx);
|
||||
|
||||
convertSendBuffer[0] = new short[outerSendBuffer->size() * 2];
|
||||
convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2];
|
||||
|
||||
sendBuffer[0] = innerSendBuffer;
|
||||
recvBuffer[0] = innerRecvBuffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -181,7 +165,7 @@ void RadioInterfaceResamp::pullBuffer()
|
||||
bool local_underrun;
|
||||
int rc, num_recv;
|
||||
|
||||
if (recvCursor > innerRecvBuffer->size() - resamp_inchunk)
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
@@ -204,57 +188,47 @@ void RadioInterfaceResamp::pullBuffer()
|
||||
/* Write to the end of the inner receive buffer */
|
||||
rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
|
||||
resamp_outchunk,
|
||||
(float *) (innerRecvBuffer->begin() + recvCursor),
|
||||
recvBuffer[0]->getWriteSegment(),
|
||||
resamp_inchunk);
|
||||
if (rc < 0) {
|
||||
LOG(ALERT) << "Sample rate upsampling error";
|
||||
}
|
||||
|
||||
recvCursor += resamp_inchunk;
|
||||
/* Set history for the next chunk */
|
||||
outerRecvBuffer->updateHistory();
|
||||
}
|
||||
|
||||
/* Send a timestamped chunk to the device */
|
||||
void RadioInterfaceResamp::pushBuffer()
|
||||
bool RadioInterfaceResamp::pushBuffer()
|
||||
{
|
||||
int rc, chunks, num_sent;
|
||||
int inner_len, outer_len;
|
||||
int rc;
|
||||
size_t numSent;
|
||||
|
||||
if (sendCursor < resamp_inchunk)
|
||||
return;
|
||||
|
||||
if (sendCursor > innerSendBuffer->size())
|
||||
LOG(ALERT) << "Send buffer overflow";
|
||||
|
||||
chunks = sendCursor / resamp_inchunk;
|
||||
|
||||
inner_len = chunks * resamp_inchunk;
|
||||
outer_len = chunks * resamp_outchunk;
|
||||
if (sendBuffer[0]->getAvailSegments() <= 0)
|
||||
return false;
|
||||
|
||||
/* Always send from the beginning of the buffer */
|
||||
rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
|
||||
(float *) outerSendBuffer->begin(), outer_len);
|
||||
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 * outer_len);
|
||||
powerScaling[0], 2 * resamp_outchunk);
|
||||
|
||||
num_sent = mRadio->writeSamples(convertSendBuffer,
|
||||
outer_len,
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
if (num_sent != outer_len) {
|
||||
LOG(ALERT) << "Transmit error " << num_sent;
|
||||
numSent = mRadio->writeSamples(convertSendBuffer,
|
||||
resamp_outchunk,
|
||||
&underrun,
|
||||
writeTimestamp);
|
||||
if (numSent != resamp_outchunk) {
|
||||
LOG(ALERT) << "Transmit error " << numSent;
|
||||
}
|
||||
|
||||
/* Shift remaining samples to beginning of buffer */
|
||||
memmove(innerSendBuffer->begin(),
|
||||
innerSendBuffer->begin() + inner_len,
|
||||
(sendCursor - inner_len) * 2 * sizeof(float));
|
||||
writeTimestamp += resamp_outchunk;
|
||||
|
||||
writeTimestamp += outer_len;
|
||||
sendCursor -= inner_len;
|
||||
assert(sendCursor >= 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,15 +20,21 @@
|
||||
#include "BitVector.h"
|
||||
#include "signalVector.h"
|
||||
|
||||
/** Convolution type indicator */
|
||||
enum ConvType {
|
||||
START_ONLY,
|
||||
NO_DELAY,
|
||||
CUSTOM,
|
||||
UNDEFINED,
|
||||
/* Burst lengths */
|
||||
#define NORMAL_BURST_NBITS 148
|
||||
#define EDGE_BURST_NBITS 444
|
||||
#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
|
||||
|
||||
/** 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
|
||||
};
|
||||
|
||||
enum signalError {
|
||||
enum SignalError {
|
||||
SIGERR_NONE,
|
||||
SIGERR_BOUNDS,
|
||||
SIGERR_CLIP,
|
||||
@@ -36,112 +42,47 @@ enum signalError {
|
||||
SIGERR_INTERNAL,
|
||||
};
|
||||
|
||||
/** Convert a linear number to a dB value */
|
||||
float dB(float x);
|
||||
|
||||
/** Convert a dB value into a linear value */
|
||||
float dBinv(float x);
|
||||
|
||||
/** Compute the energy of a vector */
|
||||
float vectorNorm2(const signalVector &x);
|
||||
|
||||
/** Compute the average power of a vector */
|
||||
float vectorPower(const signalVector &x);
|
||||
/*
|
||||
* 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
|
||||
|
||||
/** Setup the signal processing library */
|
||||
bool sigProcLibSetup(int sps);
|
||||
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 or NULL on error.
|
||||
*/
|
||||
signalVector *convolve(const signalVector *a, const signalVector *b,
|
||||
signalVector *c, ConvType spanType,
|
||||
size_t start = 0, size_t len = 0,
|
||||
size_t step = 1, int offset = 0);
|
||||
|
||||
/**
|
||||
Frequency shift a vector.
|
||||
@param y The frequency shifted vector.
|
||||
@param x The vector to-be-shifted.
|
||||
@param freq The digital frequency shift
|
||||
@param startPhase The starting phase of the oscillator
|
||||
@param finalPhase The final phase of the oscillator
|
||||
@return The frequency shifted vector.
|
||||
*/
|
||||
signalVector* frequencyShift(signalVector *y,
|
||||
signalVector *x,
|
||||
float freq = 0.0,
|
||||
float startPhase = 0.0,
|
||||
float *finalPhase=NULL);
|
||||
|
||||
/**
|
||||
Correlate two vectors.
|
||||
@param a,b The vectors to be correlated.
|
||||
@param c, A preallocated vector to hold the correlation result.
|
||||
@param spanType The type/span of the correlation.
|
||||
@return The correlation result.
|
||||
*/
|
||||
signalVector* correlate(signalVector *a,
|
||||
signalVector *b,
|
||||
signalVector *c,
|
||||
ConvType spanType,
|
||||
bool bReversedConjugated = false,
|
||||
unsigned startIx = 0,
|
||||
unsigned len = 0);
|
||||
|
||||
/** Operate soft slicer on 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,
|
||||
int guardPeriodLength,
|
||||
int sps, bool emptyPulse = false);
|
||||
int guardPeriodLength,
|
||||
int sps, bool emptyPulse = false);
|
||||
|
||||
/** Sinc function */
|
||||
float sinc(float x);
|
||||
/** 8-PSK modulate a burst of bits */
|
||||
signalVector *modulateEdgeBurst(const BitVector &bits,
|
||||
int sps, bool emptyPulse = false);
|
||||
|
||||
/** Delay a vector */
|
||||
signalVector *delayVector(signalVector *in, signalVector *out, float delay);
|
||||
/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
|
||||
signalVector *generateEdgeBurst(int tsc);
|
||||
|
||||
/** Add two vectors in-place */
|
||||
bool addVector(signalVector &x,
|
||||
signalVector &y);
|
||||
/** Generate an empty burst - 4 or 1 SPS */
|
||||
signalVector *generateEmptyBurst(int sps, int tn);
|
||||
|
||||
/** Multiply two vectors in-place*/
|
||||
bool multVector(signalVector &x,
|
||||
signalVector &y);
|
||||
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
|
||||
signalVector *genRandNormalBurst(int tsc, int sps, int tn);
|
||||
|
||||
/** Generate a vector of gaussian noise */
|
||||
signalVector *gaussianNoise(int length,
|
||||
float variance = 1.0,
|
||||
complex mean = complex(0.0));
|
||||
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
|
||||
signalVector *genRandAccessBurst(int delay, int sps, int tn);
|
||||
|
||||
/**
|
||||
Given a non-integer index, interpolate a sample.
|
||||
@param inSig The signal from which to interpolate.
|
||||
@param ix The index.
|
||||
@return The interpolated signal value.
|
||||
*/
|
||||
complex interpolatePoint(const signalVector &inSig,
|
||||
float ix);
|
||||
|
||||
/**
|
||||
Given a correlator output, locate the correlation peak.
|
||||
@param rxBurst The correlator result.
|
||||
@param peakIndex Pointer to value to receive interpolated peak index.
|
||||
@param avgPower Power to value to receive mean power.
|
||||
@return Peak value.
|
||||
*/
|
||||
complex peakDetect(const signalVector &rxBurst,
|
||||
float *peakIndex,
|
||||
float *avgPwr);
|
||||
/** Generate a dummy GSM burst - 4 or 1 SPS */
|
||||
signalVector *generateDummyBurst(int sps, int tn);
|
||||
|
||||
/**
|
||||
Apply a scalar to a vector.
|
||||
@@ -149,125 +90,40 @@ complex peakDetect(const signalVector &rxBurst,
|
||||
@param scale The scalar.
|
||||
*/
|
||||
void scaleVector(signalVector &x,
|
||||
complex scale);
|
||||
complex scale);
|
||||
|
||||
/**
|
||||
Generate a modulated GSM midamble, stored within the library.
|
||||
@param gsmPulse The GSM pulse used for modulation.
|
||||
@param sps The number of samples per GSM symbol.
|
||||
@param TSC The training sequence [0..7]
|
||||
@return Success.
|
||||
*/
|
||||
bool generateMidamble(int sps, int tsc);
|
||||
/**
|
||||
Generate a modulated RACH sequence, stored within the library.
|
||||
@param gsmPulse The GSM pulse used for modulation.
|
||||
@param sps The number of samples per GSM symbol.
|
||||
@return Success.
|
||||
*/
|
||||
bool generateRACHSequence(int sps);
|
||||
|
||||
/**
|
||||
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 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 positive if threshold value is reached, negative on error, zero otherwise
|
||||
*/
|
||||
int detectRACHBurst(signalVector &rxBurst,
|
||||
float detectThreshold,
|
||||
int sps,
|
||||
complex &litude,
|
||||
float &TOA);
|
||||
|
||||
/**
|
||||
Normal burst correlator, detector, channel estimator.
|
||||
@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.
|
||||
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.
|
||||
@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 positive if threshold value is reached, negative on error, zero otherwise
|
||||
@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 analyzeTrafficBurst(signalVector &rxBurst,
|
||||
unsigned TSC,
|
||||
float detectThreshold,
|
||||
int sps,
|
||||
complex &litude,
|
||||
float &TOA,
|
||||
unsigned maxTOA,
|
||||
bool requestChannel = false,
|
||||
signalVector** channelResponse = NULL,
|
||||
float *channelResponseOffset = NULL);
|
||||
int detectAnyBurst(const signalVector &burst,
|
||||
unsigned tsc,
|
||||
float threshold,
|
||||
int sps,
|
||||
CorrType type,
|
||||
complex &,
|
||||
float &toa,
|
||||
unsigned max_toa);
|
||||
|
||||
/**
|
||||
Decimate a vector.
|
||||
@param wVector The vector of interest.
|
||||
@param factor Decimation factor.
|
||||
@return The decimated signal vector.
|
||||
*/
|
||||
signalVector *decimateVector(signalVector &wVector, size_t factor);
|
||||
|
||||
/**
|
||||
Demodulates a received burst using a soft-slicer.
|
||||
@param rxBurst The burst to be demodulated.
|
||||
@param gsmPulse The GSM pulse.
|
||||
@param sps The number of samples per GSM symbol.
|
||||
@param channel The amplitude estimate of the received burst.
|
||||
@param TOA The time-of-arrival of the received burst.
|
||||
@return The demodulated bit sequence.
|
||||
*/
|
||||
SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
|
||||
complex channel, float TOA);
|
||||
|
||||
/**
|
||||
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 sps 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 sps,
|
||||
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 */
|
||||
|
||||
@@ -45,11 +45,25 @@ void signalVector::operator=(const signalVector& vector)
|
||||
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;
|
||||
|
||||
@@ -30,8 +30,12 @@ public:
|
||||
/** Override base assignment operator to include start offsets */
|
||||
void operator=(const signalVector& vector);
|
||||
|
||||
/** Return an alias to a segment of this signalVector. */
|
||||
signalVector segment(size_t start, size_t span);
|
||||
|
||||
/** Return head room */
|
||||
size_t getStart() const;
|
||||
size_t updateHistory();
|
||||
|
||||
Symmetry getSymmetry() const;
|
||||
void setSymmetry(Symmetry symmetry);
|
||||
|
||||
@@ -1,10 +1,32 @@
|
||||
if !ARCH_ARM
|
||||
AM_CFLAGS = -Wall -std=gnu99 -march=native -I../common
|
||||
AM_CFLAGS = -Wall -std=gnu99 -I${srcdir}/../common
|
||||
|
||||
noinst_LTLIBRARIES = libarch.la
|
||||
noinst_LTLIBRARIES += libarch_sse_3.la
|
||||
noinst_LTLIBRARIES += libarch_sse_4_1.la
|
||||
|
||||
libarch_la_LIBADD =
|
||||
|
||||
# SSE 3 specific code
|
||||
if HAVE_SSE3
|
||||
libarch_sse_3_la_SOURCES = \
|
||||
convert_sse_3.c \
|
||||
convolve_sse_3.c
|
||||
libarch_sse_3_la_CFLAGS = $(AM_CFLAGS) -msse3
|
||||
libarch_la_LIBADD += libarch_sse_3.la
|
||||
endif
|
||||
|
||||
# SSE 4.1 specific code
|
||||
if HAVE_SSE4_1
|
||||
libarch_sse_4_1_la_SOURCES = \
|
||||
convert_sse_4_1.c
|
||||
libarch_sse_4_1_la_CFLAGS = $(AM_CFLAGS) -msse4.1
|
||||
libarch_la_LIBADD += libarch_sse_4_1.la
|
||||
endif
|
||||
|
||||
libarch_la_SOURCES = \
|
||||
../common/convolve_base.c \
|
||||
../common/convert_base.c \
|
||||
convert.c \
|
||||
convolve.c
|
||||
endif
|
||||
|
||||
@@ -20,182 +20,64 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "convert.h"
|
||||
#include "convert_sse_3.h"
|
||||
#include "convert_sse_4_1.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
/* Architecture dependant function pointers */
|
||||
struct convert_cpu_context {
|
||||
void (*convert_si16_ps_16n) (float *, const short *, int);
|
||||
void (*convert_si16_ps) (float *, const short *, int);
|
||||
void (*convert_scale_ps_si16_16n)(short *, const float *, float, int);
|
||||
void (*convert_scale_ps_si16_8n)(short *, const float *, float, int);
|
||||
void (*convert_scale_ps_si16)(short *, const float *, float, int);
|
||||
};
|
||||
|
||||
static struct convert_cpu_context c;
|
||||
|
||||
void convert_init(void)
|
||||
{
|
||||
c.convert_scale_ps_si16_16n = base_convert_float_short;
|
||||
c.convert_scale_ps_si16_8n = base_convert_float_short;
|
||||
c.convert_scale_ps_si16 = base_convert_float_short;
|
||||
c.convert_si16_ps_16n = base_convert_short_float;
|
||||
c.convert_si16_ps = base_convert_short_float;
|
||||
|
||||
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||
#ifdef HAVE_SSE4_1
|
||||
#include <smmintrin.h>
|
||||
|
||||
/* 16*N 16-bit signed integer converted to single precision floats */
|
||||
static void _sse_convert_si16_ps_16n(float *restrict out,
|
||||
const short *restrict in,
|
||||
int len)
|
||||
{
|
||||
__m128i m0, m1, m2, m3, m4, m5;
|
||||
__m128 m6, m7, m8, m9;
|
||||
|
||||
for (int i = 0; i < len / 16; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_si128((__m128i *) &in[16 * i + 0]);
|
||||
m1 = _mm_loadu_si128((__m128i *) &in[16 * i + 8]);
|
||||
|
||||
/* Unpack */
|
||||
m2 = _mm_cvtepi16_epi32(m0);
|
||||
m4 = _mm_cvtepi16_epi32(m1);
|
||||
m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
m3 = _mm_cvtepi16_epi32(m0);
|
||||
m5 = _mm_cvtepi16_epi32(m1);
|
||||
|
||||
/* Convert */
|
||||
m6 = _mm_cvtepi32_ps(m2);
|
||||
m7 = _mm_cvtepi32_ps(m3);
|
||||
m8 = _mm_cvtepi32_ps(m4);
|
||||
m9 = _mm_cvtepi32_ps(m5);
|
||||
|
||||
/* Store */
|
||||
_mm_storeu_ps(&out[16 * i + 0], m6);
|
||||
_mm_storeu_ps(&out[16 * i + 4], m7);
|
||||
_mm_storeu_ps(&out[16 * i + 8], m8);
|
||||
_mm_storeu_ps(&out[16 * i + 12], m9);
|
||||
if (__builtin_cpu_supports("sse4.1")) {
|
||||
c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n;
|
||||
c.convert_si16_ps = &_sse_convert_si16_ps;
|
||||
}
|
||||
}
|
||||
|
||||
/* 16*N 16-bit signed integer conversion with remainder */
|
||||
static void _sse_convert_si16_ps(float *restrict out,
|
||||
const short *restrict in,
|
||||
int len)
|
||||
{
|
||||
int start = len / 16 * 16;
|
||||
|
||||
_sse_convert_si16_ps_16n(out, in, len);
|
||||
|
||||
for (int i = 0; i < len % 16; i++)
|
||||
out[start + i] = in[start + i];
|
||||
}
|
||||
#endif /* HAVE_SSE4_1 */
|
||||
|
||||
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
static void _sse_convert_scale_ps_si16_8n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len)
|
||||
{
|
||||
__m128 m0, m1, m2;
|
||||
__m128i m4, m5;
|
||||
|
||||
for (int i = 0; i < len / 8; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_ps(&in[8 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&in[8 * i + 4]);
|
||||
m2 = _mm_load1_ps(&scale);
|
||||
|
||||
/* Scale */
|
||||
m0 = _mm_mul_ps(m0, m2);
|
||||
m1 = _mm_mul_ps(m1, m2);
|
||||
|
||||
/* Convert */
|
||||
m4 = _mm_cvtps_epi32(m0);
|
||||
m5 = _mm_cvtps_epi32(m1);
|
||||
|
||||
/* Pack and store */
|
||||
m5 = _mm_packs_epi32(m4, m5);
|
||||
_mm_storeu_si128((__m128i *) &out[8 * i], m5);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8*N single precision floats scaled and converted with remainder */
|
||||
static void _sse_convert_scale_ps_si16(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len)
|
||||
{
|
||||
int start = len / 8 * 8;
|
||||
|
||||
_sse_convert_scale_ps_si16_8n(out, in, scale, len);
|
||||
|
||||
for (int i = 0; i < len % 8; i++)
|
||||
out[start + i] = in[start + i] * scale;
|
||||
}
|
||||
|
||||
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
static void _sse_convert_scale_ps_si16_16n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4;
|
||||
__m128i m5, m6, m7, m8;
|
||||
|
||||
for (int i = 0; i < len / 16; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_ps(&in[16 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&in[16 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&in[16 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&in[16 * i + 12]);
|
||||
m4 = _mm_load1_ps(&scale);
|
||||
|
||||
/* Scale */
|
||||
m0 = _mm_mul_ps(m0, m4);
|
||||
m1 = _mm_mul_ps(m1, m4);
|
||||
m2 = _mm_mul_ps(m2, m4);
|
||||
m3 = _mm_mul_ps(m3, m4);
|
||||
|
||||
/* Convert */
|
||||
m5 = _mm_cvtps_epi32(m0);
|
||||
m6 = _mm_cvtps_epi32(m1);
|
||||
m7 = _mm_cvtps_epi32(m2);
|
||||
m8 = _mm_cvtps_epi32(m3);
|
||||
|
||||
/* Pack and store */
|
||||
m5 = _mm_packs_epi32(m5, m6);
|
||||
m7 = _mm_packs_epi32(m7, m8);
|
||||
_mm_storeu_si128((__m128i *) &out[16 * i + 0], m5);
|
||||
_mm_storeu_si128((__m128i *) &out[16 * i + 8], m7);
|
||||
}
|
||||
}
|
||||
#else /* HAVE_SSE3 */
|
||||
static void convert_scale_ps_si16(short *out, const float *in,
|
||||
float scale, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
out[i] = in[i] * scale;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SSE4_1
|
||||
static void convert_si16_ps(float *out, const short *in, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
out[i] = in[i];
|
||||
}
|
||||
#ifdef HAVE_SSE3
|
||||
if (__builtin_cpu_supports("sse3")) {
|
||||
c.convert_scale_ps_si16_16n = _sse_convert_scale_ps_si16_16n;
|
||||
c.convert_scale_ps_si16_8n = _sse_convert_scale_ps_si16_8n;
|
||||
c.convert_scale_ps_si16 = _sse_convert_scale_ps_si16;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void convert_float_short(short *out, const float *in, float scale, int len)
|
||||
{
|
||||
#ifdef HAVE_SSE3
|
||||
if (!(len % 16))
|
||||
_sse_convert_scale_ps_si16_16n(out, in, scale, len);
|
||||
c.convert_scale_ps_si16_16n(out, in, scale, len);
|
||||
else if (!(len % 8))
|
||||
_sse_convert_scale_ps_si16_8n(out, in, scale, len);
|
||||
c.convert_scale_ps_si16_8n(out, in, scale, len);
|
||||
else
|
||||
_sse_convert_scale_ps_si16(out, in, scale, len);
|
||||
#else
|
||||
convert_scale_ps_si16(out, in, scale, len);
|
||||
#endif
|
||||
c.convert_scale_ps_si16(out, in, scale, len);
|
||||
}
|
||||
|
||||
void convert_short_float(float *out, const short *in, int len)
|
||||
{
|
||||
#ifdef HAVE_SSE4_1
|
||||
if (!(len % 16))
|
||||
_sse_convert_si16_ps_16n(out, in, len);
|
||||
c.convert_si16_ps_16n(out, in, len);
|
||||
else
|
||||
_sse_convert_si16_ps(out, in, len);
|
||||
#else
|
||||
convert_si16_ps(out, in, len);
|
||||
#endif
|
||||
c.convert_si16_ps(out, in, len);
|
||||
}
|
||||
|
||||
107
Transceiver52M/x86/convert_sse_3.c
Normal file
107
Transceiver52M/x86/convert_sse_3.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "convert_sse_3.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
void _sse_convert_scale_ps_si16_8n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len)
|
||||
{
|
||||
__m128 m0, m1, m2;
|
||||
__m128i m4, m5;
|
||||
|
||||
for (int i = 0; i < len / 8; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_ps(&in[8 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&in[8 * i + 4]);
|
||||
m2 = _mm_load1_ps(&scale);
|
||||
|
||||
/* Scale */
|
||||
m0 = _mm_mul_ps(m0, m2);
|
||||
m1 = _mm_mul_ps(m1, m2);
|
||||
|
||||
/* Convert */
|
||||
m4 = _mm_cvtps_epi32(m0);
|
||||
m5 = _mm_cvtps_epi32(m1);
|
||||
|
||||
/* Pack and store */
|
||||
m5 = _mm_packs_epi32(m4, m5);
|
||||
_mm_storeu_si128((__m128i *) & out[8 * i], m5);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8*N single precision floats scaled and converted with remainder */
|
||||
void _sse_convert_scale_ps_si16(short *restrict out,
|
||||
const float *restrict in, float scale, int len)
|
||||
{
|
||||
int start = len / 8 * 8;
|
||||
|
||||
_sse_convert_scale_ps_si16_8n(out, in, scale, len);
|
||||
|
||||
for (int i = 0; i < len % 8; i++)
|
||||
out[start + i] = in[start + i] * scale;
|
||||
}
|
||||
|
||||
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
void _sse_convert_scale_ps_si16_16n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4;
|
||||
__m128i m5, m6, m7, m8;
|
||||
|
||||
for (int i = 0; i < len / 16; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_ps(&in[16 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&in[16 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&in[16 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&in[16 * i + 12]);
|
||||
m4 = _mm_load1_ps(&scale);
|
||||
|
||||
/* Scale */
|
||||
m0 = _mm_mul_ps(m0, m4);
|
||||
m1 = _mm_mul_ps(m1, m4);
|
||||
m2 = _mm_mul_ps(m2, m4);
|
||||
m3 = _mm_mul_ps(m3, m4);
|
||||
|
||||
/* Convert */
|
||||
m5 = _mm_cvtps_epi32(m0);
|
||||
m6 = _mm_cvtps_epi32(m1);
|
||||
m7 = _mm_cvtps_epi32(m2);
|
||||
m8 = _mm_cvtps_epi32(m3);
|
||||
|
||||
/* Pack and store */
|
||||
m5 = _mm_packs_epi32(m5, m6);
|
||||
m7 = _mm_packs_epi32(m7, m8);
|
||||
_mm_storeu_si128((__m128i *) & out[16 * i + 0], m5);
|
||||
_mm_storeu_si128((__m128i *) & out[16 * i + 8], m7);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
34
Transceiver52M/x86/convert_sse_3.h
Normal file
34
Transceiver52M/x86/convert_sse_3.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
void _sse_convert_scale_ps_si16_8n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len);
|
||||
|
||||
/* 8*N single precision floats scaled and converted with remainder */
|
||||
void _sse_convert_scale_ps_si16(short *restrict out,
|
||||
const float *restrict in, float scale, int len);
|
||||
|
||||
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
void _sse_convert_scale_ps_si16_16n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len);
|
||||
77
Transceiver52M/x86/convert_sse_4_1.c
Normal file
77
Transceiver52M/x86/convert_sse_4_1.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "convert_sse_4_1.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE4_1
|
||||
#include <smmintrin.h>
|
||||
|
||||
/* 16*N 16-bit signed integer converted to single precision floats */
|
||||
void _sse_convert_si16_ps_16n(float *restrict out,
|
||||
const short *restrict in, int len)
|
||||
{
|
||||
__m128i m0, m1, m2, m3, m4, m5;
|
||||
__m128 m6, m7, m8, m9;
|
||||
|
||||
for (int i = 0; i < len / 16; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_si128((__m128i *) & in[16 * i + 0]);
|
||||
m1 = _mm_loadu_si128((__m128i *) & in[16 * i + 8]);
|
||||
|
||||
/* Unpack */
|
||||
m2 = _mm_cvtepi16_epi32(m0);
|
||||
m4 = _mm_cvtepi16_epi32(m1);
|
||||
m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
m3 = _mm_cvtepi16_epi32(m0);
|
||||
m5 = _mm_cvtepi16_epi32(m1);
|
||||
|
||||
/* Convert */
|
||||
m6 = _mm_cvtepi32_ps(m2);
|
||||
m7 = _mm_cvtepi32_ps(m3);
|
||||
m8 = _mm_cvtepi32_ps(m4);
|
||||
m9 = _mm_cvtepi32_ps(m5);
|
||||
|
||||
/* Store */
|
||||
_mm_storeu_ps(&out[16 * i + 0], m6);
|
||||
_mm_storeu_ps(&out[16 * i + 4], m7);
|
||||
_mm_storeu_ps(&out[16 * i + 8], m8);
|
||||
_mm_storeu_ps(&out[16 * i + 12], m9);
|
||||
}
|
||||
}
|
||||
|
||||
/* 16*N 16-bit signed integer conversion with remainder */
|
||||
void _sse_convert_si16_ps(float *restrict out,
|
||||
const short *restrict in, int len)
|
||||
{
|
||||
int start = len / 16 * 16;
|
||||
|
||||
_sse_convert_si16_ps_16n(out, in, len);
|
||||
|
||||
for (int i = 0; i < len % 16; i++)
|
||||
out[start + i] = in[start + i];
|
||||
}
|
||||
|
||||
#endif
|
||||
28
Transceiver52M/x86/convert_sse_4_1.h
Normal file
28
Transceiver52M/x86/convert_sse_4_1.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* 16*N 16-bit signed integer converted to single precision floats */
|
||||
void _sse_convert_si16_ps_16n(float *restrict out,
|
||||
const short *restrict in, int len);
|
||||
|
||||
/* 16*N 16-bit signed integer conversion with remainder */
|
||||
void _sse_convert_si16_ps(float *restrict out,
|
||||
const short *restrict in, int len);
|
||||
@@ -21,11 +21,37 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "convolve.h"
|
||||
#include "convolve_sse_3.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Architecture dependant function pointers */
|
||||
struct convolve_cpu_context {
|
||||
void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_cmplx_8n) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_cmplx) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real4) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real8) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real12) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real16) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real20) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real4n) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real) (const float *, int, const float *, int, float *, int,
|
||||
int, int, int, int);
|
||||
};
|
||||
static struct convolve_cpu_context c;
|
||||
|
||||
/* Forward declarations from base implementation */
|
||||
int _base_convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
@@ -42,536 +68,77 @@ int _base_convolve_complex(const float *x, int x_len,
|
||||
int bounds_check(int x_len, int h_len, int y_len,
|
||||
int start, int len, int step);
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
#include <xmmintrin.h>
|
||||
#include <pmmintrin.h>
|
||||
|
||||
/* 4-tap SSE complex-real convolution */
|
||||
static void sse_conv_real4(const float *restrict x,
|
||||
const float *restrict h,
|
||||
float *restrict y,
|
||||
int len)
|
||||
/* API: Initalize convolve module */
|
||||
void convolve_init(void)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
c.conv_cmplx_4n = (void *)_base_convolve_complex;
|
||||
c.conv_cmplx_8n = (void *)_base_convolve_complex;
|
||||
c.conv_cmplx = (void *)_base_convolve_complex;
|
||||
c.conv_real4 = (void *)_base_convolve_real;
|
||||
c.conv_real8 = (void *)_base_convolve_real;
|
||||
c.conv_real12 = (void *)_base_convolve_real;
|
||||
c.conv_real16 = (void *)_base_convolve_real;
|
||||
c.conv_real20 = (void *)_base_convolve_real;
|
||||
c.conv_real4n = (void *)_base_convolve_real;
|
||||
c.conv_real = (void *)_base_convolve_real;
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m4 = _mm_mul_ps(m2, m7);
|
||||
m5 = _mm_mul_ps(m3, m7);
|
||||
|
||||
/* Sum and store */
|
||||
m6 = _mm_hadd_ps(m4, m5);
|
||||
m0 = _mm_hadd_ps(m6, m6);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
|
||||
if (__builtin_cpu_supports("sse3")) {
|
||||
c.conv_cmplx_4n = sse_conv_cmplx_4n;
|
||||
c.conv_cmplx_8n = sse_conv_cmplx_8n;
|
||||
c.conv_real4 = sse_conv_real4;
|
||||
c.conv_real8 = sse_conv_real8;
|
||||
c.conv_real12 = sse_conv_real12;
|
||||
c.conv_real16 = sse_conv_real16;
|
||||
c.conv_real20 = sse_conv_real20;
|
||||
c.conv_real4n = sse_conv_real4n;
|
||||
}
|
||||
}
|
||||
|
||||
/* 8-tap SSE complex-real convolution */
|
||||
static void sse_conv_real8(const float *restrict x,
|
||||
const float *restrict h,
|
||||
float *restrict y,
|
||||
int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&x[2 * i + 12]);
|
||||
|
||||
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m6 = _mm_mul_ps(m6, m4);
|
||||
m7 = _mm_mul_ps(m7, m4);
|
||||
m8 = _mm_mul_ps(m8, m5);
|
||||
m9 = _mm_mul_ps(m9, m5);
|
||||
|
||||
/* Sum and store */
|
||||
m6 = _mm_add_ps(m6, m8);
|
||||
m7 = _mm_add_ps(m7, m9);
|
||||
m6 = _mm_hadd_ps(m6, m7);
|
||||
m6 = _mm_hadd_ps(m6, m6);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m6);
|
||||
m6 = _mm_shuffle_ps(m6, m6, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m6);
|
||||
}
|
||||
}
|
||||
|
||||
/* 12-tap SSE complex-real convolution */
|
||||
static void sse_conv_real12(const float *restrict x,
|
||||
const float *restrict h,
|
||||
float *restrict y,
|
||||
int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14;
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
|
||||
m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&x[2 * i + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 16]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 20]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m12);
|
||||
m1 = _mm_mul_ps(m5, m12);
|
||||
m2 = _mm_mul_ps(m6, m13);
|
||||
m3 = _mm_mul_ps(m7, m13);
|
||||
m4 = _mm_mul_ps(m8, m14);
|
||||
m5 = _mm_mul_ps(m9, m14);
|
||||
|
||||
/* Sum and store */
|
||||
m8 = _mm_add_ps(m0, m2);
|
||||
m9 = _mm_add_ps(m1, m3);
|
||||
m10 = _mm_add_ps(m8, m4);
|
||||
m11 = _mm_add_ps(m9, m5);
|
||||
|
||||
m2 = _mm_hadd_ps(m10, m11);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 16-tap SSE complex-real convolution */
|
||||
static void sse_conv_real16(const float *restrict x,
|
||||
const float *restrict h,
|
||||
float *restrict y,
|
||||
int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
m6 = _mm_load_ps(&h[24]);
|
||||
m7 = _mm_load_ps(&h[28]);
|
||||
|
||||
m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m15 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&x[2 * i + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 16]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 20]);
|
||||
m2 = _mm_loadu_ps(&x[2 * i + 24]);
|
||||
m3 = _mm_loadu_ps(&x[2 * i + 28]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m12);
|
||||
m1 = _mm_mul_ps(m5, m12);
|
||||
m2 = _mm_mul_ps(m6, m13);
|
||||
m3 = _mm_mul_ps(m7, m13);
|
||||
|
||||
m4 = _mm_mul_ps(m8, m14);
|
||||
m5 = _mm_mul_ps(m9, m14);
|
||||
m6 = _mm_mul_ps(m10, m15);
|
||||
m7 = _mm_mul_ps(m11, m15);
|
||||
|
||||
/* Sum and store */
|
||||
m8 = _mm_add_ps(m0, m2);
|
||||
m9 = _mm_add_ps(m1, m3);
|
||||
m10 = _mm_add_ps(m4, m6);
|
||||
m11 = _mm_add_ps(m5, m7);
|
||||
|
||||
m0 = _mm_add_ps(m8, m10);
|
||||
m1 = _mm_add_ps(m9, m11);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 20-tap SSE complex-real convolution */
|
||||
static void sse_conv_real20(const float *restrict x,
|
||||
const float *restrict h,
|
||||
float *restrict y,
|
||||
int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m11, m12, m13, m14, m15;
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
m6 = _mm_load_ps(&h[24]);
|
||||
m7 = _mm_load_ps(&h[28]);
|
||||
m8 = _mm_load_ps(&h[32]);
|
||||
m9 = _mm_load_ps(&h[36]);
|
||||
|
||||
m11 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m12 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m15 = _mm_shuffle_ps(m8, m9, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Multiply-accumulate first 12 taps */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&x[2 * i + 12]);
|
||||
m4 = _mm_loadu_ps(&x[2 * i + 16]);
|
||||
m5 = _mm_loadu_ps(&x[2 * i + 20]);
|
||||
|
||||
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m2 = _mm_mul_ps(m6, m11);
|
||||
m3 = _mm_mul_ps(m7, m11);
|
||||
m4 = _mm_mul_ps(m8, m12);
|
||||
m5 = _mm_mul_ps(m9, m12);
|
||||
m6 = _mm_mul_ps(m0, m13);
|
||||
m7 = _mm_mul_ps(m1, m13);
|
||||
|
||||
m0 = _mm_add_ps(m2, m4);
|
||||
m1 = _mm_add_ps(m3, m5);
|
||||
m8 = _mm_add_ps(m0, m6);
|
||||
m9 = _mm_add_ps(m1, m7);
|
||||
|
||||
/* Multiply-accumulate last 8 taps */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 24]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 28]);
|
||||
m2 = _mm_loadu_ps(&x[2 * i + 32]);
|
||||
m3 = _mm_loadu_ps(&x[2 * i + 36]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_mul_ps(m4, m14);
|
||||
m1 = _mm_mul_ps(m5, m14);
|
||||
m2 = _mm_mul_ps(m6, m15);
|
||||
m3 = _mm_mul_ps(m7, m15);
|
||||
|
||||
m4 = _mm_add_ps(m0, m2);
|
||||
m5 = _mm_add_ps(m1, m3);
|
||||
|
||||
/* Final sum and store */
|
||||
m0 = _mm_add_ps(m8, m4);
|
||||
m1 = _mm_add_ps(m9, m5);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 4*N-tap SSE complex-real convolution */
|
||||
static void sse_conv_real4n(const float *x,
|
||||
const float *h,
|
||||
float *y,
|
||||
int h_len, int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m4, m5, m6, m7;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m6 = _mm_setzero_ps();
|
||||
m7 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 4; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[8 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[8 * n + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]);
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m2, m4);
|
||||
m1 = _mm_mul_ps(m2, m5);
|
||||
|
||||
/* Accumulate */
|
||||
m6 = _mm_add_ps(m6, m0);
|
||||
m7 = _mm_add_ps(m7, m1);
|
||||
}
|
||||
|
||||
m0 = _mm_hadd_ps(m6, m7);
|
||||
m0 = _mm_hadd_ps(m0, m0);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 4*N-tap SSE complex-complex convolution */
|
||||
static void sse_conv_cmplx_4n(const float *x,
|
||||
const float *h,
|
||||
float *y,
|
||||
int h_len, int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m6 = _mm_setzero_ps();
|
||||
m7 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 4; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[8 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[8 * n + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]);
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m2, m4);
|
||||
m1 = _mm_mul_ps(m3, m5);
|
||||
|
||||
m2 = _mm_mul_ps(m2, m5);
|
||||
m3 = _mm_mul_ps(m3, m4);
|
||||
|
||||
/* Sum */
|
||||
m0 = _mm_sub_ps(m0, m1);
|
||||
m2 = _mm_add_ps(m2, m3);
|
||||
|
||||
/* Accumulate */
|
||||
m6 = _mm_add_ps(m6, m0);
|
||||
m7 = _mm_add_ps(m7, m2);
|
||||
}
|
||||
|
||||
m0 = _mm_hadd_ps(m6, m7);
|
||||
m0 = _mm_hadd_ps(m0, m0);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8*N-tap SSE complex-complex convolution */
|
||||
static void sse_conv_cmplx_8n(const float *x,
|
||||
const float *h,
|
||||
float *y,
|
||||
int h_len, int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m12 = _mm_setzero_ps();
|
||||
m13 = _mm_setzero_ps();
|
||||
m14 = _mm_setzero_ps();
|
||||
m15 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 8; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[16 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[16 * n + 4]);
|
||||
m2 = _mm_load_ps(&h[16 * n + 8]);
|
||||
m3 = _mm_load_ps(&h[16 * n + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&x[2 * i + 16 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&x[2 * i + 16 * n + 4]);
|
||||
m2 = _mm_loadu_ps(&x[2 * i + 16 * n + 8]);
|
||||
m3 = _mm_loadu_ps(&x[2 * i + 16 * n + 12]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m8);
|
||||
m1 = _mm_mul_ps(m5, m9);
|
||||
m2 = _mm_mul_ps(m6, m10);
|
||||
m3 = _mm_mul_ps(m7, m11);
|
||||
|
||||
m4 = _mm_mul_ps(m4, m9);
|
||||
m5 = _mm_mul_ps(m5, m8);
|
||||
m6 = _mm_mul_ps(m6, m11);
|
||||
m7 = _mm_mul_ps(m7, m10);
|
||||
|
||||
/* Sum */
|
||||
m0 = _mm_sub_ps(m0, m1);
|
||||
m2 = _mm_sub_ps(m2, m3);
|
||||
m4 = _mm_add_ps(m4, m5);
|
||||
m6 = _mm_add_ps(m6, m7);
|
||||
|
||||
/* Accumulate */
|
||||
m12 = _mm_add_ps(m12, m0);
|
||||
m13 = _mm_add_ps(m13, m2);
|
||||
m14 = _mm_add_ps(m14, m4);
|
||||
m15 = _mm_add_ps(m15, m6);
|
||||
}
|
||||
|
||||
m0 = _mm_add_ps(m12, m13);
|
||||
m1 = _mm_add_ps(m14, m15);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m2 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m2);
|
||||
m2 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* API: Aligned complex-real */
|
||||
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)
|
||||
float *y, int y_len, int start, int len, int step, int offset)
|
||||
{
|
||||
void (*conv_func)(const float *, const float *,
|
||||
float *, int) = NULL;
|
||||
void (*conv_func_n)(const float *, const 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_SSE3
|
||||
if (step <= 4) {
|
||||
switch (h_len) {
|
||||
case 4:
|
||||
conv_func = sse_conv_real4;
|
||||
c.conv_real4(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
case 8:
|
||||
conv_func = sse_conv_real8;
|
||||
c.conv_real8(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
case 12:
|
||||
conv_func = sse_conv_real12;
|
||||
c.conv_real12(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
case 16:
|
||||
conv_func = sse_conv_real16;
|
||||
c.conv_real16(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
case 20:
|
||||
conv_func = sse_conv_real20;
|
||||
c.conv_real20(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
default:
|
||||
if (!(h_len % 4))
|
||||
conv_func_n = sse_conv_real4n;
|
||||
c.conv_real4n(x, x_len, h, h_len, y, y_len,
|
||||
start, len, step, offset);
|
||||
else
|
||||
c.conv_real(x, x_len, h, h_len, y, y_len, start,
|
||||
len, step, offset);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (conv_func) {
|
||||
conv_func(&x[2 * (-(h_len - 1) + start)],
|
||||
h, y, len);
|
||||
} else if (conv_func_n) {
|
||||
conv_func_n(&x[2 * (-(h_len - 1) + start)],
|
||||
h, y, h_len, len);
|
||||
} else {
|
||||
_base_convolve_real(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
} else
|
||||
c.conv_real(x, x_len, h, h_len, y, y_len, start, len, step,
|
||||
offset);
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -580,34 +147,26 @@ int convolve_real(const float *x, int x_len,
|
||||
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 start, int len, int step, int offset)
|
||||
{
|
||||
void (*conv_func)(const float *, const 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_SSE3
|
||||
if (step <= 4) {
|
||||
if (!(h_len % 8))
|
||||
conv_func = sse_conv_cmplx_8n;
|
||||
c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start,
|
||||
len, step, offset);
|
||||
else if (!(h_len % 4))
|
||||
conv_func = sse_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);
|
||||
}
|
||||
c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start,
|
||||
len, step, offset);
|
||||
else
|
||||
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
} else
|
||||
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len, step,
|
||||
offset);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
542
Transceiver52M/x86/convolve_sse_3.c
Normal file
542
Transceiver52M/x86/convolve_sse_3.c
Normal file
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* SSE 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>
|
||||
#include "convolve_sse_3.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
#include <xmmintrin.h>
|
||||
#include <pmmintrin.h>
|
||||
|
||||
/* 4-tap SSE complex-real convolution */
|
||||
void sse_conv_real4(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)
|
||||
{
|
||||
/* NOTE: The parameter list of this function has to match the parameter
|
||||
* list of _base_convolve_real() in convolve_base.c. This specific
|
||||
* implementation, ignores some of the parameters of
|
||||
* _base_convolve_complex(), which are: x_len, y_len, offset, step */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m4 = _mm_mul_ps(m2, m7);
|
||||
m5 = _mm_mul_ps(m3, m7);
|
||||
|
||||
/* Sum and store */
|
||||
m6 = _mm_hadd_ps(m4, m5);
|
||||
m0 = _mm_hadd_ps(m6, m6);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8-tap SSE complex-real convolution */
|
||||
void sse_conv_real8(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)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||
|
||||
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m6 = _mm_mul_ps(m6, m4);
|
||||
m7 = _mm_mul_ps(m7, m4);
|
||||
m8 = _mm_mul_ps(m8, m5);
|
||||
m9 = _mm_mul_ps(m9, m5);
|
||||
|
||||
/* Sum and store */
|
||||
m6 = _mm_add_ps(m6, m8);
|
||||
m7 = _mm_add_ps(m7, m9);
|
||||
m6 = _mm_hadd_ps(m6, m7);
|
||||
m6 = _mm_hadd_ps(m6, m6);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m6);
|
||||
m6 = _mm_shuffle_ps(m6, m6, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m6);
|
||||
}
|
||||
}
|
||||
|
||||
/* 12-tap SSE complex-real convolution */
|
||||
void sse_conv_real12(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)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
|
||||
m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m12);
|
||||
m1 = _mm_mul_ps(m5, m12);
|
||||
m2 = _mm_mul_ps(m6, m13);
|
||||
m3 = _mm_mul_ps(m7, m13);
|
||||
m4 = _mm_mul_ps(m8, m14);
|
||||
m5 = _mm_mul_ps(m9, m14);
|
||||
|
||||
/* Sum and store */
|
||||
m8 = _mm_add_ps(m0, m2);
|
||||
m9 = _mm_add_ps(m1, m3);
|
||||
m10 = _mm_add_ps(m8, m4);
|
||||
m11 = _mm_add_ps(m9, m5);
|
||||
|
||||
m2 = _mm_hadd_ps(m10, m11);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 16-tap SSE complex-real convolution */
|
||||
void sse_conv_real16(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)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
m6 = _mm_load_ps(&h[24]);
|
||||
m7 = _mm_load_ps(&h[28]);
|
||||
|
||||
m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m15 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 24]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 28]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m12);
|
||||
m1 = _mm_mul_ps(m5, m12);
|
||||
m2 = _mm_mul_ps(m6, m13);
|
||||
m3 = _mm_mul_ps(m7, m13);
|
||||
|
||||
m4 = _mm_mul_ps(m8, m14);
|
||||
m5 = _mm_mul_ps(m9, m14);
|
||||
m6 = _mm_mul_ps(m10, m15);
|
||||
m7 = _mm_mul_ps(m11, m15);
|
||||
|
||||
/* Sum and store */
|
||||
m8 = _mm_add_ps(m0, m2);
|
||||
m9 = _mm_add_ps(m1, m3);
|
||||
m10 = _mm_add_ps(m4, m6);
|
||||
m11 = _mm_add_ps(m5, m7);
|
||||
|
||||
m0 = _mm_add_ps(m8, m10);
|
||||
m1 = _mm_add_ps(m9, m11);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 20-tap SSE complex-real convolution */
|
||||
void sse_conv_real20(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)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m11, m12, m13, m14, m15;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
m6 = _mm_load_ps(&h[24]);
|
||||
m7 = _mm_load_ps(&h[28]);
|
||||
m8 = _mm_load_ps(&h[32]);
|
||||
m9 = _mm_load_ps(&h[36]);
|
||||
|
||||
m11 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m12 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m15 = _mm_shuffle_ps(m8, m9, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Multiply-accumulate first 12 taps */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||
m4 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||
m5 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||
|
||||
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m2 = _mm_mul_ps(m6, m11);
|
||||
m3 = _mm_mul_ps(m7, m11);
|
||||
m4 = _mm_mul_ps(m8, m12);
|
||||
m5 = _mm_mul_ps(m9, m12);
|
||||
m6 = _mm_mul_ps(m0, m13);
|
||||
m7 = _mm_mul_ps(m1, m13);
|
||||
|
||||
m0 = _mm_add_ps(m2, m4);
|
||||
m1 = _mm_add_ps(m3, m5);
|
||||
m8 = _mm_add_ps(m0, m6);
|
||||
m9 = _mm_add_ps(m1, m7);
|
||||
|
||||
/* Multiply-accumulate last 8 taps */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 24]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 28]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 32]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 36]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_mul_ps(m4, m14);
|
||||
m1 = _mm_mul_ps(m5, m14);
|
||||
m2 = _mm_mul_ps(m6, m15);
|
||||
m3 = _mm_mul_ps(m7, m15);
|
||||
|
||||
m4 = _mm_add_ps(m0, m2);
|
||||
m5 = _mm_add_ps(m1, m3);
|
||||
|
||||
/* Final sum and store */
|
||||
m0 = _mm_add_ps(m8, m4);
|
||||
m1 = _mm_add_ps(m9, m5);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 4*N-tap SSE complex-real convolution */
|
||||
void sse_conv_real4n(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)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m4, m5, m6, m7;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m6 = _mm_setzero_ps();
|
||||
m7 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 4; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[8 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[8 * n + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]);
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m2, m4);
|
||||
m1 = _mm_mul_ps(m2, m5);
|
||||
|
||||
/* Accumulate */
|
||||
m6 = _mm_add_ps(m6, m0);
|
||||
m7 = _mm_add_ps(m7, m1);
|
||||
}
|
||||
|
||||
m0 = _mm_hadd_ps(m6, m7);
|
||||
m0 = _mm_hadd_ps(m0, m0);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 4*N-tap SSE complex-complex convolution */
|
||||
void sse_conv_cmplx_4n(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)
|
||||
{
|
||||
/* NOTE: The parameter list of this function has to match the parameter
|
||||
* list of _base_convolve_complex() in convolve_base.c. This specific
|
||||
* implementation, ignores some of the parameters of
|
||||
* _base_convolve_complex(), which are: x_len, y_len, offset, step. */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m6 = _mm_setzero_ps();
|
||||
m7 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 4; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[8 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[8 * n + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]);
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m2, m4);
|
||||
m1 = _mm_mul_ps(m3, m5);
|
||||
|
||||
m2 = _mm_mul_ps(m2, m5);
|
||||
m3 = _mm_mul_ps(m3, m4);
|
||||
|
||||
/* Sum */
|
||||
m0 = _mm_sub_ps(m0, m1);
|
||||
m2 = _mm_add_ps(m2, m3);
|
||||
|
||||
/* Accumulate */
|
||||
m6 = _mm_add_ps(m6, m0);
|
||||
m7 = _mm_add_ps(m7, m2);
|
||||
}
|
||||
|
||||
m0 = _mm_hadd_ps(m6, m7);
|
||||
m0 = _mm_hadd_ps(m0, m0);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8*N-tap SSE complex-complex convolution */
|
||||
void sse_conv_cmplx_8n(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)
|
||||
{
|
||||
/* See NOTE in sse_conv_cmplx_4n() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m12 = _mm_setzero_ps();
|
||||
m13 = _mm_setzero_ps();
|
||||
m14 = _mm_setzero_ps();
|
||||
m15 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 8; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[16 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[16 * n + 4]);
|
||||
m2 = _mm_load_ps(&h[16 * n + 8]);
|
||||
m3 = _mm_load_ps(&h[16 * n + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 16 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 16 * n + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 16 * n + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 16 * n + 12]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m8);
|
||||
m1 = _mm_mul_ps(m5, m9);
|
||||
m2 = _mm_mul_ps(m6, m10);
|
||||
m3 = _mm_mul_ps(m7, m11);
|
||||
|
||||
m4 = _mm_mul_ps(m4, m9);
|
||||
m5 = _mm_mul_ps(m5, m8);
|
||||
m6 = _mm_mul_ps(m6, m11);
|
||||
m7 = _mm_mul_ps(m7, m10);
|
||||
|
||||
/* Sum */
|
||||
m0 = _mm_sub_ps(m0, m1);
|
||||
m2 = _mm_sub_ps(m2, m3);
|
||||
m4 = _mm_add_ps(m4, m5);
|
||||
m6 = _mm_add_ps(m6, m7);
|
||||
|
||||
/* Accumulate */
|
||||
m12 = _mm_add_ps(m12, m0);
|
||||
m13 = _mm_add_ps(m13, m2);
|
||||
m14 = _mm_add_ps(m14, m4);
|
||||
m15 = _mm_add_ps(m15, m6);
|
||||
}
|
||||
|
||||
m0 = _mm_add_ps(m12, m13);
|
||||
m1 = _mm_add_ps(m14, m15);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m2 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m2);
|
||||
m2 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
68
Transceiver52M/x86/convolve_sse_3.h
Normal file
68
Transceiver52M/x86/convolve_sse_3.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SSE 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* 4-tap SSE complex-real convolution */
|
||||
void sse_conv_real4(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);
|
||||
|
||||
/* 8-tap SSE complex-real convolution */
|
||||
void sse_conv_real8(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);
|
||||
|
||||
/* 12-tap SSE complex-real convolution */
|
||||
void sse_conv_real12(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);
|
||||
|
||||
/* 16-tap SSE complex-real convolution */
|
||||
void sse_conv_real16(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);
|
||||
|
||||
/* 20-tap SSE complex-real convolution */
|
||||
void sse_conv_real20(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);
|
||||
|
||||
/* 4*N-tap SSE complex-real convolution */
|
||||
void sse_conv_real4n(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);
|
||||
|
||||
/* 4*N-tap SSE complex-complex convolution */
|
||||
void sse_conv_cmplx_4n(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);
|
||||
|
||||
/* 8*N-tap SSE complex-complex convolution */
|
||||
void sse_conv_cmplx_8n(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);
|
||||
982
config/ax_cxx_compile_stdcxx.m4
Normal file
982
config/ax_cxx_compile_stdcxx.m4
Normal file
@@ -0,0 +1,982 @@
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXX and
|
||||
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
|
||||
# or '14' (for the C++14 standard).
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
# -std=c++11). If neither is specified, you get whatever works, with
|
||||
# preference for an extended mode.
|
||||
#
|
||||
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||
# indicates that baseline support for the specified C++ standard is
|
||||
# required and that the macro should error out if no mode with that
|
||||
# support is found. If specified 'optional', then configuration proceeds
|
||||
# regardless, after defining HAVE_CXX${VERSION} if and only if a
|
||||
# supporting mode is found.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
# Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 7
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
|
||||
AX_REQUIRE_DEFINED([AC_MSG_WARN])
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
|
||||
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
|
||||
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
[$2], [noext], [],
|
||||
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
|
||||
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||
AC_LANG_PUSH([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$2], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
switch="-std=gnu++${alternative}"
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
if test -n "$CXXCPP" ; then
|
||||
CXXCPP="$CXXCPP $switch"
|
||||
fi
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
|
||||
m4_if([$2], [ext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
if test -n "$CXXCPP" ; then
|
||||
CXXCPP="$CXXCPP $switch"
|
||||
fi
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test x$ac_success = xyes; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
AC_LANG_POP([C++])
|
||||
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
|
||||
if test x$ac_success = xno; then
|
||||
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
|
||||
fi
|
||||
fi
|
||||
if test x$ac_success = xno; then
|
||||
HAVE_CXX$1=0
|
||||
AC_MSG_NOTICE([No compiler with C++$1 support was found])
|
||||
else
|
||||
HAVE_CXX$1=1
|
||||
AC_DEFINE(HAVE_CXX$1,1,
|
||||
[define if the compiler supports basic C++$1 syntax])
|
||||
fi
|
||||
AC_SUBST(HAVE_CXX$1)
|
||||
m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])])
|
||||
])
|
||||
|
||||
|
||||
dnl Test body for checking C++11 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
)
|
||||
|
||||
|
||||
dnl Test body for checking C++14 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
)
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||
)
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++11, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201103L
|
||||
|
||||
#error "This is not a C++11 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx11
|
||||
{
|
||||
|
||||
namespace test_static_assert
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_final_override
|
||||
{
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual void f() {}
|
||||
};
|
||||
|
||||
struct Derived : public Base
|
||||
{
|
||||
virtual void f() override {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_double_right_angle_brackets
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
struct check {};
|
||||
|
||||
typedef check<void> single_type;
|
||||
typedef check<check<void>> double_type;
|
||||
typedef check<check<check<void>>> triple_type;
|
||||
typedef check<check<check<check<void>>>> quadruple_type;
|
||||
|
||||
}
|
||||
|
||||
namespace test_decltype
|
||||
{
|
||||
|
||||
int
|
||||
f()
|
||||
{
|
||||
int a = 1;
|
||||
decltype(a) b = 2;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_type_deduction
|
||||
{
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
auto
|
||||
add(T1 a1, T2 a2) -> decltype(a1 + a2)
|
||||
{
|
||||
return a1 + a2;
|
||||
}
|
||||
|
||||
int
|
||||
test(const int c, volatile int v)
|
||||
{
|
||||
static_assert(is_same<int, decltype(0)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(c)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(v)>::value == false, "");
|
||||
auto ac = c;
|
||||
auto av = v;
|
||||
auto sumi = ac + av + 'x';
|
||||
auto sumf = ac + av + 1.0;
|
||||
static_assert(is_same<int, decltype(ac)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(av)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumi)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumf)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
|
||||
return (sumf > 0.0) ? sumi : add(c, v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_noexcept
|
||||
{
|
||||
|
||||
int f() { return 0; }
|
||||
int g() noexcept { return 0; }
|
||||
|
||||
static_assert(noexcept(f()) == false, "");
|
||||
static_assert(noexcept(g()) == true, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
|
||||
{
|
||||
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
|
||||
}
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
return strlen_c_r(s, 0UL);
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("1") == 1UL, "");
|
||||
static_assert(strlen_c("example") == 7UL, "");
|
||||
static_assert(strlen_c("another\0example") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_rvalue_references
|
||||
{
|
||||
|
||||
template < int N >
|
||||
struct answer
|
||||
{
|
||||
static constexpr int value = N;
|
||||
};
|
||||
|
||||
answer<1> f(int&) { return answer<1>(); }
|
||||
answer<2> f(const int&) { return answer<2>(); }
|
||||
answer<3> f(int&&) { return answer<3>(); }
|
||||
|
||||
void
|
||||
test()
|
||||
{
|
||||
int i = 0;
|
||||
const int c = 0;
|
||||
static_assert(decltype(f(i))::value == 1, "");
|
||||
static_assert(decltype(f(c))::value == 2, "");
|
||||
static_assert(decltype(f(0))::value == 3, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_uniform_initialization
|
||||
{
|
||||
|
||||
struct test
|
||||
{
|
||||
static const int zero {};
|
||||
static const int one {1};
|
||||
};
|
||||
|
||||
static_assert(test::zero == 0, "");
|
||||
static_assert(test::one == 1, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambdas
|
||||
{
|
||||
|
||||
void
|
||||
test1()
|
||||
{
|
||||
auto lambda1 = [](){};
|
||||
auto lambda2 = lambda1;
|
||||
lambda1();
|
||||
lambda2();
|
||||
}
|
||||
|
||||
int
|
||||
test2()
|
||||
{
|
||||
auto a = [](int i, int j){ return i + j; }(1, 2);
|
||||
auto b = []() -> int { return '0'; }();
|
||||
auto c = [=](){ return a + b; }();
|
||||
auto d = [&](){ return c; }();
|
||||
auto e = [a, &b](int x) mutable {
|
||||
const auto identity = [](int y){ return y; };
|
||||
for (auto i = 0; i < a; ++i)
|
||||
a += b--;
|
||||
return x + identity(a + b);
|
||||
}(0);
|
||||
return a + b + c + d + e;
|
||||
}
|
||||
|
||||
int
|
||||
test3()
|
||||
{
|
||||
const auto nullary = [](){ return 0; };
|
||||
const auto unary = [](int x){ return x; };
|
||||
using nullary_t = decltype(nullary);
|
||||
using unary_t = decltype(unary);
|
||||
const auto higher1st = [](nullary_t f){ return f(); };
|
||||
const auto higher2nd = [unary](nullary_t f1){
|
||||
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
|
||||
};
|
||||
return higher1st(nullary) + higher2nd(nullary)(unary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_variadic_templates
|
||||
{
|
||||
|
||||
template <int...>
|
||||
struct sum;
|
||||
|
||||
template <int N0, int... N1toN>
|
||||
struct sum<N0, N1toN...>
|
||||
{
|
||||
static constexpr auto value = N0 + sum<N1toN...>::value;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct sum<>
|
||||
{
|
||||
static constexpr auto value = 0;
|
||||
};
|
||||
|
||||
static_assert(sum<>::value == 0, "");
|
||||
static_assert(sum<1>::value == 1, "");
|
||||
static_assert(sum<23>::value == 23, "");
|
||||
static_assert(sum<1, 2>::value == 3, "");
|
||||
static_assert(sum<5, 5, 11>::value == 21, "");
|
||||
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
|
||||
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
|
||||
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
|
||||
// because of this.
|
||||
namespace test_template_alias_sfinae
|
||||
{
|
||||
|
||||
struct foo {};
|
||||
|
||||
template<typename T>
|
||||
using member = typename T::member_type;
|
||||
|
||||
template<typename T>
|
||||
void func(...) {}
|
||||
|
||||
template<typename T>
|
||||
void func(member<T>*) {}
|
||||
|
||||
void test();
|
||||
|
||||
void test() { func<foo>(0); }
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx11
|
||||
|
||||
#endif // __cplusplus >= 201103L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++14
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++14, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201402L
|
||||
|
||||
#error "This is not a C++14 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx14
|
||||
{
|
||||
|
||||
namespace test_polymorphic_lambdas
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
const auto lambda = [](auto&&... args){
|
||||
const auto istiny = [](auto x){
|
||||
return (sizeof(x) == 1UL) ? 1 : 0;
|
||||
};
|
||||
const int aretiny[] = { istiny(args)... };
|
||||
return aretiny[0];
|
||||
};
|
||||
return lambda(1, 1L, 1.0f, '1');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_binary_literals
|
||||
{
|
||||
|
||||
constexpr auto ivii = 0b0000000000101010;
|
||||
static_assert(ivii == 42, "wrong value");
|
||||
|
||||
}
|
||||
|
||||
namespace test_generalized_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
constexpr unsigned long
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
auto length = 0UL;
|
||||
for (auto p = s; *p; ++p)
|
||||
++length;
|
||||
return length;
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("x") == 1UL, "");
|
||||
static_assert(strlen_c("test") == 4UL, "");
|
||||
static_assert(strlen_c("another\0test") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_init_capture
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
const auto lambda1 = [a = x](int b){ return a + b; };
|
||||
const auto lambda2 = [a = lambda1(x)](){ return a; };
|
||||
return lambda2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_digit_separators
|
||||
{
|
||||
|
||||
constexpr auto ten_million = 100'000'000;
|
||||
static_assert(ten_million == 100000000, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_return_type_deduction
|
||||
{
|
||||
|
||||
auto f(int& x) { return x; }
|
||||
decltype(auto) g(int& x) { return x; }
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static constexpr auto value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static constexpr auto value = true;
|
||||
};
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
static_assert(is_same<int, decltype(f(x))>::value, "");
|
||||
static_assert(is_same<int&, decltype(g(x))>::value, "");
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx14
|
||||
|
||||
#endif // __cplusplus >= 201402L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++17
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++17, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus <= 201402L
|
||||
|
||||
#error "This is not a C++17 compiler"
|
||||
|
||||
#else
|
||||
|
||||
#if defined(__clang__)
|
||||
#define REALLY_CLANG
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define REALLY_GCC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
namespace cxx17
|
||||
{
|
||||
|
||||
#if !defined(REALLY_CLANG)
|
||||
namespace test_constexpr_lambdas
|
||||
{
|
||||
|
||||
// TODO: test it with clang++ from git
|
||||
|
||||
constexpr int foo = [](){return 42;}();
|
||||
|
||||
}
|
||||
#endif // !defined(REALLY_CLANG)
|
||||
|
||||
namespace test::nested_namespace::definitions
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
namespace test_fold_expression
|
||||
{
|
||||
|
||||
template<typename... Args>
|
||||
int multiply(Args... args)
|
||||
{
|
||||
return (args * ... * 1);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool all(Args... args)
|
||||
{
|
||||
return (args && ...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_extended_static_assert
|
||||
{
|
||||
|
||||
static_assert (true);
|
||||
|
||||
}
|
||||
|
||||
namespace test_auto_brace_init_list
|
||||
{
|
||||
|
||||
auto foo = {5};
|
||||
auto bar {5};
|
||||
|
||||
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
|
||||
static_assert(std::is_same<int, decltype(bar)>::value);
|
||||
}
|
||||
|
||||
namespace test_typename_in_template_template_parameter
|
||||
{
|
||||
|
||||
template<template<typename> typename X> struct D;
|
||||
|
||||
}
|
||||
|
||||
namespace test_fallthrough_nodiscard_maybe_unused_attributes
|
||||
{
|
||||
|
||||
int f1()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
[[nodiscard]] int f2()
|
||||
{
|
||||
[[maybe_unused]] auto unused = f1();
|
||||
|
||||
switch (f1())
|
||||
{
|
||||
case 17:
|
||||
f1();
|
||||
[[fallthrough]];
|
||||
case 42:
|
||||
f1();
|
||||
}
|
||||
return f1();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_extended_aggregate_initialization
|
||||
{
|
||||
|
||||
struct base1
|
||||
{
|
||||
int b1, b2 = 42;
|
||||
};
|
||||
|
||||
struct base2
|
||||
{
|
||||
base2() {
|
||||
b3 = 42;
|
||||
}
|
||||
int b3;
|
||||
};
|
||||
|
||||
struct derived : base1, base2
|
||||
{
|
||||
int d;
|
||||
};
|
||||
|
||||
derived d1 {{1, 2}, {}, 4}; // full initialization
|
||||
derived d2 {{}, {}, 4}; // value-initialized bases
|
||||
|
||||
}
|
||||
|
||||
namespace test_general_range_based_for_loop
|
||||
{
|
||||
|
||||
struct iter
|
||||
{
|
||||
int i;
|
||||
|
||||
int& operator* ()
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
const int& operator* () const
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
iter& operator++()
|
||||
{
|
||||
++i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct sentinel
|
||||
{
|
||||
int i;
|
||||
};
|
||||
|
||||
bool operator== (const iter& i, const sentinel& s)
|
||||
{
|
||||
return i.i == s.i;
|
||||
}
|
||||
|
||||
bool operator!= (const iter& i, const sentinel& s)
|
||||
{
|
||||
return !(i == s);
|
||||
}
|
||||
|
||||
struct range
|
||||
{
|
||||
iter begin() const
|
||||
{
|
||||
return {0};
|
||||
}
|
||||
|
||||
sentinel end() const
|
||||
{
|
||||
return {5};
|
||||
}
|
||||
};
|
||||
|
||||
void f()
|
||||
{
|
||||
range r {};
|
||||
|
||||
for (auto i : r)
|
||||
{
|
||||
[[maybe_unused]] auto v = i;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_capture_asterisk_this_by_value
|
||||
{
|
||||
|
||||
struct t
|
||||
{
|
||||
int i;
|
||||
int foo()
|
||||
{
|
||||
return [*this]()
|
||||
{
|
||||
return i;
|
||||
}();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_enum_class_construction
|
||||
{
|
||||
|
||||
enum class byte : unsigned char
|
||||
{};
|
||||
|
||||
byte foo {42};
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr_if
|
||||
{
|
||||
|
||||
template <bool cond>
|
||||
int f ()
|
||||
{
|
||||
if constexpr(cond)
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_selection_statement_with_initializer
|
||||
{
|
||||
|
||||
int f()
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
|
||||
int f2()
|
||||
{
|
||||
if (auto i = f(); i > 0)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
switch (auto i = f(); i + 4)
|
||||
{
|
||||
case 17:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if !defined(REALLY_CLANG)
|
||||
namespace test_template_argument_deduction_for_class_templates
|
||||
{
|
||||
|
||||
// TODO: test it with clang++ from git
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct pair
|
||||
{
|
||||
pair (T1 p1, T2 p2)
|
||||
: m1 {p1},
|
||||
m2 {p2}
|
||||
{}
|
||||
|
||||
T1 m1;
|
||||
T2 m2;
|
||||
};
|
||||
|
||||
void f()
|
||||
{
|
||||
[[maybe_unused]] auto p = pair{13, 42u};
|
||||
}
|
||||
|
||||
}
|
||||
#endif // !defined(REALLY_CLANG)
|
||||
|
||||
namespace test_non_type_auto_template_parameters
|
||||
{
|
||||
|
||||
template <auto n>
|
||||
struct B
|
||||
{};
|
||||
|
||||
B<5> b1;
|
||||
B<'a'> b2;
|
||||
|
||||
}
|
||||
|
||||
#if !defined(REALLY_CLANG)
|
||||
namespace test_structured_bindings
|
||||
{
|
||||
|
||||
// TODO: test it with clang++ from git
|
||||
|
||||
int arr[2] = { 1, 2 };
|
||||
std::pair<int, int> pr = { 1, 2 };
|
||||
|
||||
auto f1() -> int(&)[2]
|
||||
{
|
||||
return arr;
|
||||
}
|
||||
|
||||
auto f2() -> std::pair<int, int>&
|
||||
{
|
||||
return pr;
|
||||
}
|
||||
|
||||
struct S
|
||||
{
|
||||
int x1 : 2;
|
||||
volatile double y1;
|
||||
};
|
||||
|
||||
S f3()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto [ x1, y1 ] = f1();
|
||||
auto& [ xr1, yr1 ] = f1();
|
||||
auto [ x2, y2 ] = f2();
|
||||
auto& [ xr2, yr2 ] = f2();
|
||||
const auto [ x3, y3 ] = f3();
|
||||
|
||||
}
|
||||
#endif // !defined(REALLY_CLANG)
|
||||
|
||||
#if !defined(REALLY_CLANG)
|
||||
namespace test_exception_spec_type_system
|
||||
{
|
||||
|
||||
// TODO: test it with clang++ from git
|
||||
|
||||
struct Good {};
|
||||
struct Bad {};
|
||||
|
||||
void g1() noexcept;
|
||||
void g2();
|
||||
|
||||
template<typename T>
|
||||
Bad
|
||||
f(T*, T*);
|
||||
|
||||
template<typename T1, typename T2>
|
||||
Good
|
||||
f(T1*, T2*);
|
||||
|
||||
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
|
||||
|
||||
}
|
||||
#endif // !defined(REALLY_CLANG)
|
||||
|
||||
namespace test_inline_variables
|
||||
{
|
||||
|
||||
template<class T> void f(T)
|
||||
{}
|
||||
|
||||
template<class T> inline T g(T)
|
||||
{
|
||||
return T{};
|
||||
}
|
||||
|
||||
template<> inline void f<>(int)
|
||||
{}
|
||||
|
||||
template<> int g<>(int)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx17
|
||||
|
||||
#endif // __cplusplus <= 201402L
|
||||
|
||||
]])
|
||||
39
config/ax_cxx_compile_stdcxx_11.m4
Normal file
39
config/ax_cxx_compile_stdcxx_11.m4
Normal file
@@ -0,0 +1,39 @@
|
||||
# =============================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
|
||||
# =============================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the C++11
|
||||
# standard; if necessary, add switches to CXX and CXXCPP to enable
|
||||
# support.
|
||||
#
|
||||
# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
|
||||
# macro with the version set to C++11. The two optional arguments are
|
||||
# forwarded literally as the second and third argument respectively.
|
||||
# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
|
||||
# more information. If you want to use this macro, you also need to
|
||||
# download the ax_cxx_compile_stdcxx.m4 file.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 18
|
||||
|
||||
AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])
|
||||
221
config/ax_ext.m4
221
config/ax_ext.m4
@@ -1,221 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_ext.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_EXT
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Find supported SIMD extensions by requesting cpuid. When an SIMD
|
||||
# extension is found, the -m"simdextensionname" is added to SIMD_FLAGS if
|
||||
# compiler supports it. For example, if "sse2" is available, then "-msse2"
|
||||
# is added to SIMD_FLAGS.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(SIMD_FLAGS)
|
||||
#
|
||||
# And defines:
|
||||
#
|
||||
# HAVE_MMX / HAVE_SSE / HAVE_SSE2 / HAVE_SSE3 / HAVE_SSSE3 / HAVE_SSE4.1 / HAVE_SSE4.2 / HAVE_AVX
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2007 Christophe Tournayre <turn3r@users.sourceforge.net>
|
||||
# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 12
|
||||
|
||||
AC_DEFUN([AX_EXT],
|
||||
[
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
|
||||
case $host_cpu in
|
||||
i[[3456]]86*|x86_64*|amd64*)
|
||||
|
||||
AC_REQUIRE([AX_GCC_X86_CPUID])
|
||||
AC_REQUIRE([AX_GCC_X86_AVX_XGETBV])
|
||||
|
||||
AX_GCC_X86_CPUID(0x00000001)
|
||||
ecx=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 3`
|
||||
edx=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 4`
|
||||
|
||||
AC_CACHE_CHECK([whether mmx is supported], [ax_cv_have_mmx_ext],
|
||||
[
|
||||
ax_cv_have_mmx_ext=no
|
||||
if test "$((0x$edx>>23&0x01))" = 1; then
|
||||
ax_cv_have_mmx_ext=yes
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK([whether sse is supported], [ax_cv_have_sse_ext],
|
||||
[
|
||||
ax_cv_have_sse_ext=no
|
||||
if test "$((0x$edx>>25&0x01))" = 1; then
|
||||
ax_cv_have_sse_ext=yes
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK([whether sse2 is supported], [ax_cv_have_sse2_ext],
|
||||
[
|
||||
ax_cv_have_sse2_ext=no
|
||||
if test "$((0x$edx>>26&0x01))" = 1; then
|
||||
ax_cv_have_sse2_ext=yes
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK([whether sse3 is supported], [ax_cv_have_sse3_ext],
|
||||
[
|
||||
ax_cv_have_sse3_ext=no
|
||||
if test "$((0x$ecx&0x01))" = 1; then
|
||||
ax_cv_have_sse3_ext=yes
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK([whether ssse3 is supported], [ax_cv_have_ssse3_ext],
|
||||
[
|
||||
ax_cv_have_ssse3_ext=no
|
||||
if test "$((0x$ecx>>9&0x01))" = 1; then
|
||||
ax_cv_have_ssse3_ext=yes
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK([whether sse4.1 is supported], [ax_cv_have_sse41_ext],
|
||||
[
|
||||
ax_cv_have_sse41_ext=no
|
||||
if test "$((0x$ecx>>19&0x01))" = 1; then
|
||||
ax_cv_have_sse41_ext=yes
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK([whether sse4.2 is supported], [ax_cv_have_sse42_ext],
|
||||
[
|
||||
ax_cv_have_sse42_ext=no
|
||||
if test "$((0x$ecx>>20&0x01))" = 1; then
|
||||
ax_cv_have_sse42_ext=yes
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK([whether avx is supported by processor], [ax_cv_have_avx_cpu_ext],
|
||||
[
|
||||
ax_cv_have_avx_cpu_ext=no
|
||||
if test "$((0x$ecx>>28&0x01))" = 1; then
|
||||
ax_cv_have_avx_cpu_ext=yes
|
||||
fi
|
||||
])
|
||||
|
||||
if test x"$ax_cv_have_avx_cpu_ext" = x"yes"; then
|
||||
AX_GCC_X86_AVX_XGETBV(0x00000000)
|
||||
|
||||
xgetbv_eax="0"
|
||||
if test x"$ax_cv_gcc_x86_avx_xgetbv_0x00000000" != x"unknown"; then
|
||||
xgetbv_eax=`echo $ax_cv_gcc_x86_avx_xgetbv_0x00000000 | cut -d ":" -f 1`
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([whether avx is supported by operating system], [ax_cv_have_avx_ext],
|
||||
[
|
||||
ax_cv_have_avx_ext=no
|
||||
|
||||
if test "$((0x$ecx>>27&0x01))" = 1; then
|
||||
if test "$((0x$xgetbv_eax&0x6))" = 6; then
|
||||
ax_cv_have_avx_ext=yes
|
||||
fi
|
||||
fi
|
||||
])
|
||||
if test x"$ax_cv_have_avx_ext" = x"no"; then
|
||||
AC_MSG_WARN([Your processor supports AVX, but your operating system doesn't])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ax_cv_have_mmx_ext" = yes; then
|
||||
AX_CHECK_COMPILE_FLAG(-mmmx, ax_cv_support_mmx_ext=yes, [])
|
||||
if test x"$ax_cv_support_mmx_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -mmmx"
|
||||
AC_DEFINE(HAVE_MMX,,[Support mmx instructions])
|
||||
else
|
||||
AC_MSG_WARN([Your processor supports mmx instructions but not your compiler, can you try another compiler?])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ax_cv_have_sse_ext" = yes; then
|
||||
AX_CHECK_COMPILE_FLAG(-msse, ax_cv_support_sse_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse"
|
||||
AC_DEFINE(HAVE_SSE,,[Support SSE (Streaming SIMD Extensions) instructions])
|
||||
else
|
||||
AC_MSG_WARN([Your processor supports sse instructions but not your compiler, can you try another compiler?])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ax_cv_have_sse2_ext" = yes; then
|
||||
AX_CHECK_COMPILE_FLAG(-msse2, ax_cv_support_sse2_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse2_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse2"
|
||||
AC_DEFINE(HAVE_SSE2,,[Support SSE2 (Streaming SIMD Extensions 2) instructions])
|
||||
else
|
||||
AC_MSG_WARN([Your processor supports sse2 instructions but not your compiler, can you try another compiler?])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ax_cv_have_sse3_ext" = yes; then
|
||||
AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse3_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse3"
|
||||
AC_DEFINE(HAVE_SSE3,,[Support SSE3 (Streaming SIMD Extensions 3) instructions])
|
||||
else
|
||||
AC_MSG_WARN([Your processor supports sse3 instructions but not your compiler, can you try another compiler?])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ax_cv_have_ssse3_ext" = yes; then
|
||||
AX_CHECK_COMPILE_FLAG(-mssse3, ax_cv_support_ssse3_ext=yes, [])
|
||||
if test x"$ax_cv_support_ssse3_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -mssse3"
|
||||
AC_DEFINE(HAVE_SSSE3,,[Support SSSE3 (Supplemental Streaming SIMD Extensions 3) instructions])
|
||||
else
|
||||
AC_MSG_WARN([Your processor supports ssse3 instructions but not your compiler, can you try another compiler?])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ax_cv_have_sse41_ext" = yes; then
|
||||
AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse41_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse4.1"
|
||||
AC_DEFINE(HAVE_SSE4_1,,[Support SSSE4.1 (Streaming SIMD Extensions 4.1) instructions])
|
||||
else
|
||||
AC_MSG_WARN([Your processor supports sse4.1 instructions but not your compiler, can you try another compiler?])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ax_cv_have_sse42_ext" = yes; then
|
||||
AX_CHECK_COMPILE_FLAG(-msse4.2, ax_cv_support_sse42_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse42_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse4.2"
|
||||
AC_DEFINE(HAVE_SSE4_2,,[Support SSSE4.2 (Streaming SIMD Extensions 4.2) instructions])
|
||||
else
|
||||
AC_MSG_WARN([Your processor supports sse4.2 instructions but not your compiler, can you try another compiler?])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$ax_cv_have_avx_ext" = yes; then
|
||||
AX_CHECK_COMPILE_FLAG(-mavx, ax_cv_support_avx_ext=yes, [])
|
||||
if test x"$ax_cv_support_avx_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -mavx"
|
||||
AC_DEFINE(HAVE_AVX,,[Support AVX (Advanced Vector Extensions) instructions])
|
||||
else
|
||||
AC_MSG_WARN([Your processor supports avx instructions but not your compiler, can you try another compiler?])
|
||||
fi
|
||||
fi
|
||||
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_SUBST(SIMD_FLAGS)
|
||||
])
|
||||
@@ -1,79 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_avx_xgetbv.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_GCC_X86_AVX_XGETBV
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# On later x86 processors with AVX SIMD support, with gcc or a compiler
|
||||
# that has a compatible syntax for inline assembly instructions, run a
|
||||
# small program that executes the xgetbv instruction with input OP. This
|
||||
# can be used to detect if the OS supports AVX instruction usage.
|
||||
#
|
||||
# On output, the values of the eax and edx registers are stored as
|
||||
# hexadecimal strings as "eax:edx" in the cache variable
|
||||
# ax_cv_gcc_x86_avx_xgetbv.
|
||||
#
|
||||
# If the xgetbv instruction fails (because you are running a
|
||||
# cross-compiler, or because you are not using gcc, or because you are on
|
||||
# a processor that doesn't have this instruction),
|
||||
# ax_cv_gcc_x86_avx_xgetbv_OP is set to the string "unknown".
|
||||
#
|
||||
# This macro mainly exists to be used in AX_EXT.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 1
|
||||
|
||||
AC_DEFUN([AX_GCC_X86_AVX_XGETBV],
|
||||
[AC_REQUIRE([AC_PROG_CC])
|
||||
AC_LANG_PUSH([C])
|
||||
AC_CACHE_CHECK(for x86-AVX xgetbv $1 output, ax_cv_gcc_x86_avx_xgetbv_$1,
|
||||
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
|
||||
int op = $1, eax, edx;
|
||||
FILE *f;
|
||||
/* Opcodes for xgetbv */
|
||||
__asm__(".byte 0x0f, 0x01, 0xd0"
|
||||
: "=a" (eax), "=d" (edx)
|
||||
: "c" (op));
|
||||
f = fopen("conftest_xgetbv", "w"); if (!f) return 1;
|
||||
fprintf(f, "%x:%x\n", eax, edx);
|
||||
fclose(f);
|
||||
return 0;
|
||||
])],
|
||||
[ax_cv_gcc_x86_avx_xgetbv_$1=`cat conftest_xgetbv`; rm -f conftest_xgetbv],
|
||||
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown; rm -f conftest_xgetbv],
|
||||
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown])])
|
||||
AC_LANG_POP([C])
|
||||
])
|
||||
@@ -1,79 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_GCC_X86_CPUID(OP)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# On Pentium and later x86 processors, with gcc or a compiler that has a
|
||||
# compatible syntax for inline assembly instructions, run a small program
|
||||
# that executes the cpuid instruction with input OP. This can be used to
|
||||
# detect the CPU type.
|
||||
#
|
||||
# On output, the values of the eax, ebx, ecx, and edx registers are stored
|
||||
# as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable
|
||||
# ax_cv_gcc_x86_cpuid_OP.
|
||||
#
|
||||
# If the cpuid instruction fails (because you are running a
|
||||
# cross-compiler, or because you are not using gcc, or because you are on
|
||||
# a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP
|
||||
# is set to the string "unknown".
|
||||
#
|
||||
# This macro mainly exists to be used in AX_GCC_ARCHFLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
# Copyright (c) 2008 Matteo Frigo
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 7
|
||||
|
||||
AC_DEFUN([AX_GCC_X86_CPUID],
|
||||
[AC_REQUIRE([AC_PROG_CC])
|
||||
AC_LANG_PUSH([C])
|
||||
AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1,
|
||||
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
|
||||
int op = $1, eax, ebx, ecx, edx;
|
||||
FILE *f;
|
||||
__asm__("cpuid"
|
||||
: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
|
||||
: "a" (op));
|
||||
f = fopen("conftest_cpuid", "w"); if (!f) return 1;
|
||||
fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx);
|
||||
fclose(f);
|
||||
return 0;
|
||||
])],
|
||||
[ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid],
|
||||
[ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid],
|
||||
[ax_cv_gcc_x86_cpuid_$1=unknown])])
|
||||
AC_LANG_POP([C])
|
||||
])
|
||||
71
config/ax_sse.m4
Normal file
71
config/ax_sse.m4
Normal file
@@ -0,0 +1,71 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_ext.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_SSE
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Find SIMD extensions supported by compiler. The -m"simdextensionname" is
|
||||
# added to SIMD_FLAGS if compiler supports it. For example, if "sse2" is
|
||||
# available, then "-msse2" is added to SIMD_FLAGS.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(SIMD_FLAGS)
|
||||
#
|
||||
# And defines:
|
||||
#
|
||||
# HAVE_SSE3 / HAVE_SSE4.1
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2007 Christophe Tournayre <turn3r@users.sourceforge.net>
|
||||
# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
#
|
||||
# NOTE: The functionality that requests the cpuid has been stripped because
|
||||
# this project detects the CPU capabilities during runtime. However, we
|
||||
# still need to check if the compiler supports the requested SIMD flag
|
||||
|
||||
#serial 12
|
||||
|
||||
AC_DEFUN([AX_SSE],
|
||||
[
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
|
||||
AM_CONDITIONAL(HAVE_SSE3, false)
|
||||
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
||||
|
||||
case $host_cpu in
|
||||
i[[3456]]86*|x86_64*|amd64*)
|
||||
AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse3_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse3"
|
||||
AC_DEFINE(HAVE_SSE3,,
|
||||
[Support SSE3 (Streaming SIMD Extensions 3) instructions])
|
||||
AM_CONDITIONAL(HAVE_SSE3, true)
|
||||
else
|
||||
AC_MSG_WARN([Your compiler does not support SSE3 instructions])
|
||||
fi
|
||||
|
||||
AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse41_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse4.1"
|
||||
AC_DEFINE(HAVE_SSE4_1,,
|
||||
[Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions])
|
||||
AM_CONDITIONAL(HAVE_SSE4_1, true)
|
||||
else
|
||||
AC_MSG_WARN([Your compiler does not support SSE4.1])
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_SUBST(SIMD_FLAGS)
|
||||
])
|
||||
68
configure.ac
68
configure.ac
@@ -18,12 +18,15 @@ dnl You should have received a copy of the GNU General Public License
|
||||
dnl along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
dnl
|
||||
|
||||
AC_INIT(openbts,P2.8TRUNK)
|
||||
AC_INIT([osmo-trx],
|
||||
m4_esyscmd([./git-version-gen .tarball-veresion]),
|
||||
[openbsc@lists.osmocom.org])
|
||||
AC_PREREQ(2.57)
|
||||
AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([config])
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
@@ -34,13 +37,25 @@ AM_INIT_AUTOMAKE([subdir-objects])
|
||||
dnl Linux kernel KBuild style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl include release helper
|
||||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
AM_PROG_AS
|
||||
AC_PROG_CXX
|
||||
AX_CXX_COMPILE_STDCXX_11
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_INSTALL
|
||||
AC_PATH_PROG([RM_PROG], [rm])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
AC_MSG_WARN([You need to install pkg-config])
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AC_LIBTOOL_WIN32_DLL
|
||||
AC_ENABLE_SHARED dnl do build shared libraries
|
||||
AC_DISABLE_STATIC dnl don't build static libraries
|
||||
@@ -97,9 +112,12 @@ AS_IF([test "x$with_usrp1" = "xyes"], [
|
||||
])
|
||||
|
||||
AS_IF([test "x$with_usrp1" != "xyes"],[
|
||||
PKG_CHECK_MODULES(UHD, uhd >= 003.009,
|
||||
[AC_DEFINE(USE_UHD_3_9, 1, UHD version 3.9.0 or higher)],
|
||||
[PKG_CHECK_MODULES(UHD, uhd >= 003.005.004)]
|
||||
PKG_CHECK_MODULES(UHD, uhd >= 003.011,
|
||||
[AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)],
|
||||
[PKG_CHECK_MODULES(UHD, uhd >= 003.009,
|
||||
[AC_DEFINE(USE_UHD_3_9, 1, UHD version 3.9.0 or higher)],
|
||||
[PKG_CHECK_MODULES(UHD, uhd >= 003.005)]
|
||||
)]
|
||||
)
|
||||
AC_DEFINE(USE_UHD, 1, All UHD versions)
|
||||
])
|
||||
@@ -110,14 +128,48 @@ AS_IF([test "x$with_singledb" = "xyes"], [
|
||||
|
||||
# Find and define supported SIMD extensions
|
||||
AS_IF([test "x$with_sse" != "xno"], [
|
||||
AX_EXT
|
||||
AX_SSE
|
||||
], [
|
||||
AM_CONDITIONAL(HAVE_SSE3, false)
|
||||
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
||||
])
|
||||
|
||||
dnl Check if the compiler supports specified GCC's built-in function
|
||||
AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
|
||||
AC_CACHE_CHECK(
|
||||
[whether ${CC} has $1 built-in],
|
||||
[osmo_cv_cc_has_builtin], [
|
||||
AC_LINK_IFELSE([
|
||||
AC_LANG_PROGRAM([], [
|
||||
__builtin_cpu_supports("sse");
|
||||
])
|
||||
],
|
||||
[AS_VAR_SET([osmo_cv_cc_has_builtin], [yes])],
|
||||
[AS_VAR_SET([osmo_cv_cc_has_builtin], [no])])
|
||||
]
|
||||
)
|
||||
|
||||
AS_IF([test yes = AS_VAR_GET([osmo_cv_cc_has_builtin])], [
|
||||
AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
|
||||
[Define to 1 if compiler has the '$1' built-in function])
|
||||
], [
|
||||
AC_MSG_WARN($2)
|
||||
])
|
||||
])
|
||||
|
||||
dnl Check if the compiler supports runtime SIMD detection
|
||||
CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
|
||||
[Runtime SIMD detection will be disabled])
|
||||
|
||||
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
|
||||
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
||||
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
|
||||
|
||||
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
|
||||
PKG_CHECK_MODULES(FFTWF, fftw3f)
|
||||
|
||||
AC_CHECK_HEADER([boost/config.hpp],[],
|
||||
[AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])])
|
||||
|
||||
dnl Output files
|
||||
AC_CONFIG_FILES([\
|
||||
@@ -127,7 +179,9 @@ AC_CONFIG_FILES([\
|
||||
Transceiver52M/Makefile \
|
||||
Transceiver52M/arm/Makefile \
|
||||
Transceiver52M/x86/Makefile \
|
||||
sqlite3/Makefile \
|
||||
tests/Makefile \
|
||||
tests/CommonLibs/Makefile \
|
||||
tests/Transceiver52M/Makefile \
|
||||
])
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
63
contrib/jenkins.sh
Executable file
63
contrib/jenkins.sh
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
substr() { [ -z "${2##*$1*}" ]; }
|
||||
|
||||
#apt-get install qemu qemu-user-static qemu-system-arm debootstrap fakeroot proot
|
||||
mychroot_nocwd() {
|
||||
# LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container
|
||||
# PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH.
|
||||
# PROOT_NO_SECCOMP is requried due to proot bug #106
|
||||
LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@"
|
||||
}
|
||||
|
||||
mychroot() {
|
||||
mychroot_nocwd -w / "$@"
|
||||
}
|
||||
|
||||
if [ -z "${INSIDE_CHROOT}" ]; then
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
# Only use ARM chroot if host is not ARM and the target is ARM:
|
||||
if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
|
||||
|
||||
OSMOTRX_DIR="$PWD" # we assume we are called as contrib/jenkins.sh
|
||||
ROOTFS_PREFIX="${ROOTFS_PREFIX:-/opt}"
|
||||
ROOTFS="${ROOTFS_PREFIX}/qemu-img"
|
||||
mkdir -p "${ROOTFS_PREFIX}"
|
||||
|
||||
# Prepare chroot:
|
||||
if [ ! -d "$ROOTFS" ]; then
|
||||
mkdir -p "$ROOTFS"
|
||||
if [ "x${USE_DEBOOTSTRAP}" = "x1" ]; then
|
||||
fakeroot qemu-debootstrap --foreign --include="linux-image-armmp-lpae" --arch=armhf stretch "$ROOTFS" http://ftp.de.debian.org/debian/
|
||||
# Hack to avoid debootstrap trying to mount /proc, as it will fail with "no permissions" and anyway proot takes care of it:
|
||||
sed -i "s/setup_proc//g" "$ROOTFS/debootstrap/suite-script"
|
||||
mychroot /debootstrap/debootstrap --second-stage --verbose http://ftp.de.debian.org/debian/
|
||||
else
|
||||
YESTERDAY=$(python -c 'import datetime ; print((datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y%m%d"))')
|
||||
wget -nc -q "https://uk.images.linuxcontainers.org/images/debian/stretch/armhf/default/${YESTERDAY}_22:42/rootfs.tar.xz"
|
||||
tar -xf rootfs.tar.xz -C "$ROOTFS/" || true
|
||||
echo "nameserver 8.8.8.8" > "$ROOTFS/etc/resolv.conf"
|
||||
fi
|
||||
mychroot -b /dev apt-get update
|
||||
mychroot apt-get -y install build-essential dh-autoreconf pkg-config libuhd-dev libusb-1.0-0-dev libusb-dev git
|
||||
fi
|
||||
# Run jenkins.sh inside the chroot:
|
||||
INSIDE_CHROOT=1 mychroot_nocwd -w /osmo-trx -b "$OSMOTRX_DIR:/osmo-trx" -b "$(which osmo-clean-workspace.sh):/usr/bin/osmo-clean-workspace.sh" ./contrib/jenkins.sh
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
### BUILD osmo-trx
|
||||
|
||||
autoreconf --install --force
|
||||
./configure $INSTR
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if [ -z "x${INSIDE_CHROOT}" ]; then
|
||||
osmo-clean-workspace.sh
|
||||
fi
|
||||
246
debian/changelog
vendored
Normal file
246
debian/changelog
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
osmo-trx (0.3.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* jenkins: use osmo-clean-workspace.sh before and after build
|
||||
|
||||
[ Harald Welte ]
|
||||
* SocketsTest: Fix printing of non-nul-terminated string
|
||||
|
||||
[ Piotr Krysik ]
|
||||
* UHDDevice.cpp: add USRP B205mini support
|
||||
|
||||
[ Max ]
|
||||
* Mark release target as virtual
|
||||
* Remove outdated references to OpenBTS
|
||||
* Remove unused headers
|
||||
* Update installation instructions
|
||||
* Update legal disclaimer
|
||||
* Update license notes
|
||||
* tests: null-terminate buffer
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: Remove trailing whitespace
|
||||
* Logger: Stop using Log.Alarms.Max from config
|
||||
* Logger: Stop using Log.File and Log.Level from config
|
||||
* Drop use of ConfigurationTable gConfig
|
||||
* Remove Configuration module and libsqlite dependency
|
||||
* cosmetic: AUTHORS: fix trailing whitespace
|
||||
* Set up GNU Autotest infrastructure
|
||||
* tests: InterThread: adapt to have reproducible output and enable autotest
|
||||
* tests: Timeval: adapt to have reproducible output and enable autotest
|
||||
* tests: Log: adapt to have reproducible output and enable autotest
|
||||
* Sockets.cpp: Fix initialization of UDD socket
|
||||
* tests: Sockets: adapt to have reproducible output and enable autotest
|
||||
* utils/convolvtest: Remove uneeded libosmocore dependency
|
||||
* Move ARCH_LA to Makefile.common
|
||||
* tests: Migrate convtest util to autotest infrastructure
|
||||
* arm/convert.c: Fix compilation error
|
||||
* arm/convert.c: Add missing convert_init implementation
|
||||
* .gitignore: Add missing test related files
|
||||
* Remove UDDSocket class
|
||||
* tests: SocketTests: Pick OS-assigned instead of setting one manually
|
||||
* tests: SocketsTest: Avoid hang forever if test fails
|
||||
* tests: SocketsTest: Fail test on write fail
|
||||
* tests: TimevalTest: refactor and avoid double comparison
|
||||
* contrib/jenkins.sh: Use qemu+proot+debootstrap to run tests with ARM instruction set
|
||||
* tests: convolve: Disable due to difference in output in different archs
|
||||
* Remove unneeded libdl dependency
|
||||
* Fix whitespace
|
||||
* Add support to set Rx/TxAntenna
|
||||
* UHDDevice: Initialize async_event_thrd in constructor
|
||||
* Logger: Drop unused gLogEarly
|
||||
* Logger: Remove unused logging macros
|
||||
* Logger: get rid of alarm APIs
|
||||
* Logger: Drop syslog support
|
||||
* Logger: Drop support to log into file
|
||||
* Logger: Remove unused includes
|
||||
* Logger: Remove gLogToConsole flag
|
||||
* configure.ac: Check for pkg-config
|
||||
|
||||
[ Alexander Huemer ]
|
||||
* Unbreak `./configure --with-usrp1` build
|
||||
* Fix USRP1 build with support for setting Rx/TxAntenna
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* jenkins.sh: fix the download url if the qemu image wasn't setup
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Mon, 05 Mar 2018 16:49:42 +0100
|
||||
|
||||
osmo-trx (0.2.0) unstable; urgency=medium
|
||||
|
||||
[ Alexander Chemeris ]
|
||||
* EDGE: Add support for UmTRX.
|
||||
* Common: Get rid of a compilation warning.
|
||||
* Common: Make sure gLogEarly() log to the same facilities as the normal log.
|
||||
* transceiver: Properly handle MAXDLY.
|
||||
* transceiver: Add an option to generate random Access Bursts.
|
||||
* osmo-trx: Output Rx SPS as a part of configuration output.
|
||||
* transceiver: Do not pass transceiver state struct to function where it's not used.
|
||||
* makefile: Fix build from an external path.
|
||||
* radioDevice: GSMRATE macro must have parentheses around its definition.
|
||||
* uhd: Fix comment.
|
||||
* radioInterface: Initialize power scale with a meaningful default.
|
||||
* transceiver: Log channel number in DEBUG output of demoded bursts.
|
||||
* transceiver: Add an option to emulate a RACH delay in random filler mode.
|
||||
* UHD: Initial LimeSDR support.
|
||||
* CommonLibs: Remove unused files.
|
||||
* sigProcLib: Typo sybols -> symbols
|
||||
* radioBuffer: Remove extra ; at the end of inline function definitions.
|
||||
* sigProcLib: Fix documentation, sync argument names in .cpp and .h files.
|
||||
* sigProcLib: make energyDetect() simpler by returning actual energy.
|
||||
* sigProcLib: Rename demodulateBurst() to demodGmskBurst() for clarity.
|
||||
* sigProcLib: Slice SoftVector instead of signalVector for GMSK demod.
|
||||
* Call vectorSlicer() right before packing bits for transmission to osmo-bts.
|
||||
* CommonLibs: Print soft bits with less confidence to console when printing a soft vector.
|
||||
* BitVector: Remove convolutional codec - we don't use it in osmo-trx.
|
||||
* BitVector: Convert SoftVector from 0..1 to -1..+1 soft bits.
|
||||
* signalVector: Implement segment().
|
||||
* vector: Introduce segmentMove() method to move data inside of a vector.
|
||||
* vector: Introduce shrink() function to shrink vector size without loosing data.
|
||||
* Move CorrType type from Transceiver to sigProcLib.
|
||||
* sigProcLib: rename signalError type to SignalError.
|
||||
* Move Transceiver::detectBurst() to sigProcLib to make it reusable.
|
||||
* Move BURST_THRESH from Transceiver.cpp to sigProcLib.h to make it reusable.
|
||||
* sigProcLib: Add operator<< to print CorrType to a string.
|
||||
* sigProcLib.h: Fix whitespaces. No non-whitespace changes.
|
||||
* Move Transceiver::demodulate() to sigProcLib to make it reusable.
|
||||
* sigProcLib: constify signalVector arguments for detectBurst() functions.
|
||||
* sigProcLib: Constify demodulation functions burst argument.
|
||||
* sigProcLib: Fix number of tail bits in random Normal Bursts and zero Stealing Bits.
|
||||
* Configuration: Variables allocated with 'new' must be freed with 'delete'.
|
||||
* BitVector: Remove Generator class.
|
||||
* PRBS: a Pseudo-random binary sequence (PRBS) generator class.
|
||||
|
||||
[ Tom Tsou ]
|
||||
* EDGE: Fix USRP B210 device support
|
||||
* uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts
|
||||
* EDGE: Fix demodulation slicer input
|
||||
* common: Restrict UDP binding to localhost only
|
||||
* common: Add mandatory length field to UDP receive calls
|
||||
* uhd: Update default E3XX settings
|
||||
* uhd: Set default Tx sampling to 4 sps
|
||||
* uhd: Make device offset check a private method
|
||||
* uhd: Set minimum UHD version requirement for E3XX
|
||||
* sigproc: Expand RACH, TSC, and EDGE correlation windows
|
||||
* transceiver: Do not report error on SETTSC when radio is on
|
||||
* transceiver: Add Rx samples-per-symbol option
|
||||
* radioInterface: Convert diversity argument to general type
|
||||
* iface: Add inner ring-buffer implementation
|
||||
* mcbts: Add multi-ARFCN channelizing filters
|
||||
* mcbts: Add multi-ARFCN radio support
|
||||
* sigproc: Adjust burst detection threshold criteria
|
||||
* egprs: Enable 8-PSK length vectors on the Tx interface
|
||||
* egprs: Enable 8-PSK burst detection when EDGE is enabled
|
||||
* transceiver: Remove HANDOVER warnings
|
||||
* mcbts: Allow out of order channel setup
|
||||
* radioInterface: Fix multi-channel buffer index bug
|
||||
* uhd: Add command line option for GPS reference
|
||||
* transceiver: Fix mixed GSMK / 8-PSK transmission
|
||||
* transceiver: Fix 4 SPS receive TOA value
|
||||
* sigproc: Fix missing 8-PSK tail symbols
|
||||
* uhd: Update USRP2/N200/N210 for 4 SPS Rx
|
||||
* sigproc: Match differential GMSK start/end bits to tail bits
|
||||
* uhd: Add missing B200 sample timing for 4 SPS receive
|
||||
* transceiver: Fix command build warning
|
||||
* uhd: Set minimum supported version to 3.9.0
|
||||
* uhd: Add X300 sample timing for 4 SPS
|
||||
* Revert "uhd: Set minimum supported version to 3.9.0"
|
||||
* uhd: Add support for UHD-3.11 logging control
|
||||
* uhd: Increase MC-BTS FPGA clock rate to 51.2 MHz
|
||||
* Resampler: Fix initialization return checking
|
||||
* sigProcLib: Remove unreachable code and no-effect checks
|
||||
* sigProcLib: Check return status on downsampling
|
||||
* sigProcLib: Fix negative value check on unsigned value
|
||||
* Resampler: Fix non-array delete for filter taps
|
||||
* Transceiver: Remove unsigned negative compares
|
||||
* Configuration: Fix const and signedness compile warnings
|
||||
* config: Remove OpenBTS style sqlite configuration
|
||||
* radioInterface: Remove UmTRX 'diversity' option
|
||||
* build: Require and check for gcc C++11 support
|
||||
* uhd: Use map container for for device parameter access
|
||||
* sigProcLib: Remove unused functions from public interface
|
||||
* uhd: Add non-UmTRX channel swap support
|
||||
* uhd: Fix Tx-RX timing offset setting
|
||||
* uhd: Fix USRP2/N200/N210 device detection
|
||||
* transceiver: Fix POWEROFF crash on USRP2/N200/X300 devices
|
||||
* sigProcLib: Fix complex/real vector flag in Laurent modulator
|
||||
* sigProcLib: Remove heap based signal vector allocations
|
||||
* common: Declare explicit Vector move constructor
|
||||
* sigProcLib: Remove trigonometric tables
|
||||
* sigProcLib: Use explicit NaN check in sinc table generation
|
||||
* sigProcLib: Replace dynamically allocated resampling buffers
|
||||
* sigProcLib: Specify standard namespace for isnan()
|
||||
* uhd: Always specify samples-per-symbol for device lookup
|
||||
* LimeSDR: set approximate tx offset value to make GSM work
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* add basic .gitignore
|
||||
* configure.ac: check for boost/config.hpp header
|
||||
* The INSTALL file is being overwritten by autoreconf, but it is committed as empty file. As a result, the INSTALL file always shows as modified. Instead, remove INSTALL from git and ignore it.
|
||||
* add contrib/jenkins.sh, for gerrit build bot
|
||||
|
||||
[ pierre.baudry ]
|
||||
* transceiver: Fix mismatched allocations and deallocations
|
||||
|
||||
[ Holger Hans Peter Freyther ]
|
||||
* debian: Require fftw3 header files for osmo-trx
|
||||
|
||||
[ Max ]
|
||||
* Add gerrit settings
|
||||
* Integrate Debian packaging changes
|
||||
* Remove embedded sqlite3
|
||||
* Fix building against sqlite3
|
||||
* Add autoconf-archive to dependencies
|
||||
* debian: remove obsolete dependency
|
||||
* deb: remove unused dependency
|
||||
* Remove redundant explicit dependency
|
||||
* Use release helper from libosmocore
|
||||
|
||||
[ Ruben Undheim ]
|
||||
* Do not embed sqlite3 when building
|
||||
|
||||
[ Philipp Maier ]
|
||||
* buildenv: Turn off native architecture builds
|
||||
* cosmetic: Make parameter lists uniform
|
||||
* Add test program to verify convolution implementation
|
||||
* ssedetect: Add runtime CPU detection
|
||||
* cosmetic: remove code duplication
|
||||
* buildenv: Make build CPU invariant
|
||||
* buildenv: Split up SSE3 and SSE4.1 code
|
||||
* cosmetic: Add info about SSE support
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* buildenv: correct the ax_sse macro description
|
||||
* buildenv: actually strip unused cpuid functionality
|
||||
* buildenv: fix build on systems without SIMD support
|
||||
* buildenv: cosmetic changes
|
||||
* buildenv: check for __builtin_cpu_supports call support
|
||||
* ssedetect: call __builtin_cpu_supports() only if supported
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: transciever: Remove trailing whitespaces
|
||||
* transceiver: Avoid sending clock indications when trx is not powered on
|
||||
* Add -j option to bind to specific address
|
||||
|
||||
[ ignasj ]
|
||||
* LimeSDR: Change device detection to work with USB and PCIe versions
|
||||
* LimeSDR: change tx window type to TX_WINDOW_FIXED
|
||||
* LimeSDR: Fix sample value range
|
||||
|
||||
[ Harald Welte ]
|
||||
* Add '-t' command line option to enable SCHED_RR
|
||||
* Import git-version-gen and update AC_INIT()
|
||||
|
||||
-- Harald Welte <laforge@gnumonks.org> Sat, 28 Oct 2017 17:52:32 +0200
|
||||
|
||||
osmo-trx (0.1.9) trusty; urgency=medium
|
||||
|
||||
* Ask Ivan, really
|
||||
|
||||
-- Kirill Zakharenko <earwin@gmail.com> Thu, 16 Jul 2015 12:13:46 +0000
|
||||
|
||||
osmo-trx (0.1.8) precise; urgency=low
|
||||
|
||||
* Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP
|
||||
|
||||
-- Ivan Klyuchnikov <Ivan.Kluchnikov@fairwaves.ru> Sun, 9 Mar 2014 14:10:10 +0400
|
||||
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
9
|
||||
44
debian/control
vendored
Normal file
44
debian/control
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
Source: osmo-trx
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Ivan Klyuchnikov <ivan.kluchnikov@fairwaves.ru>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
autotools-dev,
|
||||
autoconf-archive,
|
||||
pkg-config,
|
||||
dh-autoreconf,
|
||||
libuhd-dev,
|
||||
libusb-1.0-0-dev,
|
||||
libboost-all-dev,
|
||||
libfftw3-dev
|
||||
Standards-Version: 3.9.6
|
||||
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
|
||||
Vcs-Git: git://git.osmocom.org/osmo-trx
|
||||
Homepage: https://projects.osmocom.org/projects/osmotrx
|
||||
|
||||
Package: osmo-trx
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: SDR transceiver that implements Layer 1 of a GSM BTS
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
.
|
||||
TS 05.01 "Physical layer on the radio path"
|
||||
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
|
||||
TS 05.04 "Modulation"
|
||||
TS 05.10 "Radio subsystem synchronization"
|
||||
.
|
||||
In this context, BTS is "Base transceiver station". It's the stations that
|
||||
connect mobile phones to the mobile network.
|
||||
.
|
||||
3GPP is the "3rd Generation Partnership Project" which is the collaboration
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
Package: osmo-trx-dbg
|
||||
Architecture: any
|
||||
Section: debug
|
||||
Priority: extra
|
||||
Depends: osmo-trx (= ${binary:Version}), ${misc:Depends}
|
||||
Description: Debug symbols for the osmo-trx
|
||||
Make debugging possible
|
||||
147
debian/copyright
vendored
Normal file
147
debian/copyright
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: OsmoTRX
|
||||
Source: http://cgit.osmocom.org/osmo-trx/
|
||||
Files-Excluded: Transceiver52M/std_inband.rbf
|
||||
|
||||
Files: *
|
||||
Copyright: 2008-2013 Free Software Foundation
|
||||
2010 Kestrel Signal Processing, Inc.
|
||||
2010-2012 Range Networks, Inc.
|
||||
License: AGPL-3+
|
||||
|
||||
Files: Transceiver52M/arm/*
|
||||
Transceiver52M/x86/*
|
||||
Transceiver52M/common/*
|
||||
Transceiver52M/Resampler.cpp
|
||||
Transceiver52M/Resampler.h
|
||||
Transceiver52M/osmo-trx.cpp
|
||||
Transceiver52M/radioInterfaceDiversity.cpp
|
||||
Copyright: 2012-2013 Thomas Tsou <tom@tsou.cc>
|
||||
License: LGPL-2.1+
|
||||
|
||||
Files: config/ax_check_compile_flag.m4
|
||||
Copyright: 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: config/ax_gcc_x86_cpuid.m4
|
||||
Copyright: 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
2008 Matteo Frigo
|
||||
License: GPL-3+
|
||||
|
||||
Files: config/ax_ext.m4
|
||||
Copyright: 2007 Christophe Tournayre <turn3r@users.sourceforge.net>
|
||||
2013 Michael Petch <mpetch@capp-sysware.com>
|
||||
License: license_for_ax_ext_m4
|
||||
|
||||
Files: config/ax_gcc_x86_avx_xgetbv.m4
|
||||
Copyright: 2013 Michael Petch <mpetch@capp-sysware.com>
|
||||
License: GPL-3+
|
||||
|
||||
Files: CommonLibs/Makefile.am
|
||||
GSM/Makefile.am
|
||||
Transceiver52M/Makefile.am
|
||||
Transceiver52M/Transceiver.h
|
||||
Transceiver52M/Transceiver.cpp
|
||||
Copyright: 2008-2010 Free Software Foundation
|
||||
2010-2012 Range Networks, Inc.
|
||||
License: GPL-3+
|
||||
|
||||
Files: autogen.sh
|
||||
Copyright: 2005-2009 United States Government as represented by
|
||||
the U.S. Army Research Laboratory.
|
||||
License: BSD-3-clause
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2015 Ruben Undheim <ruben.undheim@gmail.com>
|
||||
License: GPL-3+
|
||||
|
||||
|
||||
License: AGPL-3+
|
||||
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/>.
|
||||
|
||||
|
||||
License: GPL-3+
|
||||
This package 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 package 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/>
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
|
||||
|
||||
License: LGPL-2.1+
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
.
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this program. If not, see
|
||||
<http://www.gnu.org/licenses/>
|
||||
.
|
||||
On Debian systems, the complete text of the GNU Lesser General
|
||||
Public License version 2.1 can be found in
|
||||
"/usr/share/common-licenses/LGPL-2.1".
|
||||
|
||||
|
||||
License: license_for_ax_ext_m4
|
||||
Copying and distribution of this file, with or without modification, are
|
||||
permitted in any medium without royalty provided the copyright notice
|
||||
and this notice are preserved. This file is offered as-is, without any
|
||||
warranty.
|
||||
|
||||
|
||||
License: BSD-3-clause
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
.
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
.
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
.
|
||||
3. The name of the author may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
1
debian/osmo-trx.install
vendored
Normal file
1
debian/osmo-trx.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/usr/bin/osmo-trx
|
||||
12
debian/rules
vendored
Executable file
12
debian/rules
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
%:
|
||||
dh $@ --with autoreconf
|
||||
|
||||
override_dh_shlibdeps:
|
||||
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=osmo-trx-dbg
|
||||
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
||||
151
git-version-gen
Executable file
151
git-version-gen
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-01-28.01
|
||||
|
||||
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# It is probably wise to add these two files to .gitignore, so that you
|
||||
# don't accidentally commit either generated file.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .tarball-version will
|
||||
# exist in distribution tarballs.
|
||||
#
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
case $# in
|
||||
1) ;;
|
||||
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
|
||||
esac
|
||||
|
||||
tarball_version_file=$1
|
||||
nl='
|
||||
'
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || exit 1
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test -z "$v" \
|
||||
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif
|
||||
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
v[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
|
||||
else
|
||||
v=UNKNOWN
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed 's/^v//'`
|
||||
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git status > /dev/null 2>&1
|
||||
|
||||
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d '\012'
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
||||
@@ -1,34 +0,0 @@
|
||||
#
|
||||
# Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -lpthread -ldl
|
||||
|
||||
noinst_LTLIBRARIES = libsqlite.la
|
||||
|
||||
libsqlite_la_SOURCES = \
|
||||
sqlite3.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
sqlite3.h \
|
||||
sqlite3ext.h
|
||||
|
||||
138243
sqlite3/sqlite3.c
138243
sqlite3/sqlite3.c
File diff suppressed because it is too large
Load Diff
7055
sqlite3/sqlite3.h
7055
sqlite3/sqlite3.h
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user