mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-03 21:53:18 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
774a06369a | ||
|
|
94ce835050 | ||
|
|
09befd7a06 | ||
|
|
1303376ad1 | ||
|
|
25021dfe5a | ||
|
|
e287598e6b | ||
|
|
98b1af896c | ||
|
|
14bb9c923d | ||
|
|
c7f36c282a | ||
|
|
f31e4bb089 | ||
|
|
1189019c30 |
65
.gitignore
vendored
65
.gitignore
vendored
@@ -1,65 +0,0 @@
|
|||||||
# build results
|
|
||||||
*.o
|
|
||||||
*.lo
|
|
||||||
*.la
|
|
||||||
Transceiver52M/osmo-trx-uhd
|
|
||||||
Transceiver52M/osmo-trx-usrp1
|
|
||||||
Transceiver52M/osmo-trx-lms
|
|
||||||
|
|
||||||
# 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
|
|
||||||
tests/Transceiver52M/LMSDeviceTest
|
|
||||||
|
|
||||||
# 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?
|
|
||||||
|
|
||||||
# manuals
|
|
||||||
doc/manuals/*.html
|
|
||||||
doc/manuals/*.svg
|
|
||||||
doc/manuals/*.pdf
|
|
||||||
doc/manuals/*__*.png
|
|
||||||
doc/manuals/*.check
|
|
||||||
doc/manuals/generated/
|
|
||||||
doc/manuals/osmomsc-usermanual.xml
|
|
||||||
doc/manuals/common
|
|
||||||
doc/manuals/build
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
[gerrit]
|
|
||||||
host=gerrit.osmocom.org
|
|
||||||
project=osmo-trx
|
|
||||||
83
AUTHORS
83
AUTHORS
@@ -1,18 +1,18 @@
|
|||||||
#
|
#
|
||||||
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||||
#
|
#
|
||||||
# This file is part of GNU Radio
|
# This file is part of GNU Radio
|
||||||
#
|
#
|
||||||
# GNU Radio is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation; either version 3, or (at your option)
|
# the Free Software Foundation; either version 3, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# GNU Radio is distributed in the hope that it will be useful,
|
# GNU Radio is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# 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.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
@@ -23,17 +23,34 @@ David A. Burgess, dburgess@kestrelsp.com:
|
|||||||
CLI/CLI.h
|
CLI/CLI.h
|
||||||
CommonLibs/Assert.h
|
CommonLibs/Assert.h
|
||||||
CommonLibs/BitVector.cpp
|
CommonLibs/BitVector.cpp
|
||||||
|
CommonLibs/BitVectorTest.cpp
|
||||||
|
CommonLibs/Configuration.cpp
|
||||||
|
CommonLibs/Configuration.h
|
||||||
|
CommonLibs/ConfigurationTest.cpp
|
||||||
CommonLibs/Interthread.h
|
CommonLibs/Interthread.h
|
||||||
|
CommonLibs/InterthreadTest.cpp
|
||||||
CommonLibs/LinkedLists.cpp
|
CommonLibs/LinkedLists.cpp
|
||||||
CommonLibs/LinkedLists.h
|
CommonLibs/LinkedLists.h
|
||||||
CommonLibs/Regexp.h
|
CommonLibs/Regexp.h
|
||||||
|
CommonLibs/RegexpTest.cpp
|
||||||
CommonLibs/Sockets.cpp
|
CommonLibs/Sockets.cpp
|
||||||
CommonLibs/Sockets.h
|
CommonLibs/Sockets.h
|
||||||
|
CommonLibs/SocketsTest.cpp
|
||||||
CommonLibs/Threads.cpp
|
CommonLibs/Threads.cpp
|
||||||
CommonLibs/Threads.h
|
CommonLibs/Threads.h
|
||||||
CommonLibs/Timeval.cpp
|
CommonLibs/Timeval.cpp
|
||||||
CommonLibs/Timeval.h
|
CommonLibs/Timeval.h
|
||||||
|
CommonLibs/TimevalTest.cpp
|
||||||
CommonLibs/Vector.h
|
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.cpp
|
||||||
GSM/GSM610Tables.h
|
GSM/GSM610Tables.h
|
||||||
GSM/GSMCommon.cpp
|
GSM/GSMCommon.cpp
|
||||||
@@ -65,15 +82,29 @@ David A. Burgess, dburgess@kestrelsp.com:
|
|||||||
GSM/GSMTransfer.cpp
|
GSM/GSMTransfer.cpp
|
||||||
GSM/GSMTransfer.h
|
GSM/GSMTransfer.h
|
||||||
LICENSEBLOCK
|
LICENSEBLOCK
|
||||||
|
SIP/SIPEngine.h
|
||||||
|
SIP/SIPInterface.h
|
||||||
|
SMS/SMSMessages.cpp
|
||||||
|
SMS/SMSMessages.h
|
||||||
|
SMS/SMSTransfer.cpp
|
||||||
|
SMS/SMSTransfer.h
|
||||||
TRXManager/TRXManager.cpp
|
TRXManager/TRXManager.cpp
|
||||||
Transceiver/Complex.h
|
Transceiver/Complex.h
|
||||||
tests/CommonLibs/BitVectorTest.cpp
|
apps/OpenBTS900.cpp
|
||||||
tests/CommonLibs/InterthreadTest.cpp
|
apps/OpenBTS850.cpp
|
||||||
tests/CommonLibs/SocketsTest.cpp
|
apps/OpenBTS25c3.cpp
|
||||||
tests/CommonLibs/TimevalTest.cpp
|
tests/AGCHTest.cpp
|
||||||
tests/CommonLibs/VectorTest.cpp
|
tests/BeaconTest.cpp
|
||||||
|
tests/CallTest.cpp
|
||||||
|
tests/CallTest2.cpp
|
||||||
|
tests/LAPDmTest.cpp
|
||||||
|
tests/LoopbackTest.cpp
|
||||||
|
tests/RegistrationTest.cpp
|
||||||
|
tests/TRXSimulator.cpp
|
||||||
|
|
||||||
Harvind S. Samra, hssamra@kestrelsp.com:
|
Harvind S. Samra, hssamra@kestrelsp.com:
|
||||||
|
Control/PagerTest.cpp
|
||||||
|
Control/RadioResource.cpp
|
||||||
GSM/GSMConfig.h
|
GSM/GSMConfig.h
|
||||||
GSM/GSMTransfer.h
|
GSM/GSMTransfer.h
|
||||||
LICENSEBLOCK
|
LICENSEBLOCK
|
||||||
@@ -95,6 +126,13 @@ Harvind S. Samra, hssamra@kestrelsp.com:
|
|||||||
Transceiver/testRadio.cpp
|
Transceiver/testRadio.cpp
|
||||||
|
|
||||||
Raffi Sevlian, raffisev@gmail.com:
|
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/GSMCommon.h
|
||||||
GSM/GSMConfig.h
|
GSM/GSMConfig.h
|
||||||
GSM/GSML1FEC.h
|
GSM/GSML1FEC.h
|
||||||
@@ -119,9 +157,36 @@ Raffi Sevlian, raffisev@gmail.com:
|
|||||||
GSM/GSMSAPMux.h
|
GSM/GSMSAPMux.h
|
||||||
GSM/GSMTransfer.h
|
GSM/GSMTransfer.h
|
||||||
LICENSEBLOCK
|
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
|
TRXManager/TRXManager.h
|
||||||
|
|
||||||
Alon Levy, alonlevy1@gmail.com
|
Alon Levy, alonlevy1@gmail.com
|
||||||
RRLPMessages.cpp
|
RRLPMessages.cpp
|
||||||
RRLPMessages.h
|
RRLPMessages.h
|
||||||
RRLPTest.cpp
|
RRLPTest.cpp
|
||||||
|
|
||||||
|
|||||||
28
COPYING
28
COPYING
@@ -673,16 +673,16 @@ on the AGPLv3 text.
|
|||||||
=========================================================================
|
=========================================================================
|
||||||
|
|
||||||
|
|
||||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX
|
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS
|
||||||
|
|
||||||
|
|
||||||
Permissive Terms Supplementing the License
|
Permissive Terms Supplementing the License
|
||||||
|
|
||||||
1. Remote Interaction Through IP Networks.
|
1. Remote Interaction Through IP Networks.
|
||||||
|
|
||||||
OsmoTRX is an implementation of the GSM network cellular air interface,
|
OpenBTS includes an implementation of the GSM network cellular air interface,
|
||||||
as well as other interfaces to IP networks. The interaction of cellular
|
as well as other interfaces to IP networks. The interaction of cellular
|
||||||
handsets with the OsmoTRX software is considered "remote network interaction"
|
handsets with the OpenBTS software is considered "remote network interaction"
|
||||||
for the purposes of the Affero General Public License and cellular users are
|
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
|
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
|
||||||
Network Interaction; Use with the GNU General Public License").
|
Network Interaction; Use with the GNU General Public License").
|
||||||
@@ -694,6 +694,17 @@ interfaces other than the GSM air interface from the requirements of Section 13
|
|||||||
is an additional permission granted to you.
|
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
|
END OF ADDITIONAL TERMS
|
||||||
|
|
||||||
|
|
||||||
@@ -701,8 +712,13 @@ END OF ADDITIONAL TERMS
|
|||||||
How to comply with Section 13 of the AGPLv3 license.
|
How to comply with Section 13 of the AGPLv3 license.
|
||||||
|
|
||||||
The recommended method for compliance with Section 13 of the AGPLv3 license is
|
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 cellular
|
to deliver a text message to each handset that attaches to the OpenBTS cellular
|
||||||
network which uses OsmoTRX. At a minimum, that text message should include the string
|
network. At a minimum, that text message should include the string "OpenBTS
|
||||||
"OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This
|
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
|
||||||
message need not be delivered to handsets that are denied registration with the
|
message need not be delivered to handsets that are denied registration with the
|
||||||
network, since those handsets have been denied service.
|
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,7 +30,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -200,6 +199,49 @@ 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 BitVector::sum() const
|
||||||
{
|
{
|
||||||
unsigned sum = 0;
|
unsigned sum = 0;
|
||||||
@@ -245,12 +287,148 @@ 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)
|
SoftVector::SoftVector(const BitVector& source)
|
||||||
{
|
{
|
||||||
resize(source.size());
|
resize(source.size());
|
||||||
for (size_t i=0; i<size(); i++) {
|
for (size_t i=0; i<size(); i++) {
|
||||||
if (source.bit(i)) mStart[i]=1.0F;
|
if (source.bit(i)) mStart[i]=1.0F;
|
||||||
else mStart[i]=-1.0F;
|
else mStart[i]=0.0F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,20 +438,102 @@ BitVector SoftVector::sliced() const
|
|||||||
size_t sz = size();
|
size_t sz = size();
|
||||||
BitVector newSig(sz);
|
BitVector newSig(sz);
|
||||||
for (size_t i=0; i<sz; i++) {
|
for (size_t i=0; i<sz; i++) {
|
||||||
if (mStart[i]>0.0F) newSig[i]=1;
|
if (mStart[i]>0.5F) newSig[i]=1;
|
||||||
else newSig[i] = 0;
|
else newSig[i] = 0;
|
||||||
}
|
}
|
||||||
return newSig;
|
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
|
float SoftVector::getEnergy(float *plow) const
|
||||||
{
|
{
|
||||||
const SoftVector &vec = *this;
|
const SoftVector &vec = *this;
|
||||||
int len = vec.size();
|
int len = vec.size();
|
||||||
float avg = 0; float low = 1;
|
float avg = 0; float low = 1;
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
float energy = fabsf(vec[i]);
|
float bit = vec[i];
|
||||||
|
float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
|
||||||
if (energy < low) low = energy;
|
if (energy < low) low = energy;
|
||||||
avg += energy/len;
|
avg += energy/len;
|
||||||
}
|
}
|
||||||
@@ -285,12 +545,8 @@ float SoftVector::getEnergy(float *plow) const
|
|||||||
ostream& operator<<(ostream& os, const SoftVector& sv)
|
ostream& operator<<(ostream& os, const SoftVector& sv)
|
||||||
{
|
{
|
||||||
for (size_t i=0; i<sv.size(); i++) {
|
for (size_t i=0; i<sv.size(); i++) {
|
||||||
if (sv[i]<-0.5) os << "0";
|
if (sv[i]<0.25) os << "0";
|
||||||
else if (sv[i]<-0.25) os << "o";
|
else if (sv[i]>0.75) os << "1";
|
||||||
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 << "-";
|
else os << "-";
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
|
|||||||
@@ -30,6 +30,201 @@
|
|||||||
#include <stdint.h>
|
#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> {
|
class BitVector : public Vector<char> {
|
||||||
|
|
||||||
|
|
||||||
@@ -87,6 +282,16 @@ class BitVector : public Vector<char> {
|
|||||||
|
|
||||||
void zero() { fill(0); }
|
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. */
|
/** Invert 0<->1. */
|
||||||
void invert();
|
void invert();
|
||||||
@@ -222,20 +427,23 @@ class SoftVector: public Vector<float> {
|
|||||||
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
|
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
// How good is the SoftVector in the sense of the bits being solid?
|
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
|
||||||
// Result of 1 is perfect and 0 means all the bits were 0.0
|
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
|
||||||
// If plow is non-NULL, also return the lowest energy bit.
|
// If plow is non-NULL, also return the lowest energy bit.
|
||||||
float getEnergy(float *low=0) const;
|
float getEnergy(float *low=0) const;
|
||||||
|
|
||||||
/** Fill with "unknown" values. */
|
/** Fill with "unknown" values. */
|
||||||
void unknown() { fill(0.0F); }
|
void unknown() { fill(0.5F); }
|
||||||
|
|
||||||
/** Return a hard bit value from a given index by slicing. */
|
/** Return a hard bit value from a given index by slicing. */
|
||||||
bool bit(size_t index) const
|
bool bit(size_t index) const
|
||||||
{
|
{
|
||||||
const float *dp = mStart+index;
|
const float *dp = mStart+index;
|
||||||
assert(dp<mEnd);
|
assert(dp<mEnd);
|
||||||
return (*dp)>0.0F;
|
return (*dp)>0.5F;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Slice the whole signal into bits. */
|
/** Slice the whole signal into bits. */
|
||||||
|
|||||||
88
CommonLibs/BitVectorTest.cpp
Normal file
88
CommonLibs/BitVectorTest.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
1154
CommonLibs/Configuration.cpp
Normal file
1154
CommonLibs/Configuration.cpp
Normal file
File diff suppressed because it is too large
Load Diff
422
CommonLibs/Configuration.h
Normal file
422
CommonLibs/Configuration.h
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
149
CommonLibs/ConfigurationTest.cpp
Normal file
149
CommonLibs/ConfigurationTest.cpp
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* 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
Normal file
210
CommonLibs/F16.h
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
55
CommonLibs/F16Test.cpp
Normal file
55
CommonLibs/F16Test.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 Free Software Foundation, Inc.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "F16.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
|
||||||
|
F16 a = 2.5;
|
||||||
|
F16 b = 1.5;
|
||||||
|
F16 c = 2.5 * 1.5;
|
||||||
|
F16 d = c + a;
|
||||||
|
F16 e = 10;
|
||||||
|
cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << endl;
|
||||||
|
|
||||||
|
a *= 3;
|
||||||
|
b *= 0.3;
|
||||||
|
c *= e;
|
||||||
|
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
|
||||||
|
|
||||||
|
a /= 3;
|
||||||
|
b /= 0.3;
|
||||||
|
c = d * 0.05;
|
||||||
|
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
|
||||||
|
|
||||||
|
F16 f = a/d;
|
||||||
|
cout << f << ' ' << f+0.5 << endl;
|
||||||
|
}
|
||||||
@@ -35,20 +35,14 @@ using namespace std;
|
|||||||
InterthreadQueue<int> gQ;
|
InterthreadQueue<int> gQ;
|
||||||
InterthreadMap<int,int> gMap;
|
InterthreadMap<int,int> gMap;
|
||||||
|
|
||||||
int q_last_read_val = -1;
|
|
||||||
int q_last_write_val;
|
|
||||||
int m_last_read_val;
|
|
||||||
int m_last_write_val;
|
|
||||||
|
|
||||||
void* qWriter(void*)
|
void* qWriter(void*)
|
||||||
{
|
{
|
||||||
int *p;
|
int *p;
|
||||||
for (int i=0; i<20; i++) {
|
for (int i=0; i<20; i++) {
|
||||||
p = new int;
|
p = new int;
|
||||||
*p = i;
|
*p = i;
|
||||||
CERR("queue write " << *p);
|
COUT("queue write " << *p);
|
||||||
gQ.write(p);
|
gQ.write(p);
|
||||||
q_last_write_val = i;
|
|
||||||
if (random()%2) sleep(1);
|
if (random()%2) sleep(1);
|
||||||
}
|
}
|
||||||
p = new int;
|
p = new int;
|
||||||
@@ -62,14 +56,8 @@ void* qReader(void*)
|
|||||||
bool done = false;
|
bool done = false;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
int *p = gQ.read();
|
int *p = gQ.read();
|
||||||
CERR("queue read " << *p);
|
COUT("queue read " << *p);
|
||||||
if (*p<0) {
|
if (*p<0) done=true;
|
||||||
assert(q_last_read_val == 19 && *p == -1);
|
|
||||||
done = true;
|
|
||||||
} else {
|
|
||||||
assert(q_last_read_val == *p - 1);
|
|
||||||
q_last_read_val = *p;
|
|
||||||
}
|
|
||||||
delete p;
|
delete p;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -82,9 +70,8 @@ void* mapWriter(void*)
|
|||||||
for (int i=0; i<20; i++) {
|
for (int i=0; i<20; i++) {
|
||||||
p = new int;
|
p = new int;
|
||||||
*p = i;
|
*p = i;
|
||||||
CERR("map write " << *p);
|
COUT("map write " << *p);
|
||||||
gMap.write(i,p);
|
gMap.write(i,p);
|
||||||
m_last_write_val = i;
|
|
||||||
if (random()%2) sleep(1);
|
if (random()%2) sleep(1);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -94,9 +81,7 @@ void* mapReader(void*)
|
|||||||
{
|
{
|
||||||
for (int i=0; i<20; i++) {
|
for (int i=0; i<20; i++) {
|
||||||
int *p = gMap.read(i);
|
int *p = gMap.read(i);
|
||||||
CERR("map read " << *p);
|
COUT("map read " << *p);
|
||||||
assert(*p == i);
|
|
||||||
m_last_read_val = *p;
|
|
||||||
// InterthreadMap will delete the pointers
|
// InterthreadMap will delete the pointers
|
||||||
// delete p;
|
// delete p;
|
||||||
}
|
}
|
||||||
@@ -124,13 +109,6 @@ int main(int argc, char *argv[])
|
|||||||
qWriterThread.join();
|
qWriterThread.join();
|
||||||
mapReaderThread.join();
|
mapReaderThread.join();
|
||||||
mapWriterThread.join();
|
mapWriterThread.join();
|
||||||
|
|
||||||
assert(q_last_write_val == 19);
|
|
||||||
assert(q_last_read_val == 19);
|
|
||||||
assert(m_last_write_val == 19);
|
|
||||||
assert(m_last_read_val == 19);
|
|
||||||
|
|
||||||
printf("Done\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -29,25 +29,6 @@
|
|||||||
#include "LinkedLists.h"
|
#include "LinkedLists.h"
|
||||||
|
|
||||||
|
|
||||||
PointerFIFO::~PointerFIFO()
|
|
||||||
{
|
|
||||||
ListNode *node, *next;
|
|
||||||
|
|
||||||
node = mHead;
|
|
||||||
while (node != NULL) {
|
|
||||||
next = node->next();
|
|
||||||
delete node;
|
|
||||||
node = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = mFreeList;
|
|
||||||
while (node != NULL) {
|
|
||||||
next = node->next();
|
|
||||||
delete node;
|
|
||||||
node = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PointerFIFO::push_front(void* val) // by pat
|
void PointerFIFO::push_front(void* val) // by pat
|
||||||
{
|
{
|
||||||
// Pat added this routine for completeness, but never used or tested.
|
// Pat added this routine for completeness, but never used or tested.
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ class PointerFIFO {
|
|||||||
:mHead(NULL),mTail(NULL),mFreeList(NULL),
|
:mHead(NULL),mTail(NULL),mFreeList(NULL),
|
||||||
mSize(0)
|
mSize(0)
|
||||||
{}
|
{}
|
||||||
~PointerFIFO();
|
|
||||||
|
|
||||||
unsigned size() const { return mSize; }
|
unsigned size() const { return mSize; }
|
||||||
unsigned totalSize() const { return 0; } // Not used in this version.
|
unsigned totalSize() const { return 0; } // Not used in this version.
|
||||||
|
|||||||
@@ -28,42 +28,42 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
extern "C" {
|
#include "Configuration.h"
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/application.h>
|
|
||||||
#include <osmocom/core/utils.h>
|
|
||||||
#include "debug.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MYCAT 0
|
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[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct log_info_cat categories[1];
|
gLogInit("LogTest","NOTICE",LOG_LOCAL7);
|
||||||
struct log_info linfo;
|
|
||||||
categories[MYCAT] = {
|
|
||||||
"MYCAT",
|
|
||||||
NULL,
|
|
||||||
"Whatever",
|
|
||||||
LOGL_NOTICE,
|
|
||||||
1,
|
|
||||||
};
|
|
||||||
linfo.cat = categories;
|
|
||||||
linfo.num_cat = ARRAY_SIZE(categories);
|
|
||||||
|
|
||||||
void *tall_ctx = talloc_named_const(NULL, 1, "OsmoTRX context");
|
LOG(EMERG) << " testing the logger.";
|
||||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
LOG(ALERT) << " testing the logger.";
|
||||||
|
LOG(CRIT) << " testing the logger.";
|
||||||
osmo_init_logging2(tall_ctx, &linfo);
|
LOG(ERR) << " testing the logger.";
|
||||||
|
LOG(WARNING) << " testing the logger.";
|
||||||
log_set_use_color(osmo_stderr_target, 0);
|
LOG(NOTICE) << " testing the logger.";
|
||||||
log_set_print_filename(osmo_stderr_target, 0);
|
LOG(INFO) << " testing the logger.";
|
||||||
log_set_print_level(osmo_stderr_target, 1);
|
LOG(DEBUG) << " testing the logger.";
|
||||||
|
std::cout << "\n\n\n";
|
||||||
Log(MYCAT, LOGL_FATAL, __BASE_FILE__, __LINE__).get() << "testing the logger.";
|
std::cout << "testing Alarms\n";
|
||||||
Log(MYCAT, LOGL_ERROR, __BASE_FILE__, __LINE__).get() << "testing the logger.";
|
std::cout << "you should see three lines:" << std::endl;
|
||||||
Log(MYCAT, LOGL_NOTICE, __BASE_FILE__, __LINE__).get() << "testing the logger.";
|
printAlarms();
|
||||||
Log(MYCAT, LOGL_INFO, __BASE_FILE__, __LINE__).get() << "testing the logger.";
|
std::cout << "----------- generating 20 alarms ----------" << std::endl;
|
||||||
Log(MYCAT, LOGL_DEBUG, __BASE_FILE__, __LINE__).get() << "testing the logger.";
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018 sysmocom - s.f.m.c. GmbH
|
* 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.
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
@@ -28,37 +30,248 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <sys/time.h> // For gettimeofday
|
|
||||||
|
|
||||||
|
#include "Configuration.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Threads.h" // pat added
|
#include "Threads.h" // pat added
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Names of the logging levels. */
|
||||||
|
const char *levelNames[] = {
|
||||||
|
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||||
|
};
|
||||||
|
int numLevels = 8;
|
||||||
|
bool gLogToConsole = 0;
|
||||||
|
FILE *gLogToFile = NULL;
|
||||||
Mutex gLogToLock;
|
Mutex gLogToLock;
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
|
|
||||||
|
int levelStringToInt(const string& name)
|
||||||
{
|
{
|
||||||
return os << ss.str();
|
// Reverse search, since the numerically larger levels are more common.
|
||||||
|
for (int i=numLevels-1; i>=0; i--) {
|
||||||
|
if (name == levelNames[i]) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common substitutions.
|
||||||
|
if (name=="INFORMATION") return 6;
|
||||||
|
if (name=="WARN") return 4;
|
||||||
|
if (name=="ERROR") return 3;
|
||||||
|
if (name=="CRITICAL") return 2;
|
||||||
|
if (name=="EMERGENCY") return 0;
|
||||||
|
|
||||||
|
// Unknown level.
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Given a string, return the corresponding level name. */
|
||||||
|
int lookupLevel(const string& key)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int getLoggingLevel(const char* filename)
|
||||||
|
{
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int gGetLoggingLevel(const char* filename)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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()
|
Log::~Log()
|
||||||
{
|
{
|
||||||
int old_state;
|
if (mDummyInit) return;
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
|
// Anything at or above LOG_CRIT is an "alarm".
|
||||||
int mlen = mStream.str().size();
|
// Save alarms in the local list and echo them to stderr.
|
||||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
if (mPriority <= LOG_CRIT) {
|
||||||
const char *fmt = neednl ? "%s\n" : "%s";
|
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||||
ScopedLock lock(gLogToLock);
|
cerr << mStream.str() << endl;
|
||||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
}
|
||||||
// so just use std::cout.
|
// Current logging level was already checked by the macro.
|
||||||
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
|
// So just log.
|
||||||
pthread_setcancelstate(old_state, NULL);
|
syslog(mPriority, "%s", mStream.str().c_str());
|
||||||
|
// pat added for easy debugging.
|
||||||
|
if (gLogToConsole||gLogToFile) {
|
||||||
|
int mlen = mStream.str().size();
|
||||||
|
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||||
|
gLogToLock.lock();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
gLogToLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Log::Log(const char* name, const char* level, int facility)
|
||||||
|
{
|
||||||
|
mDummyInit = true;
|
||||||
|
gLogInit(name, level, facility);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ostringstream& Log::get()
|
ostringstream& Log::get()
|
||||||
{
|
{
|
||||||
|
assert(mPriority<numLevels);
|
||||||
|
mStream << levelNames[mPriority] << ' ';
|
||||||
return mStream;
|
return mStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void gLogInit(const char* name, const char* level, int facility)
|
||||||
|
{
|
||||||
|
// Set the level if one has been specified.
|
||||||
|
if (level) {
|
||||||
|
gConfig.set("Log.Level",level);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pat added, tired of the syslog facility.
|
||||||
|
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
|
||||||
|
string str = gConfig.getStr("Log.File");
|
||||||
|
if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
// vim: ts=4 sw=4
|
// vim: ts=4 sw=4
|
||||||
|
|||||||
@@ -23,44 +23,72 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// (pat) WARNING is stupidly defined in /usr/local/include/osipparser2/osip_const.h.
|
||||||
|
// This must be outside the #ifndef LOGGER_H to fix it as long as Logger.h included after the above file.
|
||||||
|
#ifdef WARNING
|
||||||
|
#undef WARNING
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef LOGGER_H
|
#ifndef LOGGER_H
|
||||||
#define LOGGER_H
|
#define LOGGER_H
|
||||||
|
|
||||||
|
#include <syslog.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
extern "C" {
|
#define _LOG(level) \
|
||||||
#include <osmocom/core/logging.h>
|
Log(LOG_##level).get() << pthread_self() \
|
||||||
#include "debug.h"
|
<< timestr() << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
|
||||||
}
|
|
||||||
|
|
||||||
/* Translation for old log statements */
|
#define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel)
|
||||||
#ifndef LOGL_ALERT
|
|
||||||
#define LOGL_ALERT LOGL_FATAL
|
#ifdef NDEBUG
|
||||||
#endif
|
#define LOG(wLevel) \
|
||||||
#ifndef LOGL_ERR
|
if (LOG_##wLevel!=LOG_DEBUG && IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
|
||||||
#define LOGL_ERR LOGL_ERROR
|
#else
|
||||||
#endif
|
#define LOG(wLevel) \
|
||||||
#ifndef LOGL_WARNING
|
if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
|
||||||
#define LOGL_WARNING LOGL_NOTICE
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOG(level) \
|
// pat: And for your edification here are the 'levels' as defined in syslog.h:
|
||||||
Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
|
// 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
|
||||||
|
|
||||||
#define LOGC(category, level) \
|
// (pat) added - print out a var and its name.
|
||||||
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
|
// 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 LOGLV(category, level) \
|
|
||||||
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
|
|
||||||
|
|
||||||
#define LOGCHAN(chan, category, level) \
|
#define OBJLOG(wLevel) \
|
||||||
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "][chan=" << chan << "] "
|
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.
|
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;
|
This object is NOT the global logger;
|
||||||
every log record is an object of this class.
|
every log record is an object of this class.
|
||||||
*/
|
*/
|
||||||
@@ -70,27 +98,41 @@ class Log {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
std::ostringstream mStream; ///< This is where we buffer up the log entry.
|
std::ostringstream mStream; ///< This is where we buffer up the log entry.
|
||||||
int mCategory; ///< Priority of current report.
|
int mPriority; ///< Priority of current report.
|
||||||
int mPriority; ///< Category of current report.
|
bool mDummyInit;
|
||||||
const char *filename; ///< Source File Name of current report.
|
|
||||||
int line; ///< Line number in source file of current report.
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Log(int wCategory, int wPriority, const char* filename, int line)
|
Log(int wPriority)
|
||||||
: mCategory(wCategory), mPriority(wPriority),
|
:mPriority(wPriority), mDummyInit(false)
|
||||||
filename(filename), line(line)
|
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
Log(const char* name, const char* level=NULL, int facility=LOG_USER);
|
||||||
|
|
||||||
// Most of the work is in the destructor.
|
// Most of the work is in the destructor.
|
||||||
/** The destructor actually generates the log entry. */
|
/** The destructor actually generates the log entry. */
|
||||||
~Log();
|
~Log();
|
||||||
|
|
||||||
std::ostringstream& get();
|
std::ostringstream& get();
|
||||||
};
|
};
|
||||||
|
extern bool gLogToConsole; // Pat added for easy debugging.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::list<std::string> gGetLoggerAlarms(); ///< Get a copy of the recent alarm list.
|
||||||
|
|
||||||
|
|
||||||
|
/**@ 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)));
|
||||||
|
//@}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,11 @@
|
|||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||||
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
|
||||||
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
example.config \
|
||||||
|
README.common
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libcommon.la
|
noinst_LTLIBRARIES = libcommon.la
|
||||||
|
|
||||||
@@ -33,24 +36,78 @@ libcommon_la_SOURCES = \
|
|||||||
Sockets.cpp \
|
Sockets.cpp \
|
||||||
Threads.cpp \
|
Threads.cpp \
|
||||||
Timeval.cpp \
|
Timeval.cpp \
|
||||||
|
Reporting.cpp \
|
||||||
Logger.cpp \
|
Logger.cpp \
|
||||||
Utils.cpp \
|
Configuration.cpp \
|
||||||
trx_vty.c \
|
sqlite3util.cpp \
|
||||||
debug.c
|
URLEncode.cpp \
|
||||||
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
|
Utils.cpp
|
||||||
|
|
||||||
|
noinst_PROGRAMS = \
|
||||||
|
BitVectorTest \
|
||||||
|
InterthreadTest \
|
||||||
|
SocketsTest \
|
||||||
|
TimevalTest \
|
||||||
|
RegexpTest \
|
||||||
|
VectorTest \
|
||||||
|
ConfigurationTest \
|
||||||
|
LogTest \
|
||||||
|
URLEncodeTest \
|
||||||
|
F16Test
|
||||||
|
|
||||||
|
# ReportingTest
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
BitVector.h \
|
BitVector.h \
|
||||||
PRBS.h \
|
|
||||||
Interthread.h \
|
Interthread.h \
|
||||||
LinkedLists.h \
|
LinkedLists.h \
|
||||||
Sockets.h \
|
Sockets.h \
|
||||||
Threads.h \
|
Threads.h \
|
||||||
Timeval.h \
|
Timeval.h \
|
||||||
|
Regexp.h \
|
||||||
Vector.h \
|
Vector.h \
|
||||||
Logger.h \
|
Configuration.h \
|
||||||
|
Reporting.h \
|
||||||
|
F16.h \
|
||||||
|
URLEncode.h \
|
||||||
Utils.h \
|
Utils.h \
|
||||||
trx_vty.h \
|
Logger.h \
|
||||||
debug.h \
|
sqlite3util.h
|
||||||
osmo_signal.h \
|
|
||||||
config_defs.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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
111
CommonLibs/MemoryLeak.h
Normal file
111
CommonLibs/MemoryLeak.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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
|
|
||||||
64
CommonLibs/Regexp.h
Normal file
64
CommonLibs/Regexp.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
@@ -25,36 +25,24 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "Regexp.h"
|
||||||
#include "BitVector.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
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();
|
Regexp email("^[[:graph:]]+@[[:graph:]]+ ");
|
||||||
cout << v5 << endl;
|
Regexp simple("^dburgess@");
|
||||||
|
|
||||||
|
const char text1[] = "dburgess@jcis.net test message";
|
||||||
|
const char text2[] = "no address text message";
|
||||||
|
|
||||||
unsigned char ts[9] = "abcdefgh";
|
cout << email.match(text1) << " " << text1 << endl;
|
||||||
BitVector tp(70);
|
cout << email.match(text2) << " " << text2 << endl;
|
||||||
cout << "ts=" << ts << endl;
|
|
||||||
tp.unpack(ts);
|
cout << simple.match(text1) << " " << text1 << endl;
|
||||||
cout << "tp=" << tp << endl;
|
cout << simple.match(text2) << " " << text2 << endl;
|
||||||
tp.pack(ts);
|
|
||||||
cout << "ts=" << ts << endl;
|
|
||||||
}
|
}
|
||||||
145
CommonLibs/Reporting.cpp
Normal file
145
CommonLibs/Reporting.cpp
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/**@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
86
CommonLibs/Reporting.h
Normal file
86
CommonLibs/Reporting.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**@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
|
||||||
136
CommonLibs/ScalarTypes.h
Normal file
136
CommonLibs/ScalarTypes.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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,20 +187,24 @@ int DatagramSocket::send(const struct sockaddr* dest, const char * message)
|
|||||||
return send(dest,message,length);
|
return send(dest,message,length);
|
||||||
}
|
}
|
||||||
|
|
||||||
int DatagramSocket::read(char* buffer, size_t length)
|
|
||||||
{
|
|
||||||
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)) {
|
|
||||||
|
|
||||||
|
|
||||||
|
int DatagramSocket::read(char* buffer)
|
||||||
|
{
|
||||||
|
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)) {
|
||||||
perror("DatagramSocket::read() failed");
|
perror("DatagramSocket::read() failed");
|
||||||
throw SocketError();
|
throw SocketError();
|
||||||
}
|
}
|
||||||
return rd_length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
|
||||||
|
int DatagramSocket::read(char* buffer, unsigned timeout)
|
||||||
{
|
{
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
@@ -214,7 +218,7 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
|||||||
throw SocketError();
|
throw SocketError();
|
||||||
}
|
}
|
||||||
if (sel==0) return -1;
|
if (sel==0) return -1;
|
||||||
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
|
if (FD_ISSET(mSocketFD,&fds)) return read(buffer);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,18 +227,18 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
|
UDPSocket::UDPSocket(unsigned short wSrcPort)
|
||||||
:DatagramSocket()
|
:DatagramSocket()
|
||||||
{
|
{
|
||||||
open(wSrcPort, wSrcIP);
|
open(wSrcPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
|
UDPSocket::UDPSocket(unsigned short wSrcPort,
|
||||||
const char *wDestIP, unsigned short wDestPort)
|
const char * wDestIP, unsigned short wDestPort )
|
||||||
:DatagramSocket()
|
:DatagramSocket()
|
||||||
{
|
{
|
||||||
open(wSrcPort, wSrcIP);
|
open(wSrcPort);
|
||||||
destination(wDestPort, wDestIP);
|
destination(wDestPort, wDestIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +250,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
|
void UDPSocket::open(unsigned short localPort)
|
||||||
{
|
{
|
||||||
// create
|
// create
|
||||||
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
||||||
@@ -265,7 +269,7 @@ void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
|
|||||||
size_t length = sizeof(address);
|
size_t length = sizeof(address);
|
||||||
bzero(&address,length);
|
bzero(&address,length);
|
||||||
address.sin_family = AF_INET;
|
address.sin_family = AF_INET;
|
||||||
address.sin_addr.s_addr = inet_addr(wlocalIP);
|
address.sin_addr.s_addr = INADDR_ANY;
|
||||||
address.sin_port = htons(localPort);
|
address.sin_port = htons(localPort);
|
||||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||||
perror("bind() failed");
|
perror("bind() failed");
|
||||||
@@ -284,4 +288,50 @@ unsigned short UDPSocket::port() const
|
|||||||
return ntohs(name.sin_port);
|
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
|
// vim:ts=4:sw=4
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public:
|
|||||||
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
|
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
|
||||||
@return The number of bytes received or -1 on non-blocking pass.
|
@return The number of bytes received or -1 on non-blocking pass.
|
||||||
*/
|
*/
|
||||||
int read(char* buffer, size_t length);
|
int read(char* buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Receive a packet with a timeout.
|
Receive a packet with a timeout.
|
||||||
@@ -116,7 +116,7 @@ public:
|
|||||||
@param maximum wait time in milliseconds
|
@param maximum wait time in milliseconds
|
||||||
@return The number of bytes received or -1 on timeout.
|
@return The number of bytes received or -1 on timeout.
|
||||||
*/
|
*/
|
||||||
int read(char* buffer, size_t length, unsigned timeout);
|
int read(char* buffer, unsigned timeout);
|
||||||
|
|
||||||
|
|
||||||
/** Send a packet to a given destination, other than the default. */
|
/** Send a packet to a given destination, other than the default. */
|
||||||
@@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** Open a USP socket with an OS-assigned port and no default destination. */
|
/** Open a USP socket with an OS-assigned port and no default destination. */
|
||||||
UDPSocket(const char *localIP, unsigned short localPort);
|
UDPSocket( unsigned short localPort=0);
|
||||||
|
|
||||||
/** Given a full specification, open the socket and set the dest address. */
|
/** Given a full specification, open the socket and set the dest address. */
|
||||||
UDPSocket(const char *localIP, unsigned short localPort,
|
UDPSocket( unsigned short localPort,
|
||||||
const char *remoteIP, unsigned short remotePort);
|
const char * remoteIP, unsigned short remotePort);
|
||||||
|
|
||||||
/** Set the destination port. */
|
/** Set the destination port. */
|
||||||
void destination( unsigned short wDestPort, const char * wDestIP );
|
void destination( unsigned short wDestPort, const char * wDestIP );
|
||||||
@@ -157,7 +157,7 @@ public:
|
|||||||
unsigned short port() const;
|
unsigned short port() const;
|
||||||
|
|
||||||
/** Open and bind the UDP socket to a local port. */
|
/** Open and bind the UDP socket to a local port. */
|
||||||
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
|
void open(unsigned short localPort=0);
|
||||||
|
|
||||||
/** Give the return address of the most recently received packet. */
|
/** Give the return address of the most recently received packet. */
|
||||||
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
||||||
@@ -166,6 +166,26 @@ 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
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,28 +30,21 @@
|
|||||||
#include "Threads.h"
|
#include "Threads.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
static const int gNumToSend = 10;
|
static const int gNumToSend = 10;
|
||||||
|
|
||||||
static void sigalarm_handler(int foo)
|
|
||||||
{
|
|
||||||
printf("FAIL: test did not run successfully\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *testReaderIP(void *param)
|
void *testReaderIP(void *)
|
||||||
{
|
{
|
||||||
UDPSocket *readSocket = (UDPSocket *)param;
|
UDPSocket readSocket(5934, "localhost", 5061);
|
||||||
readSocket->nonblocking();
|
readSocket.nonblocking();
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
while (rc<gNumToSend) {
|
while (rc<gNumToSend) {
|
||||||
char buf[MAX_UDP_LENGTH+1] = { 0 };
|
char buf[MAX_UDP_LENGTH];
|
||||||
int count = readSocket->read(buf, MAX_UDP_LENGTH);
|
int count = readSocket.read(buf);
|
||||||
if (count>0) {
|
if (count>0) {
|
||||||
buf[count] = 0;
|
COUT("read: " << buf);
|
||||||
CERR("read: " << buf);
|
|
||||||
rc++;
|
rc++;
|
||||||
} else {
|
} else {
|
||||||
sleep(2);
|
sleep(2);
|
||||||
@@ -60,42 +53,51 @@ void *testReaderIP(void *param)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void *testReaderUnix(void *)
|
||||||
|
{
|
||||||
|
UDDSocket readSocket("testDestination");
|
||||||
|
readSocket.nonblocking();
|
||||||
|
int rc = 0;
|
||||||
|
while (rc<gNumToSend) {
|
||||||
|
char buf[MAX_UDP_LENGTH];
|
||||||
|
int count = readSocket.read(buf);
|
||||||
|
if (count>0) {
|
||||||
|
COUT("read: " << buf);
|
||||||
|
rc++;
|
||||||
|
} else {
|
||||||
|
sleep(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char * argv[] )
|
int main(int argc, char * argv[] )
|
||||||
{
|
{
|
||||||
int count;
|
|
||||||
|
|
||||||
if (signal(SIGALRM, sigalarm_handler) == SIG_ERR) {
|
|
||||||
perror("signal");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the test takes longer than 2*gNumToSend seconds, abort it */
|
|
||||||
alarm(2* gNumToSend);
|
|
||||||
|
|
||||||
UDPSocket readSocket("127.0.0.1", 0);
|
|
||||||
UDPSocket socket1("127.0.0.1", 0, "localhost", readSocket.port());
|
|
||||||
|
|
||||||
CERR("socket1: " << socket1.port() << ", readSocket: " << readSocket.port());
|
|
||||||
|
|
||||||
Thread readerThreadIP;
|
Thread readerThreadIP;
|
||||||
readerThreadIP.start(testReaderIP, &readSocket);
|
readerThreadIP.start(testReaderIP,NULL);
|
||||||
|
Thread readerThreadUnix;
|
||||||
|
readerThreadUnix.start(testReaderUnix,NULL);
|
||||||
|
|
||||||
|
UDPSocket socket1(5061, "127.0.0.1",5934);
|
||||||
|
UDDSocket socket1U("testSource","testDestination");
|
||||||
|
|
||||||
|
COUT("socket1: " << socket1.port());
|
||||||
|
|
||||||
// give the readers time to open
|
// give the readers time to open
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
for (int i=0; i<gNumToSend; i++) {
|
for (int i=0; i<gNumToSend; i++) {
|
||||||
CERR("write");
|
socket1.write("Hello IP land");
|
||||||
count = socket1.write("Hello IP land");
|
socket1U.write("Hello Unix domain");
|
||||||
if (count < 0) {
|
sleep(1);
|
||||||
COUT("FAIL: write");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
sleep(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readerThreadIP.join();
|
readerThreadIP.join();
|
||||||
|
readerThreadUnix.join();
|
||||||
printf("Done\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: ts=4 sw=4
|
// vim: ts=4 sw=4
|
||||||
@@ -24,17 +24,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "Threads.h"
|
#include "Threads.h"
|
||||||
#include "Timeval.h"
|
#include "Timeval.h"
|
||||||
#include "Logger.h"
|
|
||||||
|
|
||||||
#ifndef gettid
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#define gettid() syscall(SYS_gettid)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -108,25 +102,6 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const
|
|||||||
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
|
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_selfthread_name(const char *name)
|
|
||||||
{
|
|
||||||
pthread_t selfid = pthread_self();
|
|
||||||
pid_t tid = gettid();
|
|
||||||
if (pthread_setname_np(selfid, name) == 0) {
|
|
||||||
LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
|
|
||||||
} else {
|
|
||||||
char buf[256];
|
|
||||||
int err = errno;
|
|
||||||
char* err_str = strerror_r(err, buf, sizeof(buf));
|
|
||||||
LOG(NOTICE) << "Thread "<< selfid << " (task " << tid << ") set name \"" << name << "\" failed: (" << err << ") " << err_str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void thread_enable_cancel(bool cancel)
|
|
||||||
{
|
|
||||||
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Thread::start(void *(*task)(void*), void *arg)
|
void Thread::start(void *(*task)(void*), void *arg)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ void unlockCout(); ///< call after writing cout
|
|||||||
#define OBJDCOUT(text) {}
|
#define OBJDCOUT(text) {}
|
||||||
#else
|
#else
|
||||||
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
|
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
|
||||||
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
|
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
|
||||||
#endif
|
#endif
|
||||||
//@}
|
//@}
|
||||||
//@}
|
//@}
|
||||||
@@ -141,9 +141,6 @@ class Signal {
|
|||||||
#define START_THREAD(thread,function,argument) \
|
#define START_THREAD(thread,function,argument) \
|
||||||
thread.start((void *(*)(void*))function, (void*)argument);
|
thread.start((void *(*)(void*))function, (void*)argument);
|
||||||
|
|
||||||
void set_selfthread_name(const char *name);
|
|
||||||
void thread_enable_cancel(bool cancel);
|
|
||||||
|
|
||||||
/** A C++ wrapper for pthread threads. */
|
/** A C++ wrapper for pthread threads. */
|
||||||
class Thread {
|
class Thread {
|
||||||
|
|
||||||
@@ -153,7 +150,7 @@ class Thread {
|
|||||||
pthread_attr_t mAttrib;
|
pthread_attr_t mAttrib;
|
||||||
// FIXME -- Can this be reduced now?
|
// FIXME -- Can this be reduced now?
|
||||||
size_t mStackSize;
|
size_t mStackSize;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -175,15 +172,8 @@ class Thread {
|
|||||||
void start(void *(*task)(void*), void *arg);
|
void start(void *(*task)(void*), void *arg);
|
||||||
|
|
||||||
/** Join a thread that will stop on its own. */
|
/** Join a thread that will stop on its own. */
|
||||||
void join() {
|
void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
|
||||||
if (mThread) {
|
|
||||||
int s = pthread_join(mThread, NULL);
|
|
||||||
assert(!s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Send cancelation to thread */
|
|
||||||
void cancel() { pthread_cancel(mThread); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,49 +27,43 @@
|
|||||||
|
|
||||||
#include "Timeval.h"
|
#include "Timeval.h"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <osmocom/core/timer.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void Timeval::now()
|
|
||||||
{
|
|
||||||
osmo_clock_gettime(CLOCK_REALTIME, &mTimespec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timeval::future(unsigned offset)
|
void Timeval::future(unsigned offset)
|
||||||
{
|
{
|
||||||
now();
|
now();
|
||||||
unsigned sec = offset/1000;
|
unsigned sec = offset/1000;
|
||||||
unsigned msec = offset%1000;
|
unsigned msec = offset%1000;
|
||||||
mTimespec.tv_nsec += msec*1000*1000;
|
mTimeval.tv_usec += msec*1000;
|
||||||
mTimespec.tv_sec += sec;
|
mTimeval.tv_sec += sec;
|
||||||
if (mTimespec.tv_nsec > 1000*1000*1000) {
|
if (mTimeval.tv_usec>1000000) {
|
||||||
mTimespec.tv_nsec -= 1000*1000*1000;
|
mTimeval.tv_usec -= 1000000;
|
||||||
mTimespec.tv_sec += 1;
|
mTimeval.tv_sec += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct timespec Timeval::timespec() const
|
struct timespec Timeval::timespec() const
|
||||||
{
|
{
|
||||||
return mTimespec;
|
struct timespec retVal;
|
||||||
|
retVal.tv_sec = mTimeval.tv_sec;
|
||||||
|
retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Timeval::passed() const
|
bool Timeval::passed() const
|
||||||
{
|
{
|
||||||
Timeval nowTime;
|
Timeval nowTime;
|
||||||
if (nowTime.mTimespec.tv_sec < mTimespec.tv_sec) return false;
|
if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
|
||||||
if (nowTime.mTimespec.tv_sec > mTimespec.tv_sec) return true;
|
if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
|
||||||
if (nowTime.mTimespec.tv_nsec >= mTimespec.tv_nsec) return true;
|
if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
double Timeval::seconds() const
|
double Timeval::seconds() const
|
||||||
{
|
{
|
||||||
return ((double)mTimespec.tv_sec) + 1e-9*((double)mTimespec.tv_nsec);
|
return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -78,8 +72,8 @@ long Timeval::delta(const Timeval& other) const
|
|||||||
{
|
{
|
||||||
// 2^31 milliseconds is just over 4 years.
|
// 2^31 milliseconds is just over 4 years.
|
||||||
int32_t deltaS = other.sec() - sec();
|
int32_t deltaS = other.sec() - sec();
|
||||||
int32_t deltaNs = other.nsec() - nsec();
|
int32_t deltaUs = other.usec() - usec();
|
||||||
return 1000*deltaS + deltaNs/1000000;
|
return 1000*deltaS + deltaUs/1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -95,7 +89,7 @@ ostream& operator<<(ostream& os, const Timeval& tv)
|
|||||||
|
|
||||||
ostream& operator<<(ostream& os, const struct timespec& ts)
|
ostream& operator<<(ostream& os, const struct timespec& ts)
|
||||||
{
|
{
|
||||||
os << ts.tv_sec << "," << ts.tv_nsec/1000;
|
os << ts.tv_sec << "," << ts.tv_nsec;
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ class Timeval {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct timespec mTimespec;
|
struct timeval mTimeval;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Set the value to current time. */
|
/** Set the value to gettimeofday. */
|
||||||
void now();
|
void now() { gettimeofday(&mTimeval,NULL); }
|
||||||
|
|
||||||
/** Set the value to gettimeofday plus an offset. */
|
/** Set the value to gettimeofday plus an offset. */
|
||||||
void future(unsigned ms);
|
void future(unsigned ms);
|
||||||
@@ -55,18 +55,16 @@ class Timeval {
|
|||||||
//@{
|
//@{
|
||||||
Timeval(unsigned sec, unsigned usec)
|
Timeval(unsigned sec, unsigned usec)
|
||||||
{
|
{
|
||||||
mTimespec.tv_sec = sec;
|
mTimeval.tv_sec = sec;
|
||||||
mTimespec.tv_nsec = usec*1000;
|
mTimeval.tv_usec = usec;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timeval(const struct timeval& wTimeval)
|
Timeval(const struct timeval& wTimeval)
|
||||||
{
|
:mTimeval(wTimeval)
|
||||||
mTimespec.tv_sec = wTimeval.tv_sec;
|
{}
|
||||||
mTimespec.tv_nsec = wTimeval.tv_sec*1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Create a Timespec offset into the future.
|
Create a Timeval offset into the future.
|
||||||
@param offset milliseconds
|
@param offset milliseconds
|
||||||
*/
|
*/
|
||||||
Timeval(unsigned offset=0) { future(offset); }
|
Timeval(unsigned offset=0) { future(offset); }
|
||||||
@@ -78,9 +76,8 @@ class Timeval {
|
|||||||
/** Return total seconds. */
|
/** Return total seconds. */
|
||||||
double seconds() const;
|
double seconds() const;
|
||||||
|
|
||||||
uint32_t sec() const { return mTimespec.tv_sec; }
|
uint32_t sec() const { return mTimeval.tv_sec; }
|
||||||
uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
|
uint32_t usec() const { return mTimeval.tv_usec; }
|
||||||
uint32_t nsec() const { return mTimespec.tv_nsec; }
|
|
||||||
|
|
||||||
/** Return differnce from other (other-self), in ms. */
|
/** Return differnce from other (other-self), in ms. */
|
||||||
long delta(const Timeval& other) const;
|
long delta(const Timeval& other) const;
|
||||||
@@ -91,11 +88,11 @@ class Timeval {
|
|||||||
/** Remaining time in ms. */
|
/** Remaining time in ms. */
|
||||||
long remaining() const { return -elapsed(); }
|
long remaining() const { return -elapsed(); }
|
||||||
|
|
||||||
/** Return true if the time has passed, as per clock_gettime(CLOCK_REALTIME). */
|
/** Return true if the time has passed, as per gettimeofday. */
|
||||||
bool passed() const;
|
bool passed() const;
|
||||||
|
|
||||||
/** Add a given number of minutes to the time. */
|
/** Add a given number of minutes to the time. */
|
||||||
void addMinutes(unsigned minutes) { mTimespec.tv_sec += minutes*60; }
|
void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
45
CommonLibs/TimevalTest.cpp
Normal file
45
CommonLibs/TimevalTest.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 "Timeval.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
Timeval then(10000);
|
||||||
|
cout << then.elapsed() << endl;
|
||||||
|
|
||||||
|
while (!then.passed()) {
|
||||||
|
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
|
||||||
|
usleep(500000);
|
||||||
|
}
|
||||||
|
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
|
||||||
|
}
|
||||||
28
CommonLibs/URLEncode.cpp
Normal file
28
CommonLibs/URLEncode.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
30
CommonLibs/URLEncode.h
Normal file
30
CommonLibs/URLEncode.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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&);
|
||||||
17
CommonLibs/URLEncodeTest.cpp
Normal file
17
CommonLibs/URLEncodeTest.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
#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,36 +1,211 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 sysmocom - s.f.m.c. GmbH
|
* Copyright 2011 Range Networks, Inc.
|
||||||
*
|
* All Rights Reserved.
|
||||||
* This library is free software; you can redistribute it and/or
|
*
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* This software is distributed under multiple licenses;
|
||||||
* License as published by the Free Software Foundation; either
|
* see the COPYING file in the main directory for licensing
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
* information for this specific distribuion.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This use of this software may be subject to additional restrictions.
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* See the LEGAL file in the main directory for details.
|
||||||
* 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 <vector>
|
This program is distributed in the hope that it will be useful,
|
||||||
#include <string>
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
#include <sstream>
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
|
||||||
std::vector<std::string> comma_delimited_to_vector(const char* opt)
|
#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()
|
||||||
{
|
{
|
||||||
std::string str = std::string(opt);
|
memset(mMemNow,0,sizeof(mMemNow));
|
||||||
std::vector<std::string> result;
|
memset(mMemTotal,0,sizeof(mMemTotal));
|
||||||
std::stringstream ss(str);
|
memset(mMemName,0,sizeof(mMemName));
|
||||||
|
|
||||||
while( ss.good() )
|
|
||||||
{
|
|
||||||
std::string substr;
|
|
||||||
getline(ss, substr, ',');
|
|
||||||
result.push_back(substr);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,24 +1,148 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 sysmocom - s.f.m.c. GmbH
|
* Copyright 2011 Range Networks, Inc.
|
||||||
*
|
* All Rights Reserved.
|
||||||
* This library is free software; you can redistribute it and/or
|
*
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* This software is distributed under multiple licenses;
|
||||||
* License as published by the Free Software Foundation; either
|
* see the COPYING file in the main directory for licensing
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
* information for this specific distribuion.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This use of this software may be subject to additional restrictions.
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* See the LEGAL file in the main directory for details.
|
||||||
* 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
|
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 <vector>
|
#ifndef GPRSUTILS_H
|
||||||
|
#define GPRSUTILS_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h> // for sqrtf
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
std::vector<std::string> comma_delimited_to_vector(const char* opt);
|
|
||||||
|
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
|
||||||
|
|||||||
@@ -32,14 +32,11 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
// We cant use Logger.h in this file...
|
// We cant use Logger.h in this file...
|
||||||
extern int gVectorDebug;
|
extern int gVectorDebug;
|
||||||
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
|
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
|
||||||
|
|
||||||
typedef void (*vector_free_func)(void* wData);
|
|
||||||
typedef void *(*vector_alloc_func)(size_t newSize);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A simplified Vector template with aliases.
|
A simplified Vector template with aliases.
|
||||||
@@ -63,8 +60,6 @@ template <class T> class Vector {
|
|||||||
T* mData; ///< allocated data block, if any
|
T* mData; ///< allocated data block, if any
|
||||||
T* mStart; ///< start of useful data
|
T* mStart; ///< start of useful data
|
||||||
T* mEnd; ///< end of useful data + 1
|
T* mEnd; ///< end of useful data + 1
|
||||||
vector_alloc_func mAllocFunc; ///< function used to alloc new mData during resize.
|
|
||||||
vector_free_func mFreeFunc; ///< function used to free mData.
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -90,30 +85,13 @@ template <class T> class Vector {
|
|||||||
/** Change the size of the Vector, discarding content. */
|
/** Change the size of the Vector, discarding content. */
|
||||||
void resize(size_t newSize)
|
void resize(size_t newSize)
|
||||||
{
|
{
|
||||||
if (mData!=NULL) {
|
if (mData!=NULL) delete[] mData;
|
||||||
if (mFreeFunc)
|
|
||||||
mFreeFunc(mData);
|
|
||||||
else
|
|
||||||
delete[] mData;
|
|
||||||
}
|
|
||||||
if (newSize==0) mData=NULL;
|
if (newSize==0) mData=NULL;
|
||||||
else {
|
else mData = new T[newSize];
|
||||||
if (mAllocFunc)
|
|
||||||
mData = (T*) mAllocFunc(newSize);
|
|
||||||
else
|
|
||||||
mData = new T[newSize];
|
|
||||||
}
|
|
||||||
mStart = mData;
|
mStart = mData;
|
||||||
mEnd = mStart + newSize;
|
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. */
|
/** Release memory and clear pointers. */
|
||||||
void clear() { resize(0); }
|
void clear() { resize(0); }
|
||||||
|
|
||||||
@@ -122,7 +100,7 @@ template <class T> class Vector {
|
|||||||
void clone(const Vector<T>& other)
|
void clone(const Vector<T>& other)
|
||||||
{
|
{
|
||||||
resize(other.size());
|
resize(other.size());
|
||||||
other.copyTo(*this);
|
memcpy(mData,other.mStart,other.bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -131,31 +109,29 @@ template <class T> class Vector {
|
|||||||
//@{
|
//@{
|
||||||
|
|
||||||
/** Build an empty Vector of a given size. */
|
/** Build an empty Vector of a given size. */
|
||||||
Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
||||||
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
|
||||||
{ resize(wSize); }
|
|
||||||
|
|
||||||
/** Build a Vector by moving another. */
|
/** Build a Vector by shifting the data block. */
|
||||||
Vector(Vector<T>&& other)
|
Vector(Vector<T>& other)
|
||||||
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc)
|
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
||||||
{ other.mData=NULL; }
|
{ other.mData=NULL; }
|
||||||
|
|
||||||
/** Build a Vector by copying another. */
|
/** Build a Vector by copying another. */
|
||||||
Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); }
|
Vector(const Vector<T>& other):mData(NULL) { clone(other); }
|
||||||
|
|
||||||
/** Build a Vector with explicit values. */
|
/** Build a Vector with explicit values. */
|
||||||
Vector(T* wData, T* wStart, T* wEnd, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
Vector(T* wData, T* wStart, T* wEnd)
|
||||||
:mData(wData),mStart(wStart),mEnd(wEnd), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
:mData(wData),mStart(wStart),mEnd(wEnd)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/** Build a vector from an existing block, NOT to be deleted upon destruction. */
|
/** Build a vector from an existing block, NOT to be deleted upon destruction. */
|
||||||
Vector(T* wStart, size_t span, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
Vector(T* wStart, size_t span)
|
||||||
:mData(NULL),mStart(wStart),mEnd(wStart+span),mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
:mData(NULL),mStart(wStart),mEnd(wStart+span)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/** Build a Vector by concatenation. */
|
/** Build a Vector by concatenation. */
|
||||||
Vector(const Vector<T>& other1, const Vector<T>& other2, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
Vector(const Vector<T>& other1, const Vector<T>& other2)
|
||||||
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
:mData(NULL)
|
||||||
{
|
{
|
||||||
resize(other1.size()+other2.size());
|
resize(other1.size()+other2.size());
|
||||||
memcpy(mStart, other1.mStart, other1.bytes());
|
memcpy(mStart, other1.mStart, other1.bytes());
|
||||||
@@ -179,8 +155,6 @@ template <class T> class Vector {
|
|||||||
mData=other.mData;
|
mData=other.mData;
|
||||||
mStart=other.mStart;
|
mStart=other.mStart;
|
||||||
mEnd=other.mEnd;
|
mEnd=other.mEnd;
|
||||||
mAllocFunc=other.mAllocFunc;
|
|
||||||
mFreeFunc=other.mFreeFunc;
|
|
||||||
other.mData=NULL;
|
other.mData=NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,15 +197,10 @@ template <class T> class Vector {
|
|||||||
*/
|
*/
|
||||||
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
|
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
|
||||||
{
|
{
|
||||||
unsigned int i;
|
T* base = other.mStart + start;
|
||||||
T* dst = other.mStart + start;
|
assert(base+span<=other.mEnd);
|
||||||
T* src = mStart;
|
|
||||||
assert(dst+span<=other.mEnd);
|
|
||||||
assert(mStart+span<=mEnd);
|
assert(mStart+span<=mEnd);
|
||||||
for (i = 0; i < span; i++, src++, dst++)
|
memcpy(base,mStart,span*sizeof(T));
|
||||||
*dst = *src;
|
|
||||||
/*TODO if not non-trivially copyable type class, optimize:
|
|
||||||
memcpy(dst,mStart,span*sizeof(T)); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Copy all of this Vector to a segment of another Vector. */
|
/** Copy all of this Vector to a segment of another Vector. */
|
||||||
@@ -253,21 +222,6 @@ template <class T> class Vector {
|
|||||||
memcpy(other.mStart,base,span*sizeof(T));
|
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)
|
void fill(const T& val)
|
||||||
{
|
{
|
||||||
T* dp=mStart;
|
T* dp=mStart;
|
||||||
@@ -306,7 +260,7 @@ template <class T> class Vector {
|
|||||||
T* end() { return mEnd; }
|
T* end() { return mEnd; }
|
||||||
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
|
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,10 @@
|
|||||||
#include "Vector.h"
|
#include "Vector.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// We must have a gConfig now to include Vector.
|
||||||
|
#include "Configuration.h"
|
||||||
|
ConfigurationTable gConfig;
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
typedef Vector<int> TestVector;
|
typedef Vector<int> TestVector;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file contains structures used by both VTY (C, dir CommonLibs) and
|
|
||||||
* osmo-trx (CXX, dir Transceiver52)
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum FillerType {
|
|
||||||
FILLER_DUMMY,
|
|
||||||
FILLER_ZERO,
|
|
||||||
FILLER_NORM_RAND,
|
|
||||||
FILLER_EDGE_RAND,
|
|
||||||
FILLER_ACCESS_RAND,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ReferenceType {
|
|
||||||
REF_INTERNAL,
|
|
||||||
REF_EXTERNAL,
|
|
||||||
REF_GPS,
|
|
||||||
};
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#include <osmocom/core/logging.h>
|
|
||||||
#include <osmocom/core/utils.h>
|
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
/* default categories */
|
|
||||||
static const struct log_info_cat default_categories[] = {
|
|
||||||
[DMAIN] = {
|
|
||||||
.name = "DMAIN",
|
|
||||||
.description = "Main generic category",
|
|
||||||
.color = NULL,
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DTRXCTRL] = {
|
|
||||||
.name = "DTRXCTRL",
|
|
||||||
.description = "TRX CTRL interface",
|
|
||||||
.color = "\033[1;33m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DDEV] = {
|
|
||||||
.name = "DDEV",
|
|
||||||
.description = "Device/Driver specific code",
|
|
||||||
.color = NULL,
|
|
||||||
.enabled = 1, .loglevel = LOGL_INFO,
|
|
||||||
},
|
|
||||||
[DLMS] = {
|
|
||||||
.name = "DLMS",
|
|
||||||
.description = "Logging from within LimeSuite itself",
|
|
||||||
.color = NULL,
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct log_info log_info = {
|
|
||||||
.cat = default_categories,
|
|
||||||
.num_cat = ARRAY_SIZE(default_categories),
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
extern const struct log_info log_info;
|
|
||||||
|
|
||||||
/* Debug Areas of the code */
|
|
||||||
enum {
|
|
||||||
DMAIN,
|
|
||||||
DTRXCTRL,
|
|
||||||
DDEV,
|
|
||||||
DLMS,
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/* Generic signalling/notification infrastructure */
|
|
||||||
/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
|
||||||
*
|
|
||||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <osmocom/core/signal.h>
|
|
||||||
|
|
||||||
/* Signalling subsystems */
|
|
||||||
enum signal_subsystems {
|
|
||||||
SS_TRANSC,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* SS_TRANSC signals */
|
|
||||||
enum SS_TRANSC {
|
|
||||||
S_TRANSC_STOP_REQUIRED, /* Transceiver fatal error, it should be stopped */
|
|
||||||
};
|
|
||||||
154
CommonLibs/sqlite3util.cpp
Normal file
154
CommonLibs/sqlite3util.cpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
29
CommonLibs/sqlite3util.h
Normal file
29
CommonLibs/sqlite3util.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#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
|
||||||
@@ -1,603 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* 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 2 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 <string.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/utils.h>
|
|
||||||
#include <osmocom/core/rate_ctr.h>
|
|
||||||
|
|
||||||
#include <osmocom/vty/command.h>
|
|
||||||
#include <osmocom/vty/vty.h>
|
|
||||||
#include <osmocom/vty/misc.h>
|
|
||||||
|
|
||||||
#include "trx_vty.h"
|
|
||||||
#include "../config.h"
|
|
||||||
|
|
||||||
static struct trx_ctx* g_trx_ctx;
|
|
||||||
|
|
||||||
static const struct value_string clock_ref_names[] = {
|
|
||||||
{ REF_INTERNAL, "internal" },
|
|
||||||
{ REF_EXTERNAL, "external" },
|
|
||||||
{ REF_GPS, "gpsdo" },
|
|
||||||
{ 0, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct value_string filler_names[] = {
|
|
||||||
{ FILLER_DUMMY, "Dummy bursts" },
|
|
||||||
{ FILLER_ZERO, "Disabled" },
|
|
||||||
{ FILLER_NORM_RAND, "Normal bursts with random payload" },
|
|
||||||
{ FILLER_EDGE_RAND, "EDGE bursts with random payload" },
|
|
||||||
{ FILLER_ACCESS_RAND, "Access bursts with random payload" },
|
|
||||||
{ 0, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct trx_ctx *trx_from_vty(struct vty *v)
|
|
||||||
{
|
|
||||||
/* It can't hurt to force callers to continue to pass the vty instance
|
|
||||||
* to this function, in case we'd like to retrieve the global
|
|
||||||
* trx instance from the vty at some point in the future. But
|
|
||||||
* until then, just return the global pointer, which should have been
|
|
||||||
* initialized by trx_vty_init().
|
|
||||||
*/
|
|
||||||
OSMO_ASSERT(g_trx_ctx);
|
|
||||||
return g_trx_ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum trx_vty_node {
|
|
||||||
TRX_NODE = _LAST_OSMOVTY_NODE + 1,
|
|
||||||
CHAN_NODE,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct cmd_node trx_node = {
|
|
||||||
TRX_NODE,
|
|
||||||
"%s(config-trx)# ",
|
|
||||||
1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct cmd_node chan_node = {
|
|
||||||
CHAN_NODE,
|
|
||||||
"%s(config-trx-chan)# ",
|
|
||||||
1,
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFUN(cfg_trx, cfg_trx_cmd,
|
|
||||||
"trx",
|
|
||||||
"Configure the TRX\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
if (!trx)
|
|
||||||
return CMD_WARNING;
|
|
||||||
|
|
||||||
vty->node = TRX_NODE;
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
|
|
||||||
"bind-ip A.B.C.D",
|
|
||||||
"Set the IP address for the local bind\n"
|
|
||||||
"IPv4 Address\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
|
|
||||||
"remote-ip A.B.C.D",
|
|
||||||
"Set the IP address for the remote BTS\n"
|
|
||||||
"IPv4 Address\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_base_port, cfg_base_port_cmd,
|
|
||||||
"base-port <1-65535>",
|
|
||||||
"Set the TRX Base Port\n"
|
|
||||||
"TRX Base Port\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.base_port = atoi(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_dev_args, cfg_dev_args_cmd,
|
|
||||||
"dev-args DESC",
|
|
||||||
"Set the device-specific arguments to pass to the device\n"
|
|
||||||
"Device-specific arguments\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
|
|
||||||
"tx-sps (1|4)",
|
|
||||||
"Set the Tx Samples-per-Symbol\n"
|
|
||||||
"Tx Samples-per-Symbol\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.tx_sps = atoi(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
|
|
||||||
"rx-sps (1|4)",
|
|
||||||
"Set the Rx Samples-per-Symbol\n"
|
|
||||||
"Rx Samples-per-Symbol\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.rx_sps = atoi(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd,
|
|
||||||
"test rtsc <0-7>",
|
|
||||||
"Set the Random Normal Burst test mode with TSC\n"
|
|
||||||
"TSC\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
if (trx->cfg.rach_delay_set) {
|
|
||||||
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
|
|
||||||
VTY_NEWLINE);
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
trx->cfg.rtsc_set = true;
|
|
||||||
trx->cfg.rtsc = atoi(argv[0]);
|
|
||||||
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
|
|
||||||
trx->cfg.filler = FILLER_NORM_RAND;
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd,
|
|
||||||
"test rach-delay <0-68>",
|
|
||||||
"Set the Random Access Burst test mode with delay\n"
|
|
||||||
"RACH delay\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
if (trx->cfg.rtsc_set) {
|
|
||||||
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
|
|
||||||
VTY_NEWLINE);
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trx->cfg.egprs) {
|
|
||||||
vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s",
|
|
||||||
VTY_NEWLINE);
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
trx->cfg.rach_delay_set = true;
|
|
||||||
trx->cfg.rach_delay = atoi(argv[0]);
|
|
||||||
trx->cfg.filler = FILLER_ACCESS_RAND;
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
|
|
||||||
"clock-ref (internal|external|gpsdo)",
|
|
||||||
"Set the Reference Clock\n"
|
|
||||||
"Enable internal referece (default)\n"
|
|
||||||
"Enable external 10 MHz reference\n"
|
|
||||||
"Enable GPSDO reference\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
|
|
||||||
"multi-arfcn (disable|enable)",
|
|
||||||
"Enable multi-ARFCN transceiver (default=disable)\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
if (strcmp("disable", argv[0]) == 0) {
|
|
||||||
trx->cfg.multi_arfcn = false;
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trx->cfg.num_chans > TRX_MCHAN_MAX) {
|
|
||||||
vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s",
|
|
||||||
TRX_MCHAN_MAX, VTY_NEWLINE);
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
trx->cfg.multi_arfcn = true;
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_offset, cfg_offset_cmd,
|
|
||||||
"offset FLOAT",
|
|
||||||
"Set the baseband frequency offset (default=0, auto)\n"
|
|
||||||
"Baseband Frequency Offset\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.offset = atof(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd,
|
|
||||||
"rssi-offset FLOAT",
|
|
||||||
"Set the RSSI to dBm offset in dB (default=0)\n"
|
|
||||||
"RSSI to dBm offset in dB\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.rssi_offset = atof(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
|
|
||||||
"swap-channels (disable|enable)",
|
|
||||||
"Swap channels (default=disable)\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
if (strcmp("disable", argv[0]) == 0) {
|
|
||||||
trx->cfg.swap_channels = false;
|
|
||||||
} else if (strcmp("enable", argv[0]) == 0) {
|
|
||||||
trx->cfg.swap_channels = true;
|
|
||||||
} else {
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_egprs, cfg_egprs_cmd,
|
|
||||||
"egprs (disable|enable)",
|
|
||||||
"Enable EDGE receiver (default=disable)\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
if (strcmp("disable", argv[0]) == 0) {
|
|
||||||
trx->cfg.egprs = false;
|
|
||||||
} else if (strcmp("enable", argv[0]) == 0) {
|
|
||||||
trx->cfg.egprs = true;
|
|
||||||
trx->cfg.filler = FILLER_EDGE_RAND;
|
|
||||||
} else {
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
|
|
||||||
"ext-rach (disable|enable)",
|
|
||||||
"Enable extended (11-bit) RACH (default=disable)\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
if (strcmp("disable", argv[0]) == 0)
|
|
||||||
trx->cfg.ext_rach = false;
|
|
||||||
|
|
||||||
if (strcmp("enable", argv[0]) == 0)
|
|
||||||
trx->cfg.ext_rach = true;
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
|
|
||||||
"rt-prio <1-32>",
|
|
||||||
"Set the SCHED_RR real-time priority\n"
|
|
||||||
"Real time priority\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.sched_rr = atoi(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_filler, cfg_filler_cmd,
|
|
||||||
"filler dummy",
|
|
||||||
"Enable C0 filler table\n"
|
|
||||||
"Dummy method\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.filler = FILLER_DUMMY;
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_chan, cfg_chan_cmd,
|
|
||||||
"chan <0-100>",
|
|
||||||
"Select a channel to configure\n"
|
|
||||||
"Channel index\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
int idx = atoi(argv[0]);
|
|
||||||
|
|
||||||
if (idx >= TRX_CHAN_MAX) {
|
|
||||||
vty_out(vty, "Chan list full.%s", VTY_NEWLINE);
|
|
||||||
return CMD_WARNING;
|
|
||||||
} else if (trx->cfg.multi_arfcn && trx->cfg.num_chans >= TRX_MCHAN_MAX) {
|
|
||||||
vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s",
|
|
||||||
TRX_MCHAN_MAX, VTY_NEWLINE);
|
|
||||||
return CMD_WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */
|
|
||||||
vty_out(vty, "Non-existent or non-consecutive chan %d.%s",
|
|
||||||
idx, VTY_NEWLINE);
|
|
||||||
return CMD_WARNING;
|
|
||||||
} else if (trx->cfg.num_chans == idx) { /* creating it */
|
|
||||||
trx->cfg.num_chans++;
|
|
||||||
trx->cfg.chans[idx].trx = trx;
|
|
||||||
trx->cfg.chans[idx].idx = idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
vty->node = CHAN_NODE;
|
|
||||||
vty->index = &trx->cfg.chans[idx];
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd,
|
|
||||||
"rx-path NAME",
|
|
||||||
"Set the Rx Path\n"
|
|
||||||
"Rx Path name\n")
|
|
||||||
{
|
|
||||||
struct trx_chan *chan = vty->index;
|
|
||||||
|
|
||||||
osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd,
|
|
||||||
"tx-path NAME",
|
|
||||||
"Set the Tx Path\n"
|
|
||||||
"Tx Path name\n")
|
|
||||||
{
|
|
||||||
struct trx_chan *chan = vty->index;
|
|
||||||
|
|
||||||
osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dummy_config_write(struct vty *v)
|
|
||||||
{
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int config_write_trx(struct vty *vty)
|
|
||||||
{
|
|
||||||
struct trx_chan *chan;
|
|
||||||
int i;
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
vty_out(vty, "trx%s", VTY_NEWLINE);
|
|
||||||
if (trx->cfg.bind_addr)
|
|
||||||
vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.remote_addr)
|
|
||||||
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.base_port != DEFAULT_TRX_PORT)
|
|
||||||
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.dev_args)
|
|
||||||
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
|
|
||||||
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.rx_sps != DEFAULT_RX_SPS)
|
|
||||||
vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.rtsc_set)
|
|
||||||
vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.rach_delay_set)
|
|
||||||
vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.clock_ref != REF_INTERNAL)
|
|
||||||
vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
|
|
||||||
vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE);
|
|
||||||
if (trx->cfg.offset != 0)
|
|
||||||
vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.rssi_offset != 0)
|
|
||||||
vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " ext-rach %s%s", trx->cfg.ext_rach ? "enable" : "disable", VTY_NEWLINE);
|
|
||||||
if (trx->cfg.sched_rr != 0)
|
|
||||||
vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE);
|
|
||||||
|
|
||||||
for (i = 0; i < trx->cfg.num_chans; i++) {
|
|
||||||
chan = &trx->cfg.chans[i];
|
|
||||||
vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE);
|
|
||||||
if (chan->rx_path)
|
|
||||||
vty_out(vty, " rx-path %s%s", chan->rx_path, VTY_NEWLINE);
|
|
||||||
if (chan->tx_path)
|
|
||||||
vty_out(vty, " tx-path %s%s", chan->tx_path, VTY_NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
|
|
||||||
{
|
|
||||||
struct trx_chan *chan;
|
|
||||||
int i;
|
|
||||||
vty_out(vty, "TRX Config:%s", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc,
|
|
||||||
trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay,
|
|
||||||
trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Extended RACH support: %s%s", trx->cfg.ext_rach ? "Enabled" : "Disabled", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr,
|
|
||||||
trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE);
|
|
||||||
vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE);
|
|
||||||
for (i = 0; i < trx->cfg.num_chans; i++) {
|
|
||||||
chan = &trx->cfg.chans[i];
|
|
||||||
vty_out(vty, " Channel %u:%s", chan->idx, VTY_NEWLINE);
|
|
||||||
if (chan->rx_path)
|
|
||||||
vty_out(vty, " Rx Path: %s%s", chan->rx_path, VTY_NEWLINE);
|
|
||||||
if (chan->tx_path)
|
|
||||||
vty_out(vty, " Tx Path: %s%s", chan->tx_path, VTY_NEWLINE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(show_trx, show_trx_cmd,
|
|
||||||
"show trx",
|
|
||||||
SHOW_STR "Display information on the TRX\n")
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx_dump_vty(vty, trx);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int trx_vty_is_config_node(struct vty *vty, int node)
|
|
||||||
{
|
|
||||||
switch (node) {
|
|
||||||
case TRX_NODE:
|
|
||||||
case CHAN_NODE:
|
|
||||||
return 1;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int trx_vty_go_parent(struct vty *vty)
|
|
||||||
{
|
|
||||||
switch (vty->node) {
|
|
||||||
case TRX_NODE:
|
|
||||||
vty->node = CONFIG_NODE;
|
|
||||||
vty->index = NULL;
|
|
||||||
vty->index_sub = NULL;
|
|
||||||
break;
|
|
||||||
case CHAN_NODE:
|
|
||||||
vty->node = TRX_NODE;
|
|
||||||
vty->index = NULL;
|
|
||||||
vty->index_sub = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
vty->node = CONFIG_NODE;
|
|
||||||
vty->index = NULL;
|
|
||||||
vty->index_sub = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return vty->node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char trx_copyright[] =
|
|
||||||
"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n"
|
|
||||||
"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n"
|
|
||||||
"Copyright (C) 2015 Ettus Research LLC\r\n"
|
|
||||||
"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
|
|
||||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
|
||||||
"This is free software: you are free to change and redistribute it.\r\n"
|
|
||||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
|
||||||
|
|
||||||
struct vty_app_info g_vty_info = {
|
|
||||||
.name = "OsmoTRX",
|
|
||||||
.version = PACKAGE_VERSION,
|
|
||||||
.copyright = trx_copyright,
|
|
||||||
.go_parent_cb = trx_vty_go_parent,
|
|
||||||
.is_config_node = trx_vty_is_config_node,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
|
|
||||||
{
|
|
||||||
struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx);
|
|
||||||
|
|
||||||
trx->cfg.bind_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
|
|
||||||
trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
|
|
||||||
trx->cfg.base_port = DEFAULT_TRX_PORT;
|
|
||||||
trx->cfg.tx_sps = DEFAULT_TX_SPS;
|
|
||||||
trx->cfg.rx_sps = DEFAULT_RX_SPS;
|
|
||||||
trx->cfg.filler = FILLER_ZERO;
|
|
||||||
|
|
||||||
return trx;
|
|
||||||
}
|
|
||||||
|
|
||||||
int trx_vty_init(struct trx_ctx* trx)
|
|
||||||
{
|
|
||||||
g_trx_ctx = trx;
|
|
||||||
install_element_ve(&show_trx_cmd);
|
|
||||||
|
|
||||||
install_element(CONFIG_NODE, &cfg_trx_cmd);
|
|
||||||
|
|
||||||
install_node(&trx_node, config_write_trx);
|
|
||||||
install_element(TRX_NODE, &cfg_bind_ip_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_remote_ip_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_base_port_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_dev_args_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_tx_sps_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_rx_sps_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_test_rtsc_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_test_rach_delay_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_clock_ref_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_multi_arfcn_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_offset_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_rssi_offset_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_swap_channels_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_egprs_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_ext_rach_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_rt_prio_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_filler_cmd);
|
|
||||||
|
|
||||||
install_element(TRX_NODE, &cfg_chan_cmd);
|
|
||||||
install_node(&chan_node, dummy_config_write);
|
|
||||||
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
|
|
||||||
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <osmocom/vty/command.h>
|
|
||||||
|
|
||||||
#include "config_defs.h"
|
|
||||||
|
|
||||||
extern struct vty_app_info g_vty_info;
|
|
||||||
|
|
||||||
/* Maximum number of physical RF channels */
|
|
||||||
#define TRX_CHAN_MAX 8
|
|
||||||
/* Maximum number of carriers in multi-ARFCN mode */
|
|
||||||
#define TRX_MCHAN_MAX 3
|
|
||||||
|
|
||||||
/* Samples-per-symbol for downlink path
|
|
||||||
* 4 - Uses precision modulator (more computation, less distortion)
|
|
||||||
* 1 - Uses minimized modulator (less computation, more distortion)
|
|
||||||
*
|
|
||||||
* Other values are invalid. Receive path (uplink) is always
|
|
||||||
* downsampled to 1 sps. Default to 4 sps for all cases.
|
|
||||||
*/
|
|
||||||
#define DEFAULT_TX_SPS 4
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Samples-per-symbol for uplink (receiver) path
|
|
||||||
* Do not modify this value. EDGE configures 4 sps automatically on
|
|
||||||
* B200/B210 devices only. Use of 4 sps on the receive path for other
|
|
||||||
* configurations is not supported.
|
|
||||||
*/
|
|
||||||
#define DEFAULT_RX_SPS 1
|
|
||||||
|
|
||||||
/* Default configuration parameters */
|
|
||||||
#define DEFAULT_TRX_PORT 5700
|
|
||||||
#define DEFAULT_TRX_IP "127.0.0.1"
|
|
||||||
#define DEFAULT_CHANS 1
|
|
||||||
|
|
||||||
struct trx_ctx;
|
|
||||||
|
|
||||||
struct trx_chan {
|
|
||||||
struct trx_ctx *trx; /* backpointer */
|
|
||||||
unsigned int idx; /* channel index */
|
|
||||||
char *rx_path;
|
|
||||||
char *tx_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct trx_ctx {
|
|
||||||
struct {
|
|
||||||
char *bind_addr;
|
|
||||||
char *remote_addr;
|
|
||||||
char *dev_args;
|
|
||||||
unsigned int base_port;
|
|
||||||
unsigned int tx_sps;
|
|
||||||
unsigned int rx_sps;
|
|
||||||
unsigned int rtsc;
|
|
||||||
bool rtsc_set;
|
|
||||||
unsigned int rach_delay;
|
|
||||||
bool rach_delay_set;
|
|
||||||
enum ReferenceType clock_ref;
|
|
||||||
enum FillerType filler;
|
|
||||||
bool multi_arfcn;
|
|
||||||
double offset;
|
|
||||||
double rssi_offset;
|
|
||||||
bool swap_channels;
|
|
||||||
bool ext_rach;
|
|
||||||
bool egprs;
|
|
||||||
unsigned int sched_rr;
|
|
||||||
unsigned int num_chans;
|
|
||||||
struct trx_chan chans[TRX_CHAN_MAX];
|
|
||||||
} cfg;
|
|
||||||
};
|
|
||||||
|
|
||||||
int trx_vty_init(struct trx_ctx* trx);
|
|
||||||
struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx);
|
|
||||||
@@ -41,26 +41,11 @@ const BitVector GSM::gTrainingSequence[] = {
|
|||||||
BitVector("11101111000100101110111100"),
|
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::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
||||||
|
|
||||||
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
|
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
|
||||||
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
|
|
||||||
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
|
|
||||||
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
|
|
||||||
|
|
||||||
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011");
|
||||||
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
|
||||||
|
|
||||||
|
|
||||||
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
|
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
|
||||||
|
|||||||
@@ -46,18 +46,15 @@ namespace GSM {
|
|||||||
|
|
||||||
/** GSM Training sequences from GSM 05.02 5.2.3. */
|
/** GSM Training sequences from GSM 05.02 5.2.3. */
|
||||||
extern const BitVector gTrainingSequence[];
|
extern const BitVector gTrainingSequence[];
|
||||||
extern const BitVector gEdgeTrainingSequence[];
|
|
||||||
|
|
||||||
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
|
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
|
||||||
extern const BitVector gDummyBurst;
|
extern const BitVector gDummyBurst;
|
||||||
|
|
||||||
/** Random access burst synch. sequence */
|
/** Random access burst synch. sequence */
|
||||||
extern const BitVector gRACHSynchSequenceTS0;
|
extern const BitVector gRACHSynchSequence;
|
||||||
extern const BitVector gRACHSynchSequenceTS1;
|
|
||||||
extern const BitVector gRACHSynchSequenceTS2;
|
|
||||||
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
|
||||||
extern const BitVector gRACHBurst;
|
|
||||||
|
|
||||||
|
/** Synchronization burst sync sequence */
|
||||||
|
extern const BitVector gSCHSynchSequence;
|
||||||
|
|
||||||
/**@name Modulus operations for frame numbers. */
|
/**@name Modulus operations for frame numbers. */
|
||||||
//@{
|
//@{
|
||||||
|
|||||||
22
INSTALLATION
22
INSTALLATION
@@ -2,18 +2,32 @@ Installation Requirements
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
osmo-trx compiles to a simple Unix binary and does not require special
|
OpenBTS compiles to a simple Unix binary and does not require special
|
||||||
installation.
|
installation.
|
||||||
|
|
||||||
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
|
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
|
||||||
running configure.
|
running configure.
|
||||||
|
|
||||||
To run osmo-trx, the following should be installed:
|
To run OpenBTS, the following should be installed:
|
||||||
libuhd (https://gnuradio.org).
|
|
||||||
|
Asterisk (http://www.asterisk.org), running SIP on port 5060.
|
||||||
|
|
||||||
|
libosip2 (http://www.gnu.org/software/osip/)
|
||||||
|
|
||||||
|
libortp (http://freshmeat.net/projects/ortp/)
|
||||||
|
|
||||||
|
libusrp (http://gnuradio.org).
|
||||||
This is part of the GNURadio installation.
|
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
|
For information on specific executables, see tests/README.tests and
|
||||||
apps/README.apps.
|
apps/README.apps.
|
||||||
|
|
||||||
See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
|
See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
|
|||||||
20
LEGAL
20
LEGAL
@@ -1,8 +1,5 @@
|
|||||||
OpenBTS
|
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.
|
Most parts copyright 2008-2011 Free Software Foundation.
|
||||||
Some parts copyright 2010 Kestrel Signal Processing, Inc.
|
Some parts copyright 2010 Kestrel Signal Processing, Inc.
|
||||||
Some parts copyright 2011 Range Networks, Inc.
|
Some parts copyright 2011 Range Networks, Inc.
|
||||||
@@ -15,9 +12,17 @@ patented technologies. The user of this software is required to take whatever
|
|||||||
actions are necessary to avoid patent infringement.
|
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
|
Telecom and Radio Spectrum Laws
|
||||||
|
|
||||||
The primary function of OsmoTRX is the provision of telecommunications service
|
The primary function of OpenBTS is the provision of telecommunications service
|
||||||
over a radio link. This activity is heavily regulated nearly everywhere in
|
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
|
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.
|
regulations in the jurisdictions where this sortware is used with radio equipment.
|
||||||
@@ -34,7 +39,7 @@ The legal restrictions listed here are not necessarily exhaustive.
|
|||||||
|
|
||||||
Note to US Government Users
|
Note to US Government Users
|
||||||
|
|
||||||
The OsmoTRX software applications and associated documentation are "Commercial
|
The OpenBTS software applications and associated documentation are "Commercial
|
||||||
Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of
|
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,"
|
"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
|
as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
|
||||||
@@ -49,12 +54,13 @@ and AGPLv3.
|
|||||||
Note to US Government Contractors
|
Note to US Government Contractors
|
||||||
|
|
||||||
GPL is not compatible with "government purpose rights" (GPR). If you receive
|
GPL is not compatible with "government purpose rights" (GPR). If you receive
|
||||||
OsmoTRX software under a GPL and deliver it under GPR, you will be in violation
|
OpenBTS 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
|
of GPL and possibly subject to enforcement actions by the original authors and
|
||||||
copyright holders, including the Free Software Foundation, Inc.
|
copyright holders, including the Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
|
||||||
Software Licensing and Distribution
|
Software Licensing and Distribution
|
||||||
|
|
||||||
The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file
|
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
|
||||||
for more information on the license for this distribution.
|
for more information on the license for this distribution.
|
||||||
|
|||||||
18
Makefile.am
18
Makefile.am
@@ -22,18 +22,16 @@ include $(top_srcdir)/Makefile.common
|
|||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I config
|
ACLOCAL_AMFLAGS = -I config
|
||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
||||||
AM_CXXFLAGS = -Wall -pthread
|
AM_CXXFLAGS = -Wall -pthread -ldl
|
||||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
|
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
|
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||||
|
|
||||||
# Order must be preserved
|
# Order must be preserved
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
doc \
|
sqlite3 \
|
||||||
CommonLibs \
|
CommonLibs \
|
||||||
GSM \
|
GSM \
|
||||||
Transceiver52M \
|
Transceiver52M
|
||||||
contrib \
|
|
||||||
tests
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
autogen.sh \
|
autogen.sh \
|
||||||
@@ -42,12 +40,6 @@ EXTRA_DIST = \
|
|||||||
COPYING \
|
COPYING \
|
||||||
README
|
README
|
||||||
|
|
||||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
|
||||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
|
||||||
|
|
||||||
.PHONY: release
|
|
||||||
|
|
||||||
@RELMAKE@
|
|
||||||
|
|
||||||
dox: FORCE
|
dox: FORCE
|
||||||
doxygen doxconfig
|
doxygen doxconfig
|
||||||
|
|||||||
@@ -18,21 +18,21 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
top_srcdir = $(abs_top_srcdir)
|
||||||
|
top_builddir = $(abs_top_builddir)
|
||||||
|
|
||||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
||||||
|
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
|
||||||
|
|
||||||
STD_DEFINES_AND_INCLUDES = \
|
STD_DEFINES_AND_INCLUDES = \
|
||||||
$(SVNDEV) \
|
$(SVNDEV) \
|
||||||
-I$(COMMON_INCLUDEDIR) \
|
-I$(COMMON_INCLUDEDIR) \
|
||||||
-I$(GSM_INCLUDEDIR)
|
-I$(GSM_INCLUDEDIR) \
|
||||||
|
-I$(SQLITE_INCLUDEDIR)
|
||||||
|
|
||||||
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
||||||
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
||||||
|
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl
|
||||||
if ARCH_ARM
|
|
||||||
ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la
|
|
||||||
else
|
|
||||||
ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la
|
|
||||||
endif
|
|
||||||
|
|
||||||
MOSTLYCLEANFILES = *~
|
MOSTLYCLEANFILES = *~
|
||||||
|
|||||||
260
README
260
README
@@ -1,116 +1,168 @@
|
|||||||
This is the interface to the transcevier.
|
Welcome to the OpenBTS source code.
|
||||||
|
|
||||||
Each TRX Manager UDP socket interface represents a single ARFCN.
|
|
||||||
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
|
For free support, please subscribe to openbts-discuss@lists.sourceforge.net.
|
||||||
Give a base port B (5700), the master clock interface is at port P=B.
|
See http://sourceforge.net/mailarchive/forum.php?forum_name=openbts-discuss
|
||||||
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
|
and https://lists.sourceforge.net/lists/listinfo/openbts-discuss for details.
|
||||||
The corresponding core-side interface for every socket is at P+100.
|
|
||||||
For any given build, the number of ARFCN interfaces can be fixed.
|
For additional information, refer to http://openbts.org.
|
||||||
|
|
||||||
|
|
||||||
|
These are the directories:
|
||||||
|
|
||||||
|
AsteriskConfig Asterisk configuration files for use with OpenBTS.
|
||||||
|
CommonLib Common-use libraries, mostly C++ wrappers for basic facilities.
|
||||||
|
Control Control-layer functions for the protocols of GSM 04.08 and SIP.
|
||||||
|
GSM The GSM stack.
|
||||||
|
SIP Components of the SIP state machines ued by the control layer.
|
||||||
|
SMS The SMS stack.
|
||||||
|
SR The subscriber registry.
|
||||||
|
TRXManager The interface between the GSM stack and the radio.
|
||||||
|
Transceiver The software transceiver and specific installation tests.
|
||||||
|
apps OpenBTS application binaries.
|
||||||
|
doc Project documentation.
|
||||||
|
tests Test fixtures for subsets of OpenBTS components.
|
||||||
|
smqueue RFC-3428 store-and-forward server for SMS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Indications on the Master Clock Interface
|
By default, OpenBTS assumes the following UDP port assignments:
|
||||||
|
|
||||||
The master clock interface is output only (from the radio).
|
5060 -- Asterisk SIP interface
|
||||||
Messages are "indications".
|
5061 -- local SIP softphone
|
||||||
|
5062 -- OpenBTS SIP interface
|
||||||
|
5063 -- smqueue SIP interface
|
||||||
|
5064 -- subscriber registry SIP interface
|
||||||
|
5700-range -- OpenBTS-transceiver interface
|
||||||
|
|
||||||
CLOCK gives the current value of the transceiver clock to be used by the core.
|
These can be controlled in the CONFIG table in /etc/OpenBTS.db.
|
||||||
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
|
|
||||||
IND CLOCK <totalFrames>
|
|
||||||
|
|
||||||
|
Standrd paths:
|
||||||
|
/OpenBTS -- Binary installation.
|
||||||
|
/etc/OpenBTS -- Configuration databases.
|
||||||
|
/var/run/OpenBTS -- Real-time reporting databases.
|
||||||
|
|
||||||
|
The script apps/setUpFiles.sh will create these directories and install the
|
||||||
Commands on the Per-ARFCN Control Interface
|
correct files in them.
|
||||||
|
|
||||||
The per-ARFCN control interface uses a command-reponse protocol.
|
|
||||||
Commands are NULL-terminated ASCII strings, one per UDP socket.
|
|
||||||
Each command has a corresponding response.
|
|
||||||
Every command is of the form:
|
|
||||||
|
|
||||||
CMD <cmdtype> [params]
|
|
||||||
|
|
||||||
The <cmdtype> is the actual command.
|
|
||||||
Parameters are optional depending on the commands type.
|
|
||||||
Every response is of the form:
|
|
||||||
|
|
||||||
RSP <cmdtype> <status> [result]
|
|
||||||
|
|
||||||
The <status> is 0 for success and a non-zero error code for failure.
|
|
||||||
Successful responses may include results, depending on the command type.
|
|
||||||
|
|
||||||
|
|
||||||
Power Control
|
|
||||||
|
|
||||||
POWEROFF shuts off transmitter power and stops the demodulator.
|
|
||||||
CMD POWEROFF
|
|
||||||
RSP POWEROFF <status>
|
|
||||||
|
|
||||||
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
|
|
||||||
This command fails if the transmitter and receiver are not yet tuned.
|
|
||||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
|
||||||
If the transceiver is already on, it response with success to this command.
|
|
||||||
CMD POWERON
|
|
||||||
RSP POWERON <status>
|
|
||||||
|
|
||||||
SETPOWER sets output power in dB wrt full scale.
|
|
||||||
This command fails if the transmitter and receiver are not running.
|
|
||||||
CMD SETPOWER <dB>
|
|
||||||
RSP SETPOWER <status> <dB>
|
|
||||||
|
|
||||||
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
|
|
||||||
This command fails if the transmitter and receiver are not running.
|
|
||||||
CMD ADJPOWER <dBStep>
|
|
||||||
RSP ADJPOWER <status> <dBLevel>
|
|
||||||
|
|
||||||
|
|
||||||
Tuning Control
|
|
||||||
|
|
||||||
RXTUNE tunes the receiver to a given frequency in kHz.
|
|
||||||
This command fails if the receiver is already running.
|
|
||||||
(To re-tune you stop the radio, re-tune, and restart.)
|
|
||||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
|
||||||
CMD RXTUNE <kHz>
|
|
||||||
RSP RXTUNE <status> <kHz>
|
|
||||||
|
|
||||||
TXTUNE tunes the transmitter to a given frequency in kHz.
|
|
||||||
This command fails if the transmitter is already running.
|
|
||||||
(To re-tune you stop the radio, re-tune, and restart.)
|
|
||||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
|
||||||
CMD TXTUNE <kHz>
|
|
||||||
RSP TXTUNE <status> <kHz>
|
|
||||||
|
|
||||||
|
|
||||||
Timeslot Control
|
|
||||||
|
|
||||||
SETSLOT sets the format of the uplink timeslots in the ARFCN.
|
|
||||||
The <timeslot> indicates the timeslot of interest.
|
|
||||||
The <chantype> indicates the type of channel that occupies the timeslot.
|
|
||||||
A chantype of zero indicates the timeslot is off.
|
|
||||||
CMD SETSLOT <timeslot> <chantype>
|
|
||||||
RSP SETSLOT <status> <timeslot> <chantype>
|
|
||||||
|
|
||||||
|
|
||||||
Messages on the per-ARFCN Data Interface
|
|
||||||
|
|
||||||
Messages on the data interface carry one radio burst per UDP message.
|
|
||||||
|
|
||||||
|
|
||||||
Received Data Burst
|
|
||||||
|
|
||||||
1 byte timeslot index
|
|
||||||
4 bytes GSM frame number, big endian
|
|
||||||
1 byte RSSI in -dBm
|
|
||||||
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
|
|
||||||
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
|
|
||||||
|
|
||||||
|
|
||||||
Transmit Data Burst
|
|
||||||
|
|
||||||
1 byte timeslot index
|
|
||||||
4 bytes GSM frame number, big endian
|
|
||||||
1 byte transmit level wrt ARFCN max, -dB (attenuation)
|
|
||||||
148 bytes output symbol values, 0 & 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Release history:
|
||||||
|
|
||||||
|
Release Name SVN Reposiory SVN Rev Comments
|
||||||
|
|
||||||
|
1.0 (none) SF.net ?? completed L1, L2
|
||||||
|
|
||||||
|
1.1 Arnaudville GNU Radio r10019 (trunk)
|
||||||
|
|
||||||
|
1.2 Breaux Bridge GNU Radio r10088 (trunk) GNU Build, very early assignment
|
||||||
|
|
||||||
|
1.3 Carencro KSP r1 (trunk) first post-injunction release
|
||||||
|
|
||||||
|
1.4 Donaldsonville KSP r23 (trunk) fixed Ubuntu build error
|
||||||
|
|
||||||
|
1.5 Eunice KSP r39 (trunk) fixed L2 bugs related to segmentation
|
||||||
|
removed incomplete SMS directory
|
||||||
|
moved "abort" calls into L3 subclasses
|
||||||
|
|
||||||
|
1.6 New Iberia KSP r130 (trunk) import of all 2.2 improvements to non-SMS release
|
||||||
|
|
||||||
|
|
||||||
|
2.0 St. Francisville KSP r54 (smswork) SMS support
|
||||||
|
file-based configuration
|
||||||
|
|
||||||
|
2.1 Grand Coteau KSP r70 (smswork) DTMF support
|
||||||
|
fixed more Linux-related build errors
|
||||||
|
-lpthread
|
||||||
|
TLMessage constructor
|
||||||
|
expanded stack to prevent overflows in Linux
|
||||||
|
moved gSIPInterface to main app
|
||||||
|
fixed iterator bug in Pager
|
||||||
|
|
||||||
|
2.2 Houma KSP r122 (smswork) added LEGAL notice
|
||||||
|
removed Assert classes
|
||||||
|
stop paging on page response
|
||||||
|
fixed Pager-spin bug
|
||||||
|
fixed Transceiver spin bugs
|
||||||
|
fixed 2^32 microsecond rollover bug
|
||||||
|
reduced stack footprints in Transceiver
|
||||||
|
fixed SMS timestamps
|
||||||
|
check LAI before using TMSI in LUR
|
||||||
|
reduced memory requirement by 75%
|
||||||
|
removed PagerTest
|
||||||
|
fixed stale-transaction bug in paging handler
|
||||||
|
fixed USRP clock rollover bug
|
||||||
|
faster call connection
|
||||||
|
new USRPDevice design
|
||||||
|
|
||||||
|
2.3 Jean Lafitte KSP r190? (trunk) check for out-of-date RACH bursts
|
||||||
|
better TRX-GSM clock sync
|
||||||
|
formal logging system
|
||||||
|
command line interface
|
||||||
|
emergency call setup
|
||||||
|
|
||||||
|
2.4 Kinder KSP r208? (trunk) fixed BCCH neighbor list bug
|
||||||
|
support for neighbor lists
|
||||||
|
fixed support for non-local Asterisk servers
|
||||||
|
cleaner configuration management
|
||||||
|
more realtime control of BCCH parameters
|
||||||
|
proper rejection of Hold messages
|
||||||
|
fixed L3 hanging bug in MTDCheckBYE
|
||||||
|
|
||||||
|
2.4.1 Kinder KSP r462 fixed lots of valgrind errors
|
||||||
|
|
||||||
|
2.4.2 Kinder KSP r482 zero-length calling party number bug
|
||||||
|
g++ 4.4 #includes
|
||||||
|
|
||||||
|
2.5 Lacassine KSP r551 imported Joshua Lackey patches
|
||||||
|
SIP fixes from Anne Kwong
|
||||||
|
SIP fixes from testing with SMS server
|
||||||
|
L3 TI handling fixes
|
||||||
|
SMS server support
|
||||||
|
GNU Radio 3.2 compatibility
|
||||||
|
configurable max range and LU-reject cause
|
||||||
|
"page" & "testcall" CLI features
|
||||||
|
|
||||||
|
2.5.1 Lacassine KSP r595 fixed some build bugs for some Linux distros
|
||||||
|
|
||||||
|
2.5.2 Lacassine KSP r630 fixed channel assignment bug for Nokia DCT4+ handsets
|
||||||
|
|
||||||
|
2.5.3 Lacassine KSP r756 merged fix for transceiver startup crash
|
||||||
|
due to use of uninitialized variables (r646)
|
||||||
|
merged fix for fusb bug from trunk (r582)
|
||||||
|
|
||||||
|
2.5.4 Lacassine KSP r812 merged fixes to build under latest Fedora and
|
||||||
|
to build with git GnuRadio (r814)
|
||||||
|
|
||||||
|
2.6 Mamou KSP r886 fixed infamous fusb bug (r582)
|
||||||
|
fixed idle-filling table size bug
|
||||||
|
smoother uplink power control
|
||||||
|
load-limiting downlink power control
|
||||||
|
new "config" features (optional, static)
|
||||||
|
IMEI interrogation
|
||||||
|
fixed MOD "missing FIFO" bug
|
||||||
|
configurable short code features
|
||||||
|
fixed transceiver startup crash (r646)
|
||||||
|
readline support is back
|
||||||
|
fixed timing advance bug (r844)
|
||||||
|
added CLI "chans" command
|
||||||
|
track time-of-use in TMSI table (r844)
|
||||||
|
added CLI "noise" command (r844)
|
||||||
|
added CLI "rxpower" command (r844)
|
||||||
|
added CLI "unconfig" command
|
||||||
|
|
||||||
|
2.7 Natchitoches Range rxxx (never released publicly)
|
||||||
|
converted TMSITable to sqlite3 (r902)
|
||||||
|
sqlite3-based configuration (r???)
|
||||||
|
converted Logger to syslogd (r903)
|
||||||
|
added support for rest octets (r1022)
|
||||||
|
external database for transaction reporting (r1184)
|
||||||
|
external database for channel status reporting (r1203)
|
||||||
|
in-call delivery and submission of text messages (r1231)
|
||||||
|
RFC-2833 DMTF (r1249)
|
||||||
|
|
||||||
|
2.8 Opelousas Range rxxx move databases to /etc and /var
|
||||||
|
RRLP aiding support
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "fft.h"
|
|
||||||
#include "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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#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_ */
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "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);
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#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,10 +21,22 @@
|
|||||||
|
|
||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
SUBDIRS = arch device
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I./common
|
||||||
|
AM_CXXFLAGS = -ldl -lpthread
|
||||||
|
|
||||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common
|
SUBDIRS = arm x86
|
||||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
|
||||||
|
if ARCH_ARM
|
||||||
|
ARCH_LA = arm/libarch.la
|
||||||
|
else
|
||||||
|
ARCH_LA = x86/libarch.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if USRP1
|
||||||
|
AM_CPPFLAGS += $(USRP_CFLAGS)
|
||||||
|
else
|
||||||
|
AM_CPPFLAGS += $(UHD_CFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
rev2dir = $(datadir)/usrp/rev2
|
rev2dir = $(datadir)/usrp/rev2
|
||||||
rev4dir = $(datadir)/usrp/rev4
|
rev4dir = $(datadir)/usrp/rev4
|
||||||
@@ -32,80 +44,58 @@ rev4dir = $(datadir)/usrp/rev4
|
|||||||
dist_rev2_DATA = std_inband.rbf
|
dist_rev2_DATA = std_inband.rbf
|
||||||
dist_rev4_DATA = std_inband.rbf
|
dist_rev4_DATA = std_inband.rbf
|
||||||
|
|
||||||
EXTRA_DIST = README
|
EXTRA_DIST = \
|
||||||
|
README \
|
||||||
|
README.Talgorithm
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libtransceiver_common.la
|
noinst_LTLIBRARIES = libtransceiver.la
|
||||||
|
|
||||||
COMMON_SOURCES = \
|
COMMON_SOURCES = \
|
||||||
radioInterface.cpp \
|
radioInterface.cpp \
|
||||||
radioVector.cpp \
|
radioVector.cpp \
|
||||||
radioClock.cpp \
|
radioClock.cpp \
|
||||||
radioBuffer.cpp \
|
|
||||||
sigProcLib.cpp \
|
sigProcLib.cpp \
|
||||||
signalVector.cpp \
|
signalVector.cpp \
|
||||||
Transceiver.cpp \
|
Transceiver.cpp \
|
||||||
ChannelizerBase.cpp \
|
sch.c
|
||||||
Channelizer.cpp \
|
|
||||||
Synthesis.cpp
|
|
||||||
|
|
||||||
libtransceiver_common_la_SOURCES = \
|
libtransceiver_la_SOURCES = \
|
||||||
$(COMMON_SOURCES) \
|
$(COMMON_SOURCES) \
|
||||||
Resampler.cpp \
|
Resampler.cpp \
|
||||||
radioInterfaceResamp.cpp \
|
radioInterfaceResamp.cpp \
|
||||||
radioInterfaceMulti.cpp
|
radioInterfaceDiversity.cpp
|
||||||
|
|
||||||
|
bin_PROGRAMS = osmo-trx
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
Complex.h \
|
Complex.h \
|
||||||
radioInterface.h \
|
radioInterface.h \
|
||||||
radioVector.h \
|
radioVector.h \
|
||||||
radioClock.h \
|
radioClock.h \
|
||||||
radioBuffer.h \
|
radioDevice.h \
|
||||||
sigProcLib.h \
|
sigProcLib.h \
|
||||||
signalVector.h \
|
signalVector.h \
|
||||||
Transceiver.h \
|
Transceiver.h \
|
||||||
|
USRPDevice.h \
|
||||||
Resampler.h \
|
Resampler.h \
|
||||||
ChannelizerBase.h \
|
common/convolve.h \
|
||||||
Channelizer.h \
|
common/convert.h \
|
||||||
Synthesis.h
|
common/scale.h \
|
||||||
|
common/mult.h
|
||||||
|
|
||||||
COMMON_LDADD = \
|
osmo_trx_SOURCES = osmo-trx.cpp
|
||||||
libtransceiver_common.la \
|
osmo_trx_LDADD = \
|
||||||
|
libtransceiver.la \
|
||||||
$(ARCH_LA) \
|
$(ARCH_LA) \
|
||||||
$(GSM_LA) \
|
$(GSM_LA) \
|
||||||
$(COMMON_LA) \
|
$(COMMON_LA) \
|
||||||
$(FFTWF_LIBS) \
|
$(SQLITE_LA) \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS)
|
||||||
$(LIBOSMOCTRL_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS)
|
|
||||||
|
|
||||||
bin_PROGRAMS =
|
if USRP1
|
||||||
|
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||||
if DEVICE_UHD
|
osmo_trx_LDADD += $(USRP_LIBS)
|
||||||
bin_PROGRAMS += osmo-trx-uhd
|
else
|
||||||
osmo_trx_uhd_SOURCES = osmo-trx.cpp
|
libtransceiver_la_SOURCES += UHDDevice.cpp
|
||||||
osmo_trx_uhd_LDADD = \
|
osmo_trx_LDADD += $(UHD_LIBS)
|
||||||
$(builddir)/device/uhd/libdevice.la \
|
|
||||||
$(COMMON_LDADD) \
|
|
||||||
$(UHD_LIBS)
|
|
||||||
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
if DEVICE_USRP1
|
|
||||||
bin_PROGRAMS += osmo-trx-usrp1
|
|
||||||
osmo_trx_usrp1_SOURCES = osmo-trx.cpp
|
|
||||||
osmo_trx_usrp1_LDADD = \
|
|
||||||
$(builddir)/device/usrp1/libdevice.la \
|
|
||||||
$(COMMON_LDADD) \
|
|
||||||
$(USRP_LIBS)
|
|
||||||
osmo_trx_usrp1_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
if DEVICE_LMS
|
|
||||||
bin_PROGRAMS += osmo-trx-lms
|
|
||||||
osmo_trx_lms_SOURCES = osmo-trx.cpp
|
|
||||||
osmo_trx_lms_LDADD = \
|
|
||||||
$(builddir)/device/lms/libdevice.la \
|
|
||||||
$(COMMON_LDADD) \
|
|
||||||
$(LMS_LIBS)
|
|
||||||
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "Resampler.h"
|
#include "Resampler.h"
|
||||||
|
|
||||||
@@ -36,8 +35,6 @@ extern "C" {
|
|||||||
|
|
||||||
#define MAX_OUTPUT_LEN 4096
|
#define MAX_OUTPUT_LEN 4096
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static float sinc(float x)
|
static float sinc(float x)
|
||||||
{
|
{
|
||||||
if (x == 0.0)
|
if (x == 0.0)
|
||||||
@@ -46,19 +43,32 @@ static float sinc(float x)
|
|||||||
return sin(M_PI * x) / (M_PI * x);
|
return sin(M_PI * x) / (M_PI * x);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resampler::initFilters(float bw)
|
bool Resampler::initFilters(float bw)
|
||||||
{
|
{
|
||||||
float cutoff;
|
size_t proto_len = p * filt_len;
|
||||||
|
float *proto, val, cutoff;
|
||||||
float sum = 0.0f, scale = 0.0f;
|
float sum = 0.0f, scale = 0.0f;
|
||||||
|
float midpt = (float) (proto_len - 1.0) / 2.0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate partition filters and the temporary prototype filter
|
* Allocate partition filters and the temporary prototype filter
|
||||||
* according to numerator of the rational rate. Coefficients are
|
* according to numerator of the rational rate. Coefficients are
|
||||||
* real only and must be 16-byte memory aligned for SSE usage.
|
* real only and must be 16-byte memory aligned for SSE usage.
|
||||||
*/
|
*/
|
||||||
auto proto = vector<float>(p * filt_len);
|
proto = new float[proto_len];
|
||||||
for (auto &part : partitions)
|
if (!proto)
|
||||||
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate the prototype filter with a Blackman-harris window.
|
* Generate the prototype filter with a Blackman-harris window.
|
||||||
@@ -75,26 +85,47 @@ void Resampler::initFilters(float bw)
|
|||||||
else
|
else
|
||||||
cutoff = (float) q;
|
cutoff = (float) q;
|
||||||
|
|
||||||
float midpt = (proto.size() - 1) / 2.0;
|
for (size_t i = 0; i < proto_len; i++) {
|
||||||
for (size_t i = 0; i < proto.size(); i++) {
|
|
||||||
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
||||||
proto[i] *= a0 -
|
proto[i] *= a0 -
|
||||||
a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
|
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
|
||||||
a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
|
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
|
||||||
a3 * cos(6 * M_PI * i / (proto.size() - 1));
|
a3 * cos(6 * M_PI * i / (proto_len - 1));
|
||||||
sum += proto[i];
|
sum += proto[i];
|
||||||
}
|
}
|
||||||
scale = p / sum;
|
scale = p / sum;
|
||||||
|
|
||||||
/* Populate filter partitions from the prototype filter */
|
/* Populate filter partitions from the prototype filter */
|
||||||
for (size_t i = 0; i < filt_len; i++) {
|
for (size_t i = 0; i < filt_len; i++) {
|
||||||
for (size_t n = 0; n < p; n++)
|
for (size_t n = 0; n < p; n++) {
|
||||||
partitions[n][i] = complex<float>(proto[i * p + n] * scale);
|
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
|
||||||
|
partitions[n][2 * i + 1] = 0.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store filter taps in reverse */
|
/* For convolution, we store the filter taps in reverse */
|
||||||
for (auto &part : partitions)
|
for (size_t n = 0; n < p; n++) {
|
||||||
reverse(&part[0], &part[filt_len]);
|
for (size_t i = 0; i < filt_len / 2; i++) {
|
||||||
|
val = partitions[n][2 * i];
|
||||||
|
partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
|
||||||
|
partitions[n][2 * (filt_len - 1 - i)] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete proto;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resampler::releaseFilters()
|
||||||
|
{
|
||||||
|
if (partitions) {
|
||||||
|
for (size_t i = 0; i < p; i++)
|
||||||
|
free(partitions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(partitions);
|
||||||
|
partitions = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||||
@@ -128,12 +159,24 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
|
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 n, path;
|
int n, path;
|
||||||
|
int hist_len = filt_len - 1;
|
||||||
|
|
||||||
if (!check_vec_len(in_len, out_len, p, q))
|
if (!check_vec_len(in_len, out_len, p, q))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/* Insert history */
|
||||||
|
memcpy(&in[-2 * hist_len], history, hist_len * 2 * sizeof(float));
|
||||||
|
|
||||||
/* Generate output from precomputed input/output paths */
|
/* Generate output from precomputed input/output paths */
|
||||||
for (size_t i = 0; i < out_len; i++) {
|
for (size_t i = 0; i < out_len; i++) {
|
||||||
@@ -141,28 +184,34 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
|
|||||||
path = out_path[i];
|
path = out_path[i];
|
||||||
|
|
||||||
convolve_real(in, in_len,
|
convolve_real(in, in_len,
|
||||||
reinterpret_cast<float *>(partitions[path]),
|
partitions[path], filt_len,
|
||||||
filt_len, &out[2 * i], out_len - i,
|
&out[2 * i], out_len - i,
|
||||||
n, 1);
|
n, 1, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Save history */
|
||||||
|
memcpy(history, &in[2 * (in_len - hist_len)],
|
||||||
|
hist_len * 2 * sizeof(float));
|
||||||
|
|
||||||
return out_len;
|
return out_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resampler::init(float bw)
|
bool Resampler::init(float bw)
|
||||||
{
|
{
|
||||||
if (p == 0 || q == 0 || filt_len == 0) return false;
|
size_t hist_len = filt_len - 1;
|
||||||
|
|
||||||
/* Filterbank filter internals */
|
/* Filterbank filter internals */
|
||||||
initFilters(bw);
|
if (initFilters(bw) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* History buffer */
|
||||||
|
history = new float[2 * hist_len];
|
||||||
|
memset(history, 0, 2 * hist_len * sizeof(float));
|
||||||
|
|
||||||
/* Precompute filterbank paths */
|
/* Precompute filterbank paths */
|
||||||
int i = 0;
|
in_index = new size_t[MAX_OUTPUT_LEN];
|
||||||
for (auto &index : in_index)
|
out_path = new size_t[MAX_OUTPUT_LEN];
|
||||||
index = (q * i++) / p;
|
computePath();
|
||||||
i = 0;
|
|
||||||
for (auto &path : out_path)
|
|
||||||
path = (q * i++) % p;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -173,7 +222,7 @@ size_t Resampler::len()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||||
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
|
: in_index(NULL), out_path(NULL), partitions(NULL), history(NULL)
|
||||||
{
|
{
|
||||||
this->p = p;
|
this->p = p;
|
||||||
this->q = q;
|
this->q = q;
|
||||||
@@ -182,6 +231,9 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
|||||||
|
|
||||||
Resampler::~Resampler()
|
Resampler::~Resampler()
|
||||||
{
|
{
|
||||||
for (auto &part : partitions)
|
releaseFilters();
|
||||||
free(part);
|
|
||||||
|
delete history;
|
||||||
|
delete in_index;
|
||||||
|
delete out_path;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,6 @@
|
|||||||
#ifndef _RESAMPLER_H_
|
#ifndef _RESAMPLER_H_
|
||||||
#define _RESAMPLER_H_
|
#define _RESAMPLER_H_
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <complex>
|
|
||||||
|
|
||||||
class Resampler {
|
class Resampler {
|
||||||
public:
|
public:
|
||||||
/* Constructor for rational sample rate conversion
|
/* Constructor for rational sample rate conversion
|
||||||
@@ -55,7 +52,7 @@ public:
|
|||||||
* Input and output vector lengths must of be equal multiples of the
|
* Input and output vector lengths must of be equal multiples of the
|
||||||
* rational conversion rate denominator and numerator respectively.
|
* rational conversion rate denominator and numerator respectively.
|
||||||
*/
|
*/
|
||||||
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
|
int rotate(float *in, size_t in_len, float *out, size_t out_len);
|
||||||
|
|
||||||
/* Get filter length
|
/* Get filter length
|
||||||
* @return number of taps in each filter partition
|
* @return number of taps in each filter partition
|
||||||
@@ -66,11 +63,15 @@ private:
|
|||||||
size_t p;
|
size_t p;
|
||||||
size_t q;
|
size_t q;
|
||||||
size_t filt_len;
|
size_t filt_len;
|
||||||
std::vector<size_t> in_index;
|
size_t *in_index;
|
||||||
std::vector<size_t> out_path;
|
size_t *out_path;
|
||||||
std::vector<std::complex<float> *> partitions;
|
|
||||||
|
|
||||||
void initFilters(float bw);
|
float **partitions;
|
||||||
|
float *history;
|
||||||
|
|
||||||
|
bool initFilters(float bw);
|
||||||
|
void releaseFilters();
|
||||||
|
void computePath();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _RESAMPLER_H_ */
|
#endif /* _RESAMPLER_H_ */
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "fft.h"
|
|
||||||
#include "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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#ifndef _SYNTHESIS_H_
|
|
||||||
#define _SYNTHESIS_H_
|
|
||||||
|
|
||||||
#include "ChannelizerBase.h"
|
|
||||||
|
|
||||||
class Synthesis : public ChannelizerBase {
|
|
||||||
public:
|
|
||||||
/** Constructor for synthesis filterbank
|
|
||||||
@param m number of physical channels
|
|
||||||
@param blockLen number of samples per output of each iteration
|
|
||||||
@param hLen number of taps in each constituent filter path
|
|
||||||
*/
|
|
||||||
Synthesis(size_t m, size_t blockLen, size_t hLen = 16);
|
|
||||||
~Synthesis();
|
|
||||||
|
|
||||||
/* Return required input and output buffer lengths */
|
|
||||||
size_t inputLen() const;
|
|
||||||
size_t outputLen() const;
|
|
||||||
|
|
||||||
/** Rotate "output commutator" and drive samples through filterbank
|
|
||||||
@param out complex output vector
|
|
||||||
@param oLen number of samples in buffer (must match block length * m)
|
|
||||||
@return false on error and true otherwise
|
|
||||||
*/
|
|
||||||
bool rotate(float *out, size_t oLen);
|
|
||||||
|
|
||||||
/** Get buffer for an input path
|
|
||||||
@param chan channel number of filterbank
|
|
||||||
@return NULL on error and pointer to buffer otherwise
|
|
||||||
*/
|
|
||||||
float *inputBuffer(size_t chan) const;
|
|
||||||
bool resetBuffer(size_t chan);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _SYNTHESIS_H_ */
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -30,11 +30,6 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <osmocom/core/signal.h>
|
|
||||||
#include "config_defs.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
class Transceiver;
|
class Transceiver;
|
||||||
|
|
||||||
/** Channel descriptor for transceiver object and channel number pair */
|
/** Channel descriptor for transceiver object and channel number pair */
|
||||||
@@ -59,7 +54,7 @@ struct TransceiverState {
|
|||||||
~TransceiverState();
|
~TransceiverState();
|
||||||
|
|
||||||
/* Initialize a multiframe slot in the filler table */
|
/* Initialize a multiframe slot in the filler table */
|
||||||
bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
|
void init(size_t slot, signalVector *burst, bool fill);
|
||||||
|
|
||||||
int chanType[8];
|
int chanType[8];
|
||||||
|
|
||||||
@@ -85,37 +80,134 @@ struct TransceiverState {
|
|||||||
|
|
||||||
/* Received noise energy levels */
|
/* Received noise energy levels */
|
||||||
float mNoiseLev;
|
float mNoiseLev;
|
||||||
noiseVector mNoises;
|
avgVector mNoises;
|
||||||
|
avgVector mFreqOffsets;
|
||||||
|
|
||||||
/* Shadowed downlink attenuation */
|
/* Store pointers to previous frame */
|
||||||
int mPower;
|
radioVector *prevFrame[8];
|
||||||
|
|
||||||
|
/* Transceiver mode */
|
||||||
|
int mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The Transceiver class, responsible for physical layer of basestation */
|
/** The Transceiver class, responsible for physical layer of basestation */
|
||||||
class Transceiver {
|
class Transceiver {
|
||||||
|
private:
|
||||||
|
int mBasePort;
|
||||||
|
std::string mAddr;
|
||||||
|
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||||
|
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||||
|
|
||||||
|
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||||
|
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||||
|
UDPSocket *mClockSocket; ///< socket for writing clock updates to GSM core
|
||||||
|
|
||||||
|
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||||
|
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||||
|
|
||||||
|
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
||||||
|
Thread *mLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
||||||
|
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||||
|
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||||
|
|
||||||
|
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||||
|
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||||
|
|
||||||
|
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||||
|
double txFullScale; ///< full scale input to radio
|
||||||
|
double rxFullScale; ///< full scale output to radio
|
||||||
|
|
||||||
|
/** 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
|
||||||
|
SCH, ///< timeslot should contain a SCH burst
|
||||||
|
IDLE ///< timeslot is an idle (or dummy) burst
|
||||||
|
} CorrType;
|
||||||
|
|
||||||
|
/** modulate and add a burst to the transmit queue */
|
||||||
|
void addRadioVector(size_t chan, BitVector &bits,
|
||||||
|
int RSSI, GSM::Time &wTime);
|
||||||
|
|
||||||
|
/** Update filler table */
|
||||||
|
void updateFillerTable(size_t chan, radioVector *burst);
|
||||||
|
|
||||||
|
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
|
||||||
|
void pushRadioVector(GSM::Time &nowTime);
|
||||||
|
|
||||||
|
/** Pull and demodulate a burst from the receive FIFO */
|
||||||
|
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI,
|
||||||
|
int &timingOffset, size_t chan = 0);
|
||||||
|
|
||||||
|
/** Set modulus for specific timeslot */
|
||||||
|
void setModulus(size_t timeslot, size_t chan);
|
||||||
|
|
||||||
|
/** return the expected burst type for the specified timestamp */
|
||||||
|
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
|
||||||
|
|
||||||
|
/** send messages over the clock socket */
|
||||||
|
void writeClockInterface(void);
|
||||||
|
|
||||||
|
/** Detect RACH bursts */
|
||||||
|
bool detectRACH(TransceiverState *state,
|
||||||
|
signalVector &burst,
|
||||||
|
complex &, float &toa);
|
||||||
|
|
||||||
|
bool detectSCH(TransceiverState *state,
|
||||||
|
signalVector &burst,
|
||||||
|
complex &, float &toa);
|
||||||
|
|
||||||
|
bool decodeSCH(SoftVector *burst, GSM::Time *time);
|
||||||
|
bool correctFCCH(TransceiverState *state, signalVector *burst);
|
||||||
|
|
||||||
|
/** Detect normal bursts */
|
||||||
|
bool 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 mOn; ///< flag to indicate that transceiver is powered on
|
||||||
|
double mTxFreq; ///< the transmit frequency
|
||||||
|
double mRxFreq; ///< the receive frequency
|
||||||
|
int mPower; ///< the transmit power in dB
|
||||||
|
unsigned mTSC; ///< the midamble sequence code
|
||||||
|
unsigned mMaxExpectedDelay; ///< maximum TOA offset in GSM symbols
|
||||||
|
unsigned long long mRxSlotMask[8]; ///< MS - enabled multiframe slot mask
|
||||||
|
int mBSIC; ///< MS - detected BSIC
|
||||||
|
|
||||||
|
std::vector<TransceiverState> mStates;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Transceiver constructor
|
|
||||||
|
/** Transceiver constructor
|
||||||
@param wBasePort base port number of UDP sockets
|
@param wBasePort base port number of UDP sockets
|
||||||
@param TRXAddress IP address of the TRX, as a string
|
@param TRXAddress IP address of the TRX manager, as a string
|
||||||
@param GSMcoreAddress IP address of the GSM core, as a string
|
|
||||||
@param wSPS number of samples per GSM symbol
|
@param wSPS number of samples per GSM symbol
|
||||||
@param wTransmitLatency initial setting of transmit latency
|
@param wTransmitLatency initial setting of transmit latency
|
||||||
@param radioInterface associated radioInterface object
|
@param radioInterface associated radioInterface object
|
||||||
*/
|
*/
|
||||||
Transceiver(int wBasePort,
|
Transceiver(int wBasePort,
|
||||||
const char *TRXAddress,
|
const char *TRXAddress,
|
||||||
const char *GSMcoreAddress,
|
size_t wSPS, size_t chans,
|
||||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
GSM::Time wTransmitLatency,
|
||||||
GSM::Time wTransmitLatency,
|
RadioInterface *wRadioInterface);
|
||||||
RadioInterface *wRadioInterface,
|
|
||||||
double wRssiOffset);
|
|
||||||
|
|
||||||
/** Destructor */
|
/** Destructor */
|
||||||
~Transceiver();
|
~Transceiver();
|
||||||
|
|
||||||
/** Start the control loop */
|
/** start the Transceiver */
|
||||||
bool init(FillerType filler, size_t rtsc, unsigned rach_delay,
|
void start();
|
||||||
bool edge, bool ext_rach);
|
bool init(bool filler);
|
||||||
|
|
||||||
/** attach the radioInterface receive FIFO */
|
/** attach the radioInterface receive FIFO */
|
||||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||||
@@ -130,8 +222,6 @@ public:
|
|||||||
/** accessor for number of channels */
|
/** accessor for number of channels */
|
||||||
size_t numChans() const { return mChans; };
|
size_t numChans() const { return mChans; };
|
||||||
|
|
||||||
void setSignalHandler(osmo_signal_cbfn cbfn);
|
|
||||||
|
|
||||||
/** Codes for channel combinations */
|
/** Codes for channel combinations */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FILL, ///< Channel is transmitted, but unused
|
FILL, ///< Channel is transmitted, but unused
|
||||||
@@ -152,85 +242,12 @@ public:
|
|||||||
LOOPBACK ///< similar go VII, used in loopback testing
|
LOOPBACK ///< similar go VII, used in loopback testing
|
||||||
} ChannelCombination;
|
} ChannelCombination;
|
||||||
|
|
||||||
private:
|
enum {
|
||||||
int mBasePort;
|
TRX_MODE_OFF,
|
||||||
std::string mLocalAddr;
|
TRX_MODE_BTS,
|
||||||
std::string mRemoteAddr;
|
TRX_MODE_MS_ACQUIRE,
|
||||||
|
TRX_MODE_MS_TRACK,
|
||||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
};
|
||||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
|
||||||
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
|
||||||
|
|
||||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
|
||||||
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
|
||||||
|
|
||||||
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
|
||||||
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
|
||||||
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
|
|
||||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
|
||||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
|
||||||
|
|
||||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
|
||||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
|
||||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
|
||||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
|
||||||
|
|
||||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
|
||||||
double txFullScale; ///< full scale input to radio
|
|
||||||
double rxFullScale; ///< full scale output to radio
|
|
||||||
|
|
||||||
double rssiOffset; ///< RSSI to dBm conversion offset
|
|
||||||
|
|
||||||
osmo_signal_cbfn *sig_cbfn; ///< Registered Signal Handler to announce events.
|
|
||||||
|
|
||||||
/** modulate and add a burst to the transmit queue */
|
|
||||||
void addRadioVector(size_t chan, BitVector &bits,
|
|
||||||
int RSSI, GSM::Time &wTime);
|
|
||||||
|
|
||||||
/** Update filler table */
|
|
||||||
void updateFillerTable(size_t chan, radioVector *burst);
|
|
||||||
|
|
||||||
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
|
|
||||||
void pushRadioVector(GSM::Time &nowTime);
|
|
||||||
|
|
||||||
/** Pull and demodulate a burst from the receive FIFO */
|
|
||||||
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
|
||||||
double &timingOffset, double &noise,
|
|
||||||
size_t chan = 0);
|
|
||||||
|
|
||||||
/** Set modulus for specific timeslot */
|
|
||||||
void setModulus(size_t timeslot, size_t chan);
|
|
||||||
|
|
||||||
/** return the expected burst type for the specified timestamp */
|
|
||||||
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
|
|
||||||
|
|
||||||
/** send messages over the clock socket */
|
|
||||||
void writeClockInterface(void);
|
|
||||||
|
|
||||||
int mSPSTx; ///< number of samples per Tx symbol
|
|
||||||
int mSPSRx; ///< number of samples per Rx symbol
|
|
||||||
size_t mChans;
|
|
||||||
|
|
||||||
bool mExtRACH;
|
|
||||||
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 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;
|
|
||||||
|
|
||||||
/** Start and stop I/O threads through the control socket API */
|
|
||||||
bool start();
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
/** Protect destructor accessable stop call */
|
|
||||||
Mutex mLock;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** drive lower receive I/O and burst generation */
|
/** drive lower receive I/O and burst generation */
|
||||||
@@ -255,9 +272,7 @@ protected:
|
|||||||
|
|
||||||
friend void *TxUpperLoopAdapter(TransceiverChannel *);
|
friend void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
friend void *RxLowerLoopAdapter(Transceiver *);
|
friend void *LowerLoopAdapter(Transceiver *);
|
||||||
|
|
||||||
friend void *TxLowerLoopAdapter(Transceiver *);
|
|
||||||
|
|
||||||
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
|
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
@@ -267,18 +282,16 @@ protected:
|
|||||||
/** set priority on current thread */
|
/** set priority on current thread */
|
||||||
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
|
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 *);
|
void *RxUpperLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
/** Main drive threads */
|
/** Main drive threads */
|
||||||
void *RxLowerLoopAdapter(Transceiver *);
|
void *LowerLoopAdapter(Transceiver *);
|
||||||
void *TxLowerLoopAdapter(Transceiver *);
|
|
||||||
|
|
||||||
/** control message handler thread loop */
|
/** control message handler thread loop */
|
||||||
void *ControlServiceLoopAdapter(TransceiverChannel *);
|
void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
/** transmit queueing thread loop */
|
/** transmit queueing thread loop */
|
||||||
void *TxUpperLoopAdapter(TransceiverChannel *);
|
void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
|
|||||||
1388
Transceiver52M/UHDDevice.cpp
Normal file
1388
Transceiver52M/UHDDevice.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,16 +27,17 @@
|
|||||||
Compilation Flags
|
Compilation Flags
|
||||||
|
|
||||||
SWLOOPBACK compile for software loopback testing
|
SWLOOPBACK compile for software loopback testing
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "Logger.h"
|
|
||||||
#include "Threads.h"
|
#include "Threads.h"
|
||||||
#include "USRPDevice.h"
|
#include "USRPDevice.h"
|
||||||
|
|
||||||
|
#include <Logger.h>
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -58,15 +59,12 @@ const dboardConfigType dboardConfig = TXA_RXB;
|
|||||||
|
|
||||||
const double USRPDevice::masterClockRate = 52.0e6;
|
const double USRPDevice::masterClockRate = 52.0e6;
|
||||||
|
|
||||||
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
|
USRPDevice::USRPDevice(size_t sps, size_t, bool)
|
||||||
size_t chans, double lo_offset,
|
|
||||||
const std::vector<std::string>& tx_paths,
|
|
||||||
const std::vector<std::string>& rx_paths):
|
|
||||||
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths)
|
|
||||||
{
|
{
|
||||||
LOGC(DDEV, INFO) << "creating USRP device...";
|
LOG(INFO) << "creating USRP device...";
|
||||||
|
|
||||||
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
|
this->sps = sps;
|
||||||
|
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
|
||||||
actualSampleRate = masterClockRate/decimRate;
|
actualSampleRate = masterClockRate/decimRate;
|
||||||
rxGain = 0;
|
rxGain = 0;
|
||||||
|
|
||||||
@@ -76,14 +74,14 @@ USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
|
|||||||
* split sample rate Tx/Rx - 4/1 sps we need to need to
|
* split sample rate Tx/Rx - 4/1 sps we need to need to
|
||||||
* compensate for advance rather than delay.
|
* compensate for advance rather than delay.
|
||||||
*/
|
*/
|
||||||
if (tx_sps == 1)
|
if (sps == 1)
|
||||||
pingOffset = 272;
|
pingOffset = 272;
|
||||||
else if (tx_sps == 4)
|
else if (sps == 4)
|
||||||
pingOffset = 269 - 7500;
|
pingOffset = 269 - 7500;
|
||||||
else
|
else
|
||||||
pingOffset = 0;
|
pingOffset = 0;
|
||||||
|
|
||||||
#ifdef SWLOOPBACK
|
#ifdef SWLOOPBACK
|
||||||
samplePeriod = 1.0e6/actualSampleRate;
|
samplePeriod = 1.0e6/actualSampleRate;
|
||||||
loopbackBufferSize = 0;
|
loopbackBufferSize = 0;
|
||||||
gettimeofday(&lastReadTime,NULL);
|
gettimeofday(&lastReadTime,NULL);
|
||||||
@@ -91,36 +89,38 @@ USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int USRPDevice::open(const std::string &, int, bool)
|
int USRPDevice::open(const std::string &, bool)
|
||||||
{
|
{
|
||||||
writeLock.unlock();
|
writeLock.unlock();
|
||||||
|
|
||||||
LOGC(DDEV, INFO) << "opening USRP device..";
|
LOG(INFO) << "opening USRP device..";
|
||||||
#ifndef SWLOOPBACK
|
#ifndef SWLOOPBACK
|
||||||
string rbf = "std_inband.rbf";
|
string rbf = "std_inband.rbf";
|
||||||
//string rbf = "inband_1rxhb_1tx.rbf";
|
//string rbf = "inband_1rxhb_1tx.rbf";
|
||||||
m_uRx.reset();
|
m_uRx.reset();
|
||||||
|
if (!skipRx) {
|
||||||
try {
|
try {
|
||||||
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
|
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
|
||||||
0, decimRate * tx_sps, 1, -1,
|
0, decimRate * sps, 1, -1,
|
||||||
usrp_standard_rx::FPGA_MODE_NORMAL,
|
usrp_standard_rx::FPGA_MODE_NORMAL,
|
||||||
1024, 16 * 8, rbf));
|
1024, 16 * 8, rbf));
|
||||||
m_uRx->set_fpga_master_clock_freq(masterClockRate);
|
m_uRx->set_fpga_master_clock_freq(masterClockRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
catch(...) {
|
catch(...) {
|
||||||
LOGC(DDEV, ALERT) << "make failed on Rx";
|
LOG(ALERT) << "make failed on Rx";
|
||||||
m_uRx.reset();
|
m_uRx.reset();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_uRx->fpga_master_clock_freq() != masterClockRate)
|
if (m_uRx->fpga_master_clock_freq() != masterClockRate)
|
||||||
{
|
{
|
||||||
LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
|
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
|
||||||
<< ", desired clock freq = " << masterClockRate;
|
<< ", desired clock freq = " << masterClockRate;
|
||||||
m_uRx.reset();
|
m_uRx.reset();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
|
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
|
||||||
@@ -130,22 +130,22 @@ int USRPDevice::open(const std::string &, int, bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
catch(...) {
|
catch(...) {
|
||||||
LOGC(DDEV, ALERT) << "make failed on Tx";
|
LOG(ALERT) << "make failed on Tx";
|
||||||
m_uTx.reset();
|
m_uTx.reset();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_uTx->fpga_master_clock_freq() != masterClockRate)
|
if (m_uTx->fpga_master_clock_freq() != masterClockRate)
|
||||||
{
|
{
|
||||||
LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
|
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
|
||||||
<< ", desired clock freq = " << masterClockRate;
|
<< ", desired clock freq = " << masterClockRate;
|
||||||
m_uTx.reset();
|
m_uTx.reset();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_uRx->stop();
|
if (!skipRx) m_uRx->stop();
|
||||||
m_uTx->stop();
|
m_uTx->stop();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (dboardConfig) {
|
switch (dboardConfig) {
|
||||||
@@ -173,21 +173,23 @@ int USRPDevice::open(const std::string &, int, bool)
|
|||||||
m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
|
m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
|
||||||
m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
|
m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
|
||||||
|
|
||||||
|
samplesRead = 0;
|
||||||
|
samplesWritten = 0;
|
||||||
started = false;
|
started = false;
|
||||||
|
|
||||||
return NORMAL;
|
return NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool USRPDevice::start()
|
bool USRPDevice::start()
|
||||||
{
|
{
|
||||||
LOGC(DDEV, INFO) << "starting USRP...";
|
LOG(INFO) << "starting USRP...";
|
||||||
#ifndef SWLOOPBACK
|
#ifndef SWLOOPBACK
|
||||||
if (!m_uRx) return false;
|
if (!m_uRx && !skipRx) return false;
|
||||||
if (!m_uTx) return false;
|
if (!m_uTx) return false;
|
||||||
|
|
||||||
m_uRx->stop();
|
if (!skipRx) m_uRx->stop();
|
||||||
m_uTx->stop();
|
m_uTx->stop();
|
||||||
|
|
||||||
writeLock.lock();
|
writeLock.lock();
|
||||||
@@ -216,8 +218,11 @@ bool USRPDevice::start()
|
|||||||
hi32Timestamp = 0;
|
hi32Timestamp = 0;
|
||||||
isAligned = false;
|
isAligned = false;
|
||||||
|
|
||||||
|
|
||||||
|
if (!skipRx)
|
||||||
started = (m_uRx->start() && m_uTx->start());
|
started = (m_uRx->start() && m_uTx->start());
|
||||||
|
else
|
||||||
|
started = m_uTx->start();
|
||||||
return started;
|
return started;
|
||||||
#else
|
#else
|
||||||
gettimeofday(&lastReadTime,NULL);
|
gettimeofday(&lastReadTime,NULL);
|
||||||
@@ -225,14 +230,14 @@ bool USRPDevice::start()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool USRPDevice::stop()
|
bool USRPDevice::stop()
|
||||||
{
|
{
|
||||||
#ifndef SWLOOPBACK
|
#ifndef SWLOOPBACK
|
||||||
if (!m_uRx) return false;
|
if (!m_uRx) return false;
|
||||||
if (!m_uTx) return false;
|
if (!m_uTx) return false;
|
||||||
|
|
||||||
delete[] currData;
|
delete[] currData;
|
||||||
|
|
||||||
started = !(m_uRx->stop() && m_uTx->stop());
|
started = !(m_uRx->stop() && m_uTx->stop());
|
||||||
return !started;
|
return !started;
|
||||||
#else
|
#else
|
||||||
@@ -253,7 +258,7 @@ double USRPDevice::minTxGain()
|
|||||||
double USRPDevice::maxRxGain()
|
double USRPDevice::maxRxGain()
|
||||||
{
|
{
|
||||||
return m_dbRx->gain_max();
|
return m_dbRx->gain_max();
|
||||||
}
|
}
|
||||||
|
|
||||||
double USRPDevice::minRxGain()
|
double USRPDevice::minRxGain()
|
||||||
{
|
{
|
||||||
@@ -263,7 +268,7 @@ double USRPDevice::minRxGain()
|
|||||||
double USRPDevice::setTxGain(double dB, size_t chan)
|
double USRPDevice::setTxGain(double dB, size_t chan)
|
||||||
{
|
{
|
||||||
if (chan) {
|
if (chan) {
|
||||||
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
|
LOG(ALERT) << "Invalid channel " << chan;
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,10 +278,10 @@ double USRPDevice::setTxGain(double dB, size_t chan)
|
|||||||
if (dB < minTxGain())
|
if (dB < minTxGain())
|
||||||
dB = minTxGain();
|
dB = minTxGain();
|
||||||
|
|
||||||
LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
|
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||||
|
|
||||||
if (!m_dbTx->set_gain(dB))
|
if (!m_dbTx->set_gain(dB))
|
||||||
LOGC(DDEV, ERR) << "Error setting TX gain";
|
LOG(ERR) << "Error setting TX gain";
|
||||||
|
|
||||||
writeLock.unlock();
|
writeLock.unlock();
|
||||||
|
|
||||||
@@ -287,7 +292,7 @@ double USRPDevice::setTxGain(double dB, size_t chan)
|
|||||||
double USRPDevice::setRxGain(double dB, size_t chan)
|
double USRPDevice::setRxGain(double dB, size_t chan)
|
||||||
{
|
{
|
||||||
if (chan) {
|
if (chan) {
|
||||||
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
|
LOG(ALERT) << "Invalid channel " << chan;
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,86 +304,38 @@ double USRPDevice::setRxGain(double dB, size_t chan)
|
|||||||
if (dB < minRxGain())
|
if (dB < minRxGain())
|
||||||
dB = minRxGain();
|
dB = minRxGain();
|
||||||
|
|
||||||
LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB.";
|
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
|
||||||
|
|
||||||
if (!m_dbRx->set_gain(dB))
|
if (!m_dbRx->set_gain(dB))
|
||||||
LOGC(DDEV, ERR) << "Error setting RX gain";
|
LOG(ERR) << "Error setting RX gain";
|
||||||
|
|
||||||
writeLock.unlock();
|
writeLock.unlock();
|
||||||
|
|
||||||
return dB;
|
return dB;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
|
|
||||||
{
|
|
||||||
if (chan >= rx_paths.size()) {
|
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOGC(DDEV, ALERT) << "Not implemented";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string USRPDevice::getRxAntenna(size_t chan)
|
|
||||||
{
|
|
||||||
if (chan >= rx_paths.size()) {
|
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
LOGC(DDEV, ALERT) << "Not implemented";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
|
|
||||||
{
|
|
||||||
if (chan >= tx_paths.size()) {
|
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOGC(DDEV, ALERT) << "Not implemented";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string USRPDevice::getTxAntenna(size_t chan)
|
|
||||||
{
|
|
||||||
if (chan >= tx_paths.size()) {
|
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
LOGC(DDEV, ALERT) << "Not implemented";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool USRPDevice::requiresRadioAlign()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GSM::Time USRPDevice::minLatency() {
|
|
||||||
return GSM::Time(1,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Assumes sequential reads
|
// NOTE: Assumes sequential reads
|
||||||
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
||||||
{
|
{
|
||||||
#ifndef SWLOOPBACK
|
#ifndef SWLOOPBACK
|
||||||
if (!m_uRx)
|
if (!m_uRx)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
short *buf = bufs[0];
|
short *buf = bufs[0];
|
||||||
|
|
||||||
timestamp += timestampOffset;
|
timestamp += timestampOffset;
|
||||||
|
|
||||||
if (timestamp + len < timeStart) {
|
if (timestamp + len < timeStart) {
|
||||||
memset(buf,0,len*2*sizeof(short));
|
memset(buf,0,len*2*sizeof(short));
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (underrun) *underrun = false;
|
if (underrun) *underrun = false;
|
||||||
|
|
||||||
uint32_t readBuf[2000];
|
uint32_t readBuf[2000];
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
//guestimate USB read size
|
//guestimate USB read size
|
||||||
int readLen=0;
|
int readLen=0;
|
||||||
@@ -388,21 +345,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
|
readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
|
||||||
if (readLen > 8000) readLen= (8000/512)*512;
|
if (readLen > 8000) readLen= (8000/512)*512;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read USRP packets, parse and save A/D data as needed
|
// read USRP packets, parse and save A/D data as needed
|
||||||
readLen = m_uRx->read((void *)readBuf,readLen,overrun);
|
readLen = m_uRx->read((void *)readBuf,readLen,overrun);
|
||||||
for (int pktNum = 0; pktNum < (readLen/512); pktNum++) {
|
for(int pktNum = 0; pktNum < (readLen/512); pktNum++) {
|
||||||
// tmpBuf points to start of a USB packet
|
// tmpBuf points to start of a USB packet
|
||||||
uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
|
uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
|
||||||
TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
|
TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
|
||||||
uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
|
uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
|
||||||
uint32_t chan = (word0 >> 16) & 0x1f;
|
uint32_t chan = (word0 >> 16) & 0x1f;
|
||||||
unsigned payloadSz = word0 & 0x1ff;
|
unsigned payloadSz = word0 & 0x1ff;
|
||||||
LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
|
LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
|
||||||
|
|
||||||
bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
|
bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
|
||||||
if (incrementHi32 && (timeStart!=0)) {
|
if (incrementHi32 && (timeStart!=0)) {
|
||||||
LOGC(DDEV, DEBUG) << "high 32 increment!!!";
|
LOG(DEBUG) << "high 32 increment!!!";
|
||||||
hi32Timestamp++;
|
hi32Timestamp++;
|
||||||
}
|
}
|
||||||
pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
|
pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
|
||||||
@@ -414,24 +371,24 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
|
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
|
||||||
timestamp -= timestampOffset;
|
timestamp -= timestampOffset;
|
||||||
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
|
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
|
||||||
LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset;
|
LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
|
||||||
timestamp += timestampOffset;
|
timestamp += timestampOffset;
|
||||||
isAligned = true;
|
isAligned = true;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (chan != 0) {
|
if (chan != 0) {
|
||||||
LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
|
LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((word0 >> 28) & 0x04) {
|
if ((word0 >> 28) & 0x04) {
|
||||||
if (underrun) *underrun = true;
|
if (underrun) *underrun = true;
|
||||||
LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
|
LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
|
||||||
}
|
}
|
||||||
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
|
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
|
||||||
|
|
||||||
if (!isAligned) continue;
|
if (!isAligned) continue;
|
||||||
|
|
||||||
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
|
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
|
||||||
while (cursorStart*2 > currDataSize) {
|
while (cursorStart*2 > currDataSize) {
|
||||||
cursorStart -= currDataSize/2;
|
cursorStart -= currDataSize/2;
|
||||||
@@ -444,25 +401,25 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
else {
|
else {
|
||||||
memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
|
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);
|
timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
|
||||||
|
|
||||||
LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
|
LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// copy desired data to buf
|
// copy desired data to buf
|
||||||
unsigned bufStart = dataStart+(timestamp-timeStart);
|
unsigned bufStart = dataStart+(timestamp-timeStart);
|
||||||
if (bufStart + len < currDataSize/2) {
|
if (bufStart + len < currDataSize/2) {
|
||||||
LOGC(DDEV, DEBUG) << "bufStart: " << bufStart;
|
LOG(DEBUG) << "bufStart: " << bufStart;
|
||||||
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
|
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
|
||||||
memset(data+bufStart*2,0,len*2*sizeof(short));
|
memset(data+bufStart*2,0,len*2*sizeof(short));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
|
LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
|
||||||
unsigned firstLength = (currDataSize/2-bufStart);
|
unsigned firstLength = (currDataSize/2-bufStart);
|
||||||
LOGC(DDEV, DEBUG) << "firstLength: " << firstLength;
|
LOG(DEBUG) << "firstLength: " << firstLength;
|
||||||
memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
|
memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
|
||||||
memset(data+bufStart*2,0,firstLength*2*sizeof(short));
|
memset(data+bufStart*2,0,firstLength*2*sizeof(short));
|
||||||
memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
|
memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
|
||||||
@@ -472,21 +429,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
timeStart = timestamp + len;
|
timeStart = timestamp + len;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
if (loopbackBufferSize < 2) return 0;
|
if (loopbackBufferSize < 2) return 0;
|
||||||
int numSamples = 0;
|
int numSamples = 0;
|
||||||
struct timeval currTime;
|
struct timeval currTime;
|
||||||
gettimeofday(&currTime,NULL);
|
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);
|
(currTime.tv_usec - lastReadTime.tv_usec);
|
||||||
if (timeElapsed < samplePeriod) {return 0;}
|
if (timeElapsed < samplePeriod) {return 0;}
|
||||||
int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
|
int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
|
||||||
if (numSamplesToRead < len) return 0;
|
if (numSamplesToRead < len) return 0;
|
||||||
|
|
||||||
if (numSamplesToRead > len) numSamplesToRead = len;
|
if (numSamplesToRead > len) numSamplesToRead = len;
|
||||||
if (numSamplesToRead > loopbackBufferSize/2) {
|
if (numSamplesToRead > loopbackBufferSize/2) {
|
||||||
firstRead =false;
|
firstRead =false;
|
||||||
numSamplesToRead = loopbackBufferSize/2;
|
numSamplesToRead = loopbackBufferSize/2;
|
||||||
}
|
}
|
||||||
memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
|
memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
|
||||||
@@ -503,7 +460,8 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
gettimeofday(&lastReadTime,NULL);
|
gettimeofday(&lastReadTime,NULL);
|
||||||
firstRead = true;
|
firstRead = true;
|
||||||
}
|
}
|
||||||
|
samplesRead += numSamples;
|
||||||
|
|
||||||
return numSamples;
|
return numSamples;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -514,7 +472,7 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
|||||||
{
|
{
|
||||||
writeLock.lock();
|
writeLock.lock();
|
||||||
|
|
||||||
#ifndef SWLOOPBACK
|
#ifndef SWLOOPBACK
|
||||||
if (!m_uTx)
|
if (!m_uTx)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -552,21 +510,23 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
|||||||
}
|
}
|
||||||
m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
|
m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
|
||||||
|
|
||||||
|
samplesWritten += len/2/sizeof(short);
|
||||||
writeLock.unlock();
|
writeLock.unlock();
|
||||||
|
|
||||||
return len/2/sizeof(short);
|
return len/2/sizeof(short);
|
||||||
#else
|
#else
|
||||||
int retVal = len;
|
int retVal = len;
|
||||||
memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
|
memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
|
||||||
|
samplesWritten += retVal;
|
||||||
loopbackBufferSize += retVal*2;
|
loopbackBufferSize += retVal*2;
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||||
{
|
{
|
||||||
#ifndef SWLOOPBACK
|
#ifndef SWLOOPBACK
|
||||||
short data[] = {0x00,0x02,0x00,0x00};
|
short data[] = {0x00,0x02,0x00,0x00};
|
||||||
uint32_t *wordPtr = (uint32_t *) data;
|
uint32_t *wordPtr = (uint32_t *) data;
|
||||||
*wordPtr = host_to_usrp_u32(*wordPtr);
|
*wordPtr = host_to_usrp_u32(*wordPtr);
|
||||||
@@ -583,25 +543,25 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SWLOOPBACK
|
#ifndef SWLOOPBACK
|
||||||
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
|
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
|
||||||
{
|
{
|
||||||
usrp_tune_result result;
|
usrp_tune_result result;
|
||||||
|
|
||||||
if (chan) {
|
if (chan) {
|
||||||
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
|
LOG(ALERT) << "Invalid channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
|
if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
|
||||||
LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl
|
LOG(INFO) << "set TX: " << wFreq << std::endl
|
||||||
<< " baseband freq: " << result.baseband_freq << std::endl
|
<< " baseband freq: " << result.baseband_freq << std::endl
|
||||||
<< " DDC freq: " << result.dxc_freq << std::endl
|
<< " DDC freq: " << result.dxc_freq << std::endl
|
||||||
<< " residual freq: " << result.residual_freq;
|
<< " residual freq: " << result.residual_freq;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOGC(DDEV, ALERT) << "set TX: " << wFreq << " failed" << std::endl
|
LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl
|
||||||
<< " baseband freq: " << result.baseband_freq << std::endl
|
<< " baseband freq: " << result.baseband_freq << std::endl
|
||||||
<< " DDC freq: " << result.dxc_freq << std::endl
|
<< " DDC freq: " << result.dxc_freq << std::endl
|
||||||
<< " residual freq: " << result.residual_freq;
|
<< " residual freq: " << result.residual_freq;
|
||||||
@@ -614,19 +574,19 @@ bool USRPDevice::setRxFreq(double wFreq, size_t chan)
|
|||||||
usrp_tune_result result;
|
usrp_tune_result result;
|
||||||
|
|
||||||
if (chan) {
|
if (chan) {
|
||||||
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
|
LOG(ALERT) << "Invalid channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
|
if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
|
||||||
LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl
|
LOG(INFO) << "set RX: " << wFreq << std::endl
|
||||||
<< " baseband freq: " << result.baseband_freq << std::endl
|
<< " baseband freq: " << result.baseband_freq << std::endl
|
||||||
<< " DDC freq: " << result.dxc_freq << std::endl
|
<< " DDC freq: " << result.dxc_freq << std::endl
|
||||||
<< " residual freq: " << result.residual_freq;
|
<< " residual freq: " << result.residual_freq;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOGC(DDEV, ALERT) << "set RX: " << wFreq << " failed" << std::endl
|
LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl
|
||||||
<< " baseband freq: " << result.baseband_freq << std::endl
|
<< " baseband freq: " << result.baseband_freq << std::endl
|
||||||
<< " DDC freq: " << result.dxc_freq << std::endl
|
<< " DDC freq: " << result.dxc_freq << std::endl
|
||||||
<< " residual freq: " << result.residual_freq;
|
<< " residual freq: " << result.residual_freq;
|
||||||
@@ -640,22 +600,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
|
|||||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity)
|
||||||
InterfaceType iface, size_t chans, double lo_offset,
|
|
||||||
const std::vector<std::string>& tx_paths,
|
|
||||||
const std::vector<std::string>& rx_paths)
|
|
||||||
{
|
{
|
||||||
if (tx_sps != rx_sps) {
|
return new USRPDevice(sps, chans, diversity);
|
||||||
LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (chans != 1) {
|
|
||||||
LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (lo_offset != 0.0) {
|
|
||||||
LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
|
||||||
}
|
}
|
||||||
@@ -48,10 +48,15 @@ private:
|
|||||||
usrp_subdev_spec rxSubdevSpec;
|
usrp_subdev_spec rxSubdevSpec;
|
||||||
usrp_subdev_spec txSubdevSpec;
|
usrp_subdev_spec txSubdevSpec;
|
||||||
|
|
||||||
|
int sps;
|
||||||
double actualSampleRate; ///< the actual USRP sampling rate
|
double actualSampleRate; ///< the actual USRP sampling rate
|
||||||
unsigned int decimRate; ///< the USRP decimation rate
|
unsigned int decimRate; ///< the USRP decimation rate
|
||||||
|
|
||||||
|
unsigned long long samplesRead; ///< number of samples read from USRP
|
||||||
|
unsigned long long samplesWritten; ///< number of samples sent to USRP
|
||||||
|
|
||||||
bool started; ///< flag indicates USRP has started
|
bool started; ///< flag indicates USRP has started
|
||||||
|
bool skipRx; ///< set if USRP is transmit-only.
|
||||||
|
|
||||||
static const unsigned int currDataSize_log2 = 21;
|
static const unsigned int currDataSize_log2 = 21;
|
||||||
static const unsigned long currDataSize = (1 << currDataSize_log2);
|
static const unsigned long currDataSize = (1 << currDataSize_log2);
|
||||||
@@ -78,10 +83,10 @@ private:
|
|||||||
|
|
||||||
double rxGain;
|
double rxGain;
|
||||||
|
|
||||||
#ifdef SWLOOPBACK
|
#ifdef SWLOOPBACK
|
||||||
short loopbackBuffer[1000000];
|
short loopbackBuffer[1000000];
|
||||||
int loopbackBufferSize;
|
int loopbackBufferSize;
|
||||||
double samplePeriod;
|
double samplePeriod;
|
||||||
|
|
||||||
struct timeval startTime;
|
struct timeval startTime;
|
||||||
struct timeval lastReadTime;
|
struct timeval lastReadTime;
|
||||||
@@ -91,12 +96,10 @@ private:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** Object constructor */
|
/** Object constructor */
|
||||||
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
USRPDevice(size_t sps, size_t chans = 1, bool diversity = false);
|
||||||
const std::vector<std::string>& tx_paths,
|
|
||||||
const std::vector<std::string>& rx_paths);
|
|
||||||
|
|
||||||
/** Instantiate the USRP */
|
/** Instantiate the USRP */
|
||||||
int open(const std::string &, int, bool);
|
int open(const std::string &, bool);
|
||||||
|
|
||||||
/** Start the USRP */
|
/** Start the USRP */
|
||||||
bool start();
|
bool start();
|
||||||
@@ -176,28 +179,14 @@ private:
|
|||||||
/** return minimum Rx Gain **/
|
/** return minimum Rx Gain **/
|
||||||
double minTxGain(void);
|
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 whether user drives synchronization of Tx/Rx of USRP */
|
|
||||||
bool requiresRadioAlign();
|
|
||||||
|
|
||||||
/** return whether user drives synchronization of Tx/Rx of USRP */
|
|
||||||
virtual GSM::Time minLatency();
|
|
||||||
|
|
||||||
/** Return internal status values */
|
/** Return internal status values */
|
||||||
inline double getTxFreq(size_t chan = 0) { return 0; }
|
inline double getTxFreq(size_t chan = 0) { return 0; }
|
||||||
inline double getRxFreq(size_t chan = 0) { return 0; }
|
inline double getRxFreq(size_t chan = 0) { return 0; }
|
||||||
inline double getSampleRate() { return actualSampleRate; }
|
inline double getSampleRate() { return actualSampleRate; }
|
||||||
|
inline double numberRead() { return samplesRead; }
|
||||||
|
inline double numberWritten() { return samplesWritten; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _USRP_DEVICE_H_
|
#endif // _USRP_DEVICE_H_
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
include $(top_srcdir)/Makefile.common
|
|
||||||
|
|
||||||
SUBDIRS = common
|
|
||||||
if ARCH_ARM
|
|
||||||
SUBDIRS += arm
|
|
||||||
else
|
|
||||||
SUBDIRS += x86
|
|
||||||
endif
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
AM_CFLAGS = -Wall -std=gnu99
|
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libarch_common.la
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
|
||||||
convolve.h \
|
|
||||||
convert.h \
|
|
||||||
scale.h \
|
|
||||||
mult.h \
|
|
||||||
fft.h
|
|
||||||
|
|
||||||
libarch_common_la_SOURCES = \
|
|
||||||
convolve_base.c \
|
|
||||||
convert_base.c \
|
|
||||||
fft.c
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#ifndef _CONVERT_H_
|
|
||||||
#define _CONVERT_H_
|
|
||||||
|
|
||||||
void convert_float_short(short *out, const float *in, float scale, int len);
|
|
||||||
|
|
||||||
void convert_short_float(float *out, const short *in, int len);
|
|
||||||
|
|
||||||
void base_convert_float_short(short *out, const float *in,
|
|
||||||
float scale, int len);
|
|
||||||
|
|
||||||
void base_convert_short_float(float *out, const short *in, int len);
|
|
||||||
|
|
||||||
void convert_init(void);
|
|
||||||
|
|
||||||
#endif /* _CONVERT_H_ */
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef _CONVOLVE_H_
|
|
||||||
#define _CONVOLVE_H_
|
|
||||||
|
|
||||||
void *convolve_h_alloc(size_t num);
|
|
||||||
|
|
||||||
int convolve_real(const float *x, int x_len,
|
|
||||||
const float *h, int h_len,
|
|
||||||
float *y, int y_len,
|
|
||||||
int start, int len);
|
|
||||||
|
|
||||||
int convolve_complex(const float *x, int x_len,
|
|
||||||
const float *h, int h_len,
|
|
||||||
float *y, int y_len,
|
|
||||||
int start, int len);
|
|
||||||
|
|
||||||
int base_convolve_real(const float *x, int x_len,
|
|
||||||
const float *h, int h_len,
|
|
||||||
float *y, int y_len,
|
|
||||||
int start, int len);
|
|
||||||
|
|
||||||
int base_convolve_complex(const float *x, int x_len,
|
|
||||||
const float *h, int h_len,
|
|
||||||
float *y, int y_len,
|
|
||||||
int start, int len);
|
|
||||||
|
|
||||||
void convolve_init(void);
|
|
||||||
|
|
||||||
#endif /* _CONVOLVE_H_ */
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#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_ */
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
|
||||||
convert_sse_3.h \
|
|
||||||
convert_sse_4_1.h \
|
|
||||||
convolve_sse_3.h
|
|
||||||
|
|
||||||
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
|
|
||||||
|
|
||||||
# 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 = \
|
|
||||||
convert.c \
|
|
||||||
convolve.c
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.h"
|
|
||||||
#include "convert_sse_3.h"
|
|
||||||
#include "convert_sse_4_1.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
if (__builtin_cpu_supports("sse4.1")) {
|
|
||||||
c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n;
|
|
||||||
c.convert_si16_ps = &_sse_convert_si16_ps;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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)
|
|
||||||
{
|
|
||||||
if (!(len % 16))
|
|
||||||
c.convert_scale_ps_si16_16n(out, in, scale, len);
|
|
||||||
else if (!(len % 8))
|
|
||||||
c.convert_scale_ps_si16_8n(out, in, scale, len);
|
|
||||||
else
|
|
||||||
c.convert_scale_ps_si16(out, in, scale, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void convert_short_float(float *out, const short *in, int len)
|
|
||||||
{
|
|
||||||
if (!(len % 16))
|
|
||||||
c.convert_si16_ps_16n(out, in, len);
|
|
||||||
else
|
|
||||||
c.convert_si16_ps(out, in, len);
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.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);
|
|
||||||
void (*conv_cmplx_8n) (const float *, int, const float *, int, float *,
|
|
||||||
int, int, int);
|
|
||||||
void (*conv_cmplx) (const float *, int, const float *, int, float *,
|
|
||||||
int, int, int);
|
|
||||||
void (*conv_real4) (const float *, int, const float *, int, float *,
|
|
||||||
int, int, int);
|
|
||||||
void (*conv_real8) (const float *, int, const float *, int, float *,
|
|
||||||
int, int, int);
|
|
||||||
void (*conv_real12) (const float *, int, const float *, int, float *,
|
|
||||||
int, int, int);
|
|
||||||
void (*conv_real16) (const float *, int, const float *, int, float *,
|
|
||||||
int, int, int);
|
|
||||||
void (*conv_real20) (const float *, int, const float *, int, float *,
|
|
||||||
int, int, int);
|
|
||||||
void (*conv_real4n) (const float *, int, const float *, int, float *,
|
|
||||||
int, int, int);
|
|
||||||
void (*conv_real) (const float *, int, const float *, int, float *, 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,
|
|
||||||
float *y, int y_len,
|
|
||||||
int start, int len);
|
|
||||||
|
|
||||||
int _base_convolve_complex(const float *x, int x_len,
|
|
||||||
const float *h, int h_len,
|
|
||||||
float *y, int y_len,
|
|
||||||
int start, int len);
|
|
||||||
|
|
||||||
int bounds_check(int x_len, int h_len, int y_len,
|
|
||||||
int start, int len);
|
|
||||||
|
|
||||||
/* API: Initalize convolve module */
|
|
||||||
void convolve_init(void)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
#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)
|
|
||||||
{
|
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
|
||||||
|
|
||||||
switch (h_len) {
|
|
||||||
case 4:
|
|
||||||
c.conv_real4(x, x_len, h, h_len, y, y_len, start, len);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
c.conv_real8(x, x_len, h, h_len, y, y_len, start, len);
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
c.conv_real12(x, x_len, h, h_len, y, y_len, start, len);
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
c.conv_real16(x, x_len, h, h_len, y, y_len, start, len);
|
|
||||||
break;
|
|
||||||
case 20:
|
|
||||||
c.conv_real20(x, x_len, h, h_len, y, y_len, start, len);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!(h_len % 4))
|
|
||||||
c.conv_real4n(x, x_len, h, h_len, y, y_len,
|
|
||||||
start, len);
|
|
||||||
else
|
|
||||||
c.conv_real(x, x_len, h, h_len, y, y_len, start,
|
|
||||||
len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* API: Aligned complex-complex */
|
|
||||||
int convolve_complex(const float *x, int x_len,
|
|
||||||
const float *h, int h_len,
|
|
||||||
float *y, int y_len,
|
|
||||||
int start, int len)
|
|
||||||
{
|
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
|
||||||
|
|
||||||
if (!(h_len % 8))
|
|
||||||
c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start, len);
|
|
||||||
else if (!(h_len % 4))
|
|
||||||
c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start, len);
|
|
||||||
else
|
|
||||||
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
|
if ARCH_ARM
|
||||||
if ARCH_ARM_A15
|
if ARCH_ARM_A15
|
||||||
ARCH_FLAGS = -mfpu=neon-vfpv4
|
ARCH_FLAGS = -mfpu=neon-vfpv4
|
||||||
else
|
else
|
||||||
ARCH_FLAGS = -mfpu=neon
|
ARCH_FLAGS = -mfpu=neon
|
||||||
endif
|
endif
|
||||||
|
|
||||||
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common
|
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I../common
|
||||||
AM_CCASFLAGS = $(ARCH_FLAGS)
|
AM_CCASFLAGS = $(ARCH_FLAGS)
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libarch.la
|
noinst_LTLIBRARIES = libarch.la
|
||||||
|
|
||||||
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
|
|
||||||
|
|
||||||
libarch_la_SOURCES = \
|
libarch_la_SOURCES = \
|
||||||
|
../common/convolve_base.c \
|
||||||
convert.c \
|
convert.c \
|
||||||
convert_neon.S \
|
convert_neon.S \
|
||||||
convolve.c \
|
convolve.c \
|
||||||
@@ -20,3 +20,4 @@ libarch_la_SOURCES = \
|
|||||||
scale_neon.S \
|
scale_neon.S \
|
||||||
mult.c \
|
mult.c \
|
||||||
mult_neon.S
|
mult_neon.S
|
||||||
|
endif
|
||||||
@@ -25,15 +25,25 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
|
void neon_convert_ps_si16_4n(short *, float *, float *, int);
|
||||||
void neon_convert_si16_ps_4n(float *, const short *, int);
|
void neon_convert_si16_ps_4n(float *, short *, int);
|
||||||
|
|
||||||
void convert_init(void) {
|
#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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 */
|
/* 4*N 16-bit signed integer conversion with remainder */
|
||||||
static void neon_convert_si16_ps(float *out,
|
static void neon_convert_si16_ps(float *restrict out,
|
||||||
const short *in,
|
short *restrict in,
|
||||||
int len)
|
int len)
|
||||||
{
|
{
|
||||||
int start = len / 4 * 4;
|
int start = len / 4 * 4;
|
||||||
@@ -45,9 +55,9 @@ static void neon_convert_si16_ps(float *out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 4*N 16-bit signed integer conversion with remainder */
|
/* 4*N 16-bit signed integer conversion with remainder */
|
||||||
static void neon_convert_ps_si16(short *out,
|
static void neon_convert_ps_si16(short *restrict out,
|
||||||
const float *in,
|
float *restrict in,
|
||||||
const float *scale,
|
float *restrict scale,
|
||||||
int len)
|
int len)
|
||||||
{
|
{
|
||||||
int start = len / 4 * 4;
|
int start = len / 4 * 4;
|
||||||
@@ -57,8 +67,9 @@ static void neon_convert_ps_si16(short *out,
|
|||||||
for (int i = 0; i < len % 4; i++)
|
for (int i = 0; i < len % 4; i++)
|
||||||
out[start + i] = (short) (in[start + i] * (*scale));
|
out[start + i] = (short) (in[start + i] * (*scale));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void convert_float_short(short *out, const float *in, float scale, int len)
|
void convert_float_short(short *out, float *in, float scale, int len)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
float q[4] = { scale, scale, scale, scale };
|
float q[4] = { scale, scale, scale, scale };
|
||||||
@@ -68,11 +79,11 @@ void convert_float_short(short *out, const float *in, float scale, int len)
|
|||||||
else
|
else
|
||||||
neon_convert_ps_si16_4n(out, in, q, len >> 2);
|
neon_convert_ps_si16_4n(out, in, q, len >> 2);
|
||||||
#else
|
#else
|
||||||
base_convert_float_short(out, in, scale, len);
|
convert_ps_si16(out, in, scale, len);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void convert_short_float(float *out, const short *in, int len)
|
void convert_short_float(float *out, short *in, int len)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
if (len % 4)
|
if (len % 4)
|
||||||
@@ -80,6 +91,6 @@ void convert_short_float(float *out, const short *in, int len)
|
|||||||
else
|
else
|
||||||
neon_convert_si16_ps_4n(out, in, len >> 2);
|
neon_convert_si16_ps_4n(out, in, len >> 2);
|
||||||
#else
|
#else
|
||||||
base_convert_short_float(out, in, len);
|
convert_si16_ps(out, in, len);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -29,15 +29,17 @@
|
|||||||
int _base_convolve_real(float *x, int x_len,
|
int _base_convolve_real(float *x, int x_len,
|
||||||
float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len);
|
int start, int len,
|
||||||
|
int step, int offset);
|
||||||
|
|
||||||
int _base_convolve_complex(float *x, int x_len,
|
int _base_convolve_complex(float *x, int x_len,
|
||||||
float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len);
|
int start, int len,
|
||||||
|
int step, int offset);
|
||||||
|
|
||||||
int bounds_check(int x_len, int h_len, int y_len,
|
int bounds_check(int x_len, int h_len, int y_len,
|
||||||
int start, int len);
|
int start, int len, int step);
|
||||||
|
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
/* Calls into NEON assembler */
|
/* Calls into NEON assembler */
|
||||||
@@ -56,43 +58,39 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* API: Initalize convolve module */
|
|
||||||
void convolve_init(void)
|
|
||||||
{
|
|
||||||
/* Stub */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* API: Aligned complex-real */
|
/* API: Aligned complex-real */
|
||||||
int convolve_real(float *x, int x_len,
|
int convolve_real(float *x, int x_len,
|
||||||
float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len)
|
int start, int len,
|
||||||
|
int step, int offset)
|
||||||
{
|
{
|
||||||
void (*conv_func)(float *, float *, float *, int) = NULL;
|
void (*conv_func)(float *, float *, float *, int) = NULL;
|
||||||
|
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
|
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
switch (h_len) {
|
if (step <= 4) {
|
||||||
case 4:
|
switch (h_len) {
|
||||||
conv_func = neon_conv_real4;
|
case 4:
|
||||||
break;
|
conv_func = neon_conv_real4;
|
||||||
case 8:
|
break;
|
||||||
conv_func = neon_conv_real8;
|
case 8:
|
||||||
break;
|
conv_func = neon_conv_real8;
|
||||||
case 12:
|
break;
|
||||||
conv_func = neon_conv_real12;
|
case 12:
|
||||||
break;
|
conv_func = neon_conv_real12;
|
||||||
case 16:
|
break;
|
||||||
conv_func = neon_conv_real16;
|
case 16:
|
||||||
break;
|
conv_func = neon_conv_real16;
|
||||||
case 20:
|
break;
|
||||||
conv_func = neon_conv_real20;
|
case 20:
|
||||||
break;
|
conv_func = neon_conv_real20;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (conv_func) {
|
if (conv_func) {
|
||||||
@@ -102,7 +100,7 @@ int convolve_real(float *x, int x_len,
|
|||||||
_base_convolve_real(x, x_len,
|
_base_convolve_real(x, x_len,
|
||||||
h, h_len,
|
h, h_len,
|
||||||
y, y_len,
|
y, y_len,
|
||||||
start, len);
|
start, len, step, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@@ -113,17 +111,18 @@ int convolve_real(float *x, int x_len,
|
|||||||
int convolve_complex(float *x, int x_len,
|
int convolve_complex(float *x, int x_len,
|
||||||
float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len)
|
int start, int len,
|
||||||
|
int step, int offset)
|
||||||
{
|
{
|
||||||
void (*conv_func)(float *, float *, float *, int, int) = NULL;
|
void (*conv_func)(float *, float *, float *, int, int) = NULL;
|
||||||
|
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
|
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
if (!(h_len % 4))
|
if (step <= 4 && !(h_len % 4))
|
||||||
conv_func = neon_conv_cmplx_4n;
|
conv_func = neon_conv_cmplx_4n;
|
||||||
#endif
|
#endif
|
||||||
if (conv_func) {
|
if (conv_func) {
|
||||||
@@ -133,7 +132,7 @@ int convolve_complex(float *x, int x_len,
|
|||||||
_base_convolve_complex(x, x_len,
|
_base_convolve_complex(x, x_len,
|
||||||
h, h_len,
|
h, h_len,
|
||||||
y, y_len,
|
y, y_len,
|
||||||
start, len);
|
start, len, step, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@@ -92,8 +92,8 @@ neon_conv_real12:
|
|||||||
vld2.32 {q8-q9}, [r4], r6
|
vld2.32 {q8-q9}, [r4], r6
|
||||||
vld2.32 {q10-q11}, [r5], r6
|
vld2.32 {q10-q11}, [r5], r6
|
||||||
#ifdef HAVE_NEON_FMA
|
#ifdef HAVE_NEON_FMA
|
||||||
vmul.f32 q1, q6, q0
|
vfma.f32 q1, q6, q0
|
||||||
vmul.f32 q3, q7, q0
|
vfma.f32 q3, q7, q0
|
||||||
vfma.f32 q1, q8, q2
|
vfma.f32 q1, q8, q2
|
||||||
vfma.f32 q3, q9, q2
|
vfma.f32 q3, q9, q2
|
||||||
vfma.f32 q1, q10, q4
|
vfma.f32 q1, q10, q4
|
||||||
7
Transceiver52M/common/convert.h
Normal file
7
Transceiver52M/common/convert.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#ifndef _CONVERT_H_
|
||||||
|
#define _CONVERT_H_
|
||||||
|
|
||||||
|
void convert_float_short(short *out, float *in, float scale, int len);
|
||||||
|
void convert_short_float(float *out, short *in, int len);
|
||||||
|
|
||||||
|
#endif /* _CONVERT_H_ */
|
||||||
30
Transceiver52M/common/convolve.h
Normal file
30
Transceiver52M/common/convolve.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef _CONVOLVE_H_
|
||||||
|
#define _CONVOLVE_H_
|
||||||
|
|
||||||
|
void *convolve_h_alloc(int num);
|
||||||
|
|
||||||
|
int convolve_real(float *x, int x_len,
|
||||||
|
float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len,
|
||||||
|
int step, int offset);
|
||||||
|
|
||||||
|
int convolve_complex(float *x, int x_len,
|
||||||
|
float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len,
|
||||||
|
int step, int offset);
|
||||||
|
|
||||||
|
int base_convolve_real(float *x, int x_len,
|
||||||
|
float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len,
|
||||||
|
int step, int offset);
|
||||||
|
|
||||||
|
int base_convolve_complex(float *x, int x_len,
|
||||||
|
float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len,
|
||||||
|
int step, int offset);
|
||||||
|
|
||||||
|
#endif /* _CONVOLVE_H_ */
|
||||||
@@ -26,61 +26,64 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Base multiply and accumulate complex-real */
|
/* Base multiply and accumulate complex-real */
|
||||||
static void mac_real(const float *x, const float *h, float *y)
|
static void mac_real(float *x, float *h, float *y)
|
||||||
{
|
{
|
||||||
y[0] += x[0] * h[0];
|
y[0] += x[0] * h[0];
|
||||||
y[1] += x[1] * h[0];
|
y[1] += x[1] * h[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Base multiply and accumulate complex-complex */
|
/* Base multiply and accumulate complex-complex */
|
||||||
static void mac_cmplx(const float *x, const float *h, float *y)
|
static void mac_cmplx(float *x, float *h, float *y)
|
||||||
{
|
{
|
||||||
y[0] += x[0] * h[0] - x[1] * h[1];
|
y[0] += x[0] * h[0] - x[1] * h[1];
|
||||||
y[1] += x[0] * h[1] + x[1] * h[0];
|
y[1] += x[0] * h[1] + x[1] * h[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Base vector complex-complex multiply and accumulate */
|
/* Base vector complex-complex multiply and accumulate */
|
||||||
static void mac_real_vec_n(const float *x, const float *h, float *y,
|
static void mac_real_vec_n(float *x, float *h, float *y,
|
||||||
int len)
|
int len, int step, int offset)
|
||||||
{
|
{
|
||||||
for (int i=0; i<len; i++)
|
for (int i = offset; i < len; i += step)
|
||||||
mac_real(&x[2 * i], &h[2 * i], y);
|
mac_real(&x[2 * i], &h[2 * i], y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Base vector complex-complex multiply and accumulate */
|
/* Base vector complex-complex multiply and accumulate */
|
||||||
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
|
static void mac_cmplx_vec_n(float *x, float *h, float *y,
|
||||||
int len)
|
int len, int step, int offset)
|
||||||
{
|
{
|
||||||
for (int i=0; i<len; i++)
|
for (int i = offset; i < len; i += step)
|
||||||
mac_cmplx(&x[2 * i], &h[2 * i], y);
|
mac_cmplx(&x[2 * i], &h[2 * i], y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Base complex-real convolution */
|
/* Base complex-real convolution */
|
||||||
int _base_convolve_real(const float *x, int x_len,
|
int _base_convolve_real(float *x, int x_len,
|
||||||
const float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len)
|
int start, int len,
|
||||||
|
int step, int offset)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
||||||
h,
|
h,
|
||||||
&y[2 * i], h_len);
|
&y[2 * i], h_len,
|
||||||
|
step, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Base complex-complex convolution */
|
/* Base complex-complex convolution */
|
||||||
int _base_convolve_complex(const float *x, int x_len,
|
int _base_convolve_complex(float *x, int x_len,
|
||||||
const float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len)
|
int start, int len,
|
||||||
|
int step, int offset)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
||||||
h,
|
h,
|
||||||
&y[2 * i],
|
&y[2 * i],
|
||||||
h_len);
|
h_len, step, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@@ -88,10 +91,10 @@ int _base_convolve_complex(const float *x, int x_len,
|
|||||||
|
|
||||||
/* Buffer validity checks */
|
/* Buffer validity checks */
|
||||||
int bounds_check(int x_len, int h_len, int y_len,
|
int bounds_check(int x_len, int h_len, int y_len,
|
||||||
int start, int len)
|
int start, int len, int step)
|
||||||
{
|
{
|
||||||
if ((x_len < 1) || (h_len < 1) ||
|
if ((x_len < 1) || (h_len < 1) ||
|
||||||
(y_len < 1) || (len < 1)) {
|
(y_len < 1) || (len < 1) || (step < 1)) {
|
||||||
fprintf(stderr, "Convolve: Invalid input\n");
|
fprintf(stderr, "Convolve: Invalid input\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -107,12 +110,13 @@ int bounds_check(int x_len, int h_len, int y_len,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* API: Non-aligned (no SSE) complex-real */
|
/* API: Non-aligned (no SSE) complex-real */
|
||||||
int base_convolve_real(const float *x, int x_len,
|
int base_convolve_real(float *x, int x_len,
|
||||||
const float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len)
|
int start, int len,
|
||||||
|
int step, int offset)
|
||||||
{
|
{
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
@@ -120,16 +124,17 @@ int base_convolve_real(const float *x, int x_len,
|
|||||||
return _base_convolve_real(x, x_len,
|
return _base_convolve_real(x, x_len,
|
||||||
h, h_len,
|
h, h_len,
|
||||||
y, y_len,
|
y, y_len,
|
||||||
start, len);
|
start, len, step, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* API: Non-aligned (no SSE) complex-complex */
|
/* API: Non-aligned (no SSE) complex-complex */
|
||||||
int base_convolve_complex(const float *x, int x_len,
|
int base_convolve_complex(float *x, int x_len,
|
||||||
const float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len)
|
int start, int len,
|
||||||
|
int step, int offset)
|
||||||
{
|
{
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
@@ -137,11 +142,11 @@ int base_convolve_complex(const float *x, int x_len,
|
|||||||
return _base_convolve_complex(x, x_len,
|
return _base_convolve_complex(x, x_len,
|
||||||
h, h_len,
|
h, h_len,
|
||||||
y, y_len,
|
y, y_len,
|
||||||
start, len);
|
start, len, step, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Aligned filter tap allocation */
|
/* Aligned filter tap allocation */
|
||||||
void *convolve_h_alloc(size_t len)
|
void *convolve_h_alloc(int len)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_SSE3
|
#ifdef HAVE_SSE3
|
||||||
return memalign(16, len * 2 * sizeof(float));
|
return memalign(16, len * 2 * sizeof(float));
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user