Compare commits

..

7 Commits

Author SHA1 Message Date
Alexander Chemeris
2061843c3d osmo-trx-dec: Offline demodulation tool.
Change-Id: Ic5b59c7fe1a0c02d962b36de2fd5d7fc9a02f266
2017-06-01 21:19:53 +03:00
Alexander Chemeris
63d0f2a496 WIP:sigProcLib: Reduce burst detection window for NB.
Otherwise we detect bursts with search window far beyond specified.

Change-Id: If3cb40d2311504a13c03e1fbccad663ac201d9a4
2017-06-01 21:19:53 +03:00
Alexander Chemeris
6459ddc55c transceiver: RSSI was calculated reverse. 2017-06-01 21:19:53 +03:00
Alexander Chemeris
7a33c7221b osmo-trx-gen: generates waveform files aka IQ binary files in a number of formats.
Generated files can be used as an input to osmo-trx-dec or load them into signal
generators.

Change-Id: I555d99a632755b5bfcbaf3501a501613c2859d4e
2017-06-01 21:19:53 +03:00
Alexander Chemeris
dd62d4baa0 WIP:sigProcLib: Use a known PRBS to generate random Normal Bursts.
ToDo:
1) Add seed randomization.
2) An option to use a longer PRBS?
3) Use a known PRBS for other types of bursts.

Change-Id: Ib7fdf2f415457da38b78129532d5b80a4a94ecd3
2017-06-01 00:51:24 +03:00
Alexander Chemeris
09130c81a7 osmo-trx: Separate command line switch to enable EDGE filler.
Now -r comand line switch always enables GMSK filler even when EDGE mode is
enabled with -e switch. If you want to enable EDGE filler, use -E switch.

Change-Id: Ic8808bbe3f06740ef3fec1d1865ecb57fbcfabab
2017-06-01 00:51:24 +03:00
Alexander Chemeris
0c4e24d197 PRBS: a Pseudo-random binary sequence (PRBS) generator class.
Implemeted with a Galois LFSR for speed and flexibility compared to Fibonacci version.

Aliases for three popular PRBS' are added for convenience - PRBS9, PRBS15 and PRBS64.

Note that we can't test PRBS64 completely, because the sequence is too long to
be generated.

Change-Id: Ib5331ba5d0b5819929541686fdd87905e2177b74
2017-05-31 17:49:09 +03:00
160 changed files with 5545 additions and 7802 deletions

45
.gitignore vendored
View File

@@ -2,23 +2,22 @@
*.o
*.lo
*.la
Transceiver52M/osmo-trx-uhd
Transceiver52M/osmo-trx-usrp1
Transceiver52M/osmo-trx-lms
Transceiver52M/osmo-trx
Transceiver52M/osmo-trx-gen
Transceiver52M/osmo-trx-dec
# 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
CommonLibs/BitVectorTest
CommonLibs/ConfigurationTest
CommonLibs/F16Test
CommonLibs/InterthreadTest
CommonLibs/LogTest
CommonLibs/RegexpTest
CommonLibs/SocketsTest
CommonLibs/TimevalTest
CommonLibs/URLEncodeTest
CommonLibs/VectorTest
CommonLibs/PRBSTest
# automake/autoconf
*.in
@@ -44,22 +43,6 @@ 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

83
AUTHORS
View File

@@ -1,18 +1,18 @@
#
# Copyright 2008, 2009 Free Software Foundation, Inc.
#
#
# This file is part of GNU Radio
#
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
@@ -23,17 +23,34 @@ David A. Burgess, dburgess@kestrelsp.com:
CLI/CLI.h
CommonLibs/Assert.h
CommonLibs/BitVector.cpp
CommonLibs/BitVectorTest.cpp
CommonLibs/Configuration.cpp
CommonLibs/Configuration.h
CommonLibs/ConfigurationTest.cpp
CommonLibs/Interthread.h
CommonLibs/InterthreadTest.cpp
CommonLibs/LinkedLists.cpp
CommonLibs/LinkedLists.h
CommonLibs/Regexp.h
CommonLibs/RegexpTest.cpp
CommonLibs/Sockets.cpp
CommonLibs/Sockets.h
CommonLibs/SocketsTest.cpp
CommonLibs/Threads.cpp
CommonLibs/Threads.h
CommonLibs/Timeval.cpp
CommonLibs/Timeval.h
CommonLibs/TimevalTest.cpp
CommonLibs/Vector.h
CommonLibs/VectorTest.cpp
Control/CallControl.cpp
Control/ControlCommon.cpp
Control/ControlCommon.h
Control/FACCHDispatch.cpp
Control/MobilityManagement.cpp
Control/PagerTest.cpp
Control/RadioResource.cpp
Control/SDCCHDispatch.cpp
GSM/GSM610Tables.cpp
GSM/GSM610Tables.h
GSM/GSMCommon.cpp
@@ -65,15 +82,29 @@ David A. Burgess, dburgess@kestrelsp.com:
GSM/GSMTransfer.cpp
GSM/GSMTransfer.h
LICENSEBLOCK
SIP/SIPEngine.h
SIP/SIPInterface.h
SMS/SMSMessages.cpp
SMS/SMSMessages.h
SMS/SMSTransfer.cpp
SMS/SMSTransfer.h
TRXManager/TRXManager.cpp
Transceiver/Complex.h
tests/CommonLibs/BitVectorTest.cpp
tests/CommonLibs/InterthreadTest.cpp
tests/CommonLibs/SocketsTest.cpp
tests/CommonLibs/TimevalTest.cpp
tests/CommonLibs/VectorTest.cpp
apps/OpenBTS900.cpp
apps/OpenBTS850.cpp
apps/OpenBTS25c3.cpp
tests/AGCHTest.cpp
tests/BeaconTest.cpp
tests/CallTest.cpp
tests/CallTest2.cpp
tests/LAPDmTest.cpp
tests/LoopbackTest.cpp
tests/RegistrationTest.cpp
tests/TRXSimulator.cpp
Harvind S. Samra, hssamra@kestrelsp.com:
Control/PagerTest.cpp
Control/RadioResource.cpp
GSM/GSMConfig.h
GSM/GSMTransfer.h
LICENSEBLOCK
@@ -95,6 +126,13 @@ Harvind S. Samra, hssamra@kestrelsp.com:
Transceiver/testRadio.cpp
Raffi Sevlian, raffisev@gmail.com:
Control/CallControl.cpp
Control/ControlCommon.cpp
Control/ControlCommon.h
Control/FACCHDispatch.cpp
Control/MobilityManagement.cpp
Control/PagerTest.cpp
Control/RadioResource.cpp
GSM/GSMCommon.h
GSM/GSMConfig.h
GSM/GSML1FEC.h
@@ -119,9 +157,36 @@ Raffi Sevlian, raffisev@gmail.com:
GSM/GSMSAPMux.h
GSM/GSMTransfer.h
LICENSEBLOCK
SIP/SIPEngine.cpp
SIP/SIPInterface.cpp
SIP/SIPInterface.h
SIP/SIPMessage.cpp
SIP/SIPMessage.h
SIP/SIPUtility.cpp
SIP/SIPUtility.h
SMS/CMMessage.cpp
SMS/CMMessage.h
SMS/CMProcessor.cpp
SMS/CMProcessor.h
SMS/CMTest.cpp
SMS/RLMessage.cpp
SMS/RLMessage.h
SMS/RLProcessor.cpp
SMS/RLProcessor.h
SMS/SMSMessages.cpp
SMS/SMSMessages.h
SMS/SMSProcessors.cpp
SMS/SMSProcessors.h
SMS/SMSTransfer.cpp
SMS/SMSTransfer.h
SMS/TLMessage.cpp
SMS/TLMessage.h
SMS/TLProcessor.cpp
SMS/TLProcessor.h
TRXManager/TRXManager.h
Alon Levy, alonlevy1@gmail.com
RRLPMessages.cpp
RRLPMessages.h
RRLPTest.cpp

28
COPYING
View File

@@ -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
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
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
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
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.
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
@@ -701,8 +712,13 @@ END OF ADDITIONAL TERMS
How to comply with Section 13 of the AGPLv3 license.
The recommended method for compliance with Section 13 of the AGPLv3 license is
to deliver a text message to each handset that attaches to the cellular
network which uses OsmoTRX. At a minimum, that text message should include the string
"OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This
to deliver a text message to each handset that attaches to the OpenBTS cellular
network. At a minimum, that text message should include the string "OpenBTS
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
message need not be delivered to handsets that are denied registration with the
network, since those handsets have been denied service.
In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message"
feature. See the OpenBTS.config.example file for more information on the use of
this feature for AGPLv3 compliance.

1171
CommonLibs/Configuration.cpp Normal file

File diff suppressed because it is too large Load Diff

422
CommonLibs/Configuration.h Normal file
View 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

View 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);
const 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;
}

View File

@@ -35,20 +35,14 @@ using namespace std;
InterthreadQueue<int> gQ;
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*)
{
int *p;
for (int i=0; i<20; i++) {
p = new int;
*p = i;
CERR("queue write " << *p);
COUT("queue write " << *p);
gQ.write(p);
q_last_write_val = i;
if (random()%2) sleep(1);
}
p = new int;
@@ -62,14 +56,8 @@ void* qReader(void*)
bool done = false;
while (!done) {
int *p = gQ.read();
CERR("queue read " << *p);
if (*p<0) {
assert(q_last_read_val == 19 && *p == -1);
done = true;
} else {
assert(q_last_read_val == *p - 1);
q_last_read_val = *p;
}
COUT("queue read " << *p);
if (*p<0) done=true;
delete p;
}
return NULL;
@@ -82,9 +70,8 @@ void* mapWriter(void*)
for (int i=0; i<20; i++) {
p = new int;
*p = i;
CERR("map write " << *p);
COUT("map write " << *p);
gMap.write(i,p);
m_last_write_val = i;
if (random()%2) sleep(1);
}
return NULL;
@@ -94,9 +81,7 @@ void* mapReader(void*)
{
for (int i=0; i<20; i++) {
int *p = gMap.read(i);
CERR("map read " << *p);
assert(*p == i);
m_last_read_val = *p;
COUT("map read " << *p);
// InterthreadMap will delete the pointers
// delete p;
}
@@ -124,13 +109,6 @@ int main(int argc, char *argv[])
qWriterThread.join();
mapReaderThread.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");
}

View File

@@ -29,25 +29,6 @@
#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
{
// Pat added this routine for completeness, but never used or tested.

View File

@@ -70,7 +70,6 @@ class PointerFIFO {
:mHead(NULL),mTail(NULL),mFreeList(NULL),
mSize(0)
{}
~PointerFIFO();
unsigned size() const { return mSize; }
unsigned totalSize() const { return 0; } // Not used in this version.

View File

@@ -28,42 +28,42 @@
#include <iterator>
#include "Logger.h"
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
#include "debug.h"
}
#include "Configuration.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[])
{
struct log_info_cat categories[1];
struct log_info linfo;
categories[MYCAT] = {
"MYCAT",
NULL,
"Whatever",
LOGL_NOTICE,
1,
};
linfo.cat = categories;
linfo.num_cat = ARRAY_SIZE(categories);
gLogInit("LogTest","NOTICE",LOG_LOCAL7);
void *tall_ctx = talloc_named_const(NULL, 1, "OsmoTRX context");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &linfo);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_level(osmo_stderr_target, 1);
Log(MYCAT, LOGL_FATAL, __BASE_FILE__, __LINE__).get() << "testing the logger.";
Log(MYCAT, LOGL_ERROR, __BASE_FILE__, __LINE__).get() << "testing the logger.";
Log(MYCAT, LOGL_NOTICE, __BASE_FILE__, __LINE__).get() << "testing the logger.";
Log(MYCAT, LOGL_INFO, __BASE_FILE__, __LINE__).get() << "testing the logger.";
Log(MYCAT, LOGL_DEBUG, __BASE_FILE__, __LINE__).get() << "testing the logger.";
LOG(EMERG) << " testing the logger.";
LOG(ALERT) << " testing the logger.";
LOG(CRIT) << " testing the logger.";
LOG(ERR) << " testing the logger.";
LOG(WARNING) << " testing the logger.";
LOG(NOTICE) << " testing the logger.";
LOG(INFO) << " testing the logger.";
LOG(DEBUG) << " testing the logger.";
std::cout << "\n\n\n";
std::cout << "testing Alarms\n";
std::cout << "you should see three lines:" << std::endl;
printAlarms();
std::cout << "----------- generating 20 alarms ----------" << std::endl;
for (int i = 0 ; i < 20 ; ++i) {
LOG(ALERT) << i;
}
std::cout << "you should see ten lines with the numbers 10..19:" << std::endl;
printAlarms();
}

View File

@@ -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.
@@ -30,35 +32,300 @@
#include <stdarg.h>
#include <sys/time.h> // For gettimeofday
#include "Configuration.h"
#include "Logger.h"
#include "Threads.h" // pat added
using namespace std;
// Switches to enable/disable logging targets
// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
// Reference to a global config table, used all over the system.
extern ConfigurationTable gConfig;
/**@ 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;
int levelStringToInt(const string& name)
{
// 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;
}
static std::string format(const char *fmt, ...)
{
va_list ap;
char buf[300];
va_start(ap,fmt);
int n = vsnprintf(buf,300,fmt,ap);
va_end(ap);
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
return std::string(buf);
}
const std::string timestr()
{
struct timeval tv;
struct tm tm;
gettimeofday(&tv,NULL);
localtime_r(&tv.tv_sec,&tm);
unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
}
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
{
return os << ss.str();
}
int getLoggingLevel(const char* filename)
{
// 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()
{
int old_state;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
const char *fmt = neednl ? "%s\n" : "%s";
ScopedLock lock(gLogToLock);
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
pthread_setcancelstate(old_state, NULL);
if (mDummyInit) return;
// Anything at or above LOG_CRIT is an "alarm".
// Save alarms in the local list and echo them to stderr.
if (mPriority <= LOG_ERR) {
if (sLoggerInited) addAlarm(mStream.str().c_str());
cerr << mStream.str() << endl;
}
// Current logging level was already checked by the macro. So just log.
// Log to syslog
if (gLogToSyslog) {
syslog(mPriority, "%s", mStream.str().c_str());
}
// Log to file and console
if (gLogToConsole||gLogToFile) {
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
ScopedLock lock(gLogToLock);
if (gLogToConsole) {
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
std::cout << mStream.str();
if (neednl) std::cout<<"\n";
}
if (gLogToFile) {
fputs(mStream.str().c_str(),gLogToFile);
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
}
}
Log::Log(const char* name, const char* level, int facility)
{
mDummyInit = true;
gLogInit(name, level, facility);
}
ostringstream& Log::get()
{
assert(mPriority<numLevels);
mStream << levelNames[mPriority] << ' ';
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);
}
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
string str = gConfig.getStr("Log.File");
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
const char *fn = str.c_str();
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
gLogToFile = fopen(fn,"w"); // New log file each time we start.
if (gLogToFile) {
time_t now;
time(&now);
fprintf(gLogToFile,"Starting at %s",ctime(&now));
fflush(gLogToFile);
std::cout << "Logging to file: " << fn << "\n";
}
}
}
// Open the log connection.
openlog(name,0,facility);
}
void gLogEarly(int level, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (gLogToSyslog) {
va_list args_copy;
va_copy(args_copy, args);
vsyslog(level | LOG_USER, fmt, args_copy);
va_end(args_copy);
}
if (gLogToConsole) {
va_list args_copy;
va_copy(args_copy, args);
vprintf(fmt, args_copy);
printf("\n");
va_end(args_copy);
}
if (gLogToFile) {
va_list args_copy;
va_copy(args_copy, args);
vfprintf(gLogToFile, fmt, args_copy);
fprintf(gLogToFile, "\n");
va_end(args_copy);
}
va_end(args);
}
// vim: ts=4 sw=4

View File

@@ -23,41 +23,71 @@
*/
// (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
#define LOGGER_H
#include <syslog.h>
#include <stdint.h>
#include <stdio.h>
#include <sstream>
#include <list>
#include <map>
#include <string>
extern "C" {
#include <osmocom/core/logging.h>
#include "debug.h"
}
#define _LOG(level) \
Log(LOG_##level).get() << pthread_self() \
<< timestr() << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
/* Translation for old log statements */
#ifndef LOGL_ALERT
#define LOGL_ALERT LOGL_FATAL
#endif
#ifndef LOGL_ERR
#define LOGL_ERR LOGL_ERROR
#endif
#ifndef LOGL_WARNING
#define LOGL_WARNING LOGL_NOTICE
#define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel)
#ifdef NDEBUG
#define LOG(wLevel) \
if (LOG_##wLevel!=LOG_DEBUG && IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
#else
#define LOG(wLevel) \
if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
#endif
#define LOG(level) \
Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
// pat: And for your edification here are the 'levels' as defined in syslog.h:
// LOG_EMERG 0 system is unusable
// LOG_ALERT 1 action must be taken immediately
// LOG_CRIT 2 critical conditions
// LOG_ERR 3 error conditions
// LOG_WARNING 4 warning conditions
// LOG_NOTICE 5 normal, but significant, condition
// LOG_INFO 6 informational message
// LOG_DEBUG 7 debug-level message
#define LOGC(category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
// (pat) added - print out a var and its name.
// Use like this: int descriptive_name; LOG(INFO)<<LOGVAR(descriptive_name);
#define LOGVAR2(name,val) " " << name << "=" << (val)
#define LOGVAR(var) (" " #var "=") << var
#define LOGHEX(var) (" " #var "=0x") << hex << ((unsigned)var) << dec
#define LOGHEX2(name,val) " " << name << "=0x" << hex << ((unsigned)(val)) << dec
// These are kind of cheesy, but you can use for bitvector
#define LOGBV2(name,val) " " << name << "=(" << val<<" size:"<<val.size()<<")"
#define LOGBV(bv) LOGBV2(#bv,bv)
#define LOGVARRANGE(name,cur,lo,hi) " "<<name <<"=("<<(cur) << " range:"<<(lo) << " to "<<(hi) <<")"
#define LOGLV(category, level) \
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
#define OBJLOG(wLevel) \
LOG(wLevel) << "obj: " << this << ' '
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
#include "Threads.h" // must be after defines above, if these files are to be allowed to use LOG()
/**
A C++ stream-based thread-safe logger.
Derived from Dr. Dobb's Sept. 2007 issue.
Updated to use syslog.
This object is NOT the global logger;
every log record is an object of this class.
*/
@@ -67,28 +97,45 @@ class Log {
protected:
std::ostringstream mStream; ///< This is where we buffer up the log entry.
int mCategory; ///< Priority of current report.
int mPriority; ///< Category of current report.
const char *filename; ///< Source File Name of current report.
int line; ///< Line number in source file of current report.
std::ostringstream mStream; ///< This is where we buffer up the log entry.
int mPriority; ///< Priority of current report.
bool mDummyInit;
public:
Log(int wCategory, int wPriority, const char* filename, int line)
: mCategory(wCategory), mPriority(wPriority),
filename(filename), line(line)
Log(int wPriority)
:mPriority(wPriority), mDummyInit(false)
{ }
Log(const char* name, const char* level=NULL, int facility=LOG_USER);
// Most of the work is in the destructor.
/** The destructor actually generates the log entry. */
~Log();
std::ostringstream& get();
};
extern bool gLogToConsole; // Output log messages to stdout
extern bool gLogToSyslog; // Output log messages to syslog
std::list<std::string> gGetLoggerAlarms(); ///< Get a copy of the recent alarm list.
const std::string timestr(); // A timestamp to print in messages.
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
/**@ Global control and initialization of the logging system. */
//@{
/** Initialize the global logging system. */
void gLogInit(const char* name, const char* level=NULL, int facility=LOG_USER);
/** Get the logging level associated with a given file. */
int gGetLoggingLevel(const char *filename=NULL);
/** Allow early logging when still in constructors */
void gLogEarly(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
//@}
#endif
// vim: ts=4 sw=4

View File

@@ -22,8 +22,11 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
EXTRA_DIST = \
example.config \
README.common
noinst_LTLIBRARIES = libcommon.la
@@ -34,10 +37,20 @@ libcommon_la_SOURCES = \
Threads.cpp \
Timeval.cpp \
Logger.cpp \
Utils.cpp \
trx_vty.c \
debug.c
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
Configuration.cpp \
sqlite3util.cpp
noinst_PROGRAMS = \
BitVectorTest \
PRBSTest \
InterthreadTest \
SocketsTest \
TimevalTest \
VectorTest \
ConfigurationTest \
LogTest
# ReportingTest
noinst_HEADERS = \
BitVector.h \
@@ -48,9 +61,38 @@ noinst_HEADERS = \
Threads.h \
Timeval.h \
Vector.h \
Configuration.h \
Logger.h \
Utils.h \
trx_vty.h \
debug.h \
osmo_signal.h \
config_defs.h
sqlite3util.h
BitVectorTest_SOURCES = BitVectorTest.cpp
BitVectorTest_LDADD = libcommon.la $(SQLITE3_LIBS)
PRBSTest_SOURCES = PRBSTest.cpp
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 $(SQLITE3_LIBS)
ConfigurationTest_SOURCES = ConfigurationTest.cpp
ConfigurationTest_LDADD = libcommon.la $(SQLITE3_LIBS)
# ReportingTest_SOURCES = ReportingTest.cpp
# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
LogTest_SOURCES = LogTest.cpp
LogTest_LDADD = libcommon.la $(SQLITE3_LIBS)
MOSTLYCLEANFILES += testSource testDestination

View File

@@ -223,18 +223,18 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
UDPSocket::UDPSocket(unsigned short wSrcPort)
:DatagramSocket()
{
open(wSrcPort, wSrcIP);
open(wSrcPort);
}
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
const char *wDestIP, unsigned short wDestPort)
UDPSocket::UDPSocket(unsigned short wSrcPort,
const char * wDestIP, unsigned short wDestPort )
:DatagramSocket()
{
open(wSrcPort, wSrcIP);
open(wSrcPort);
destination(wDestPort, wDestIP);
}
@@ -246,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
}
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
void UDPSocket::open(unsigned short localPort)
{
// create
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
@@ -265,7 +265,7 @@ void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
size_t length = sizeof(address);
bzero(&address,length);
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(wlocalIP);
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_port = htons(localPort);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");
@@ -284,4 +284,50 @@ unsigned short UDPSocket::port() const
return ntohs(name.sin_port);
}
UDDSocket::UDDSocket(const char* localPath, const char* remotePath)
:DatagramSocket()
{
if (localPath!=NULL) open(localPath);
if (remotePath!=NULL) destination(remotePath);
}
void UDDSocket::open(const char* localPath)
{
// create
mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0);
if (mSocketFD<0) {
perror("socket() failed");
throw SocketError();
}
// bind
struct sockaddr_un address;
size_t length = sizeof(address);
bzero(&address,length);
address.sun_family = AF_UNIX;
strcpy(address.sun_path,localPath);
unlink(localPath);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");
throw SocketError();
}
}
void UDDSocket::destination(const char* remotePath)
{
struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination;
strcpy(unAddr->sun_path,remotePath);
}
// vim:ts=4:sw=4

View File

@@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket {
public:
/** Open a USP socket with an OS-assigned port and no default destination. */
UDPSocket(const char *localIP, unsigned short localPort);
UDPSocket( unsigned short localPort=0);
/** Given a full specification, open the socket and set the dest address. */
UDPSocket(const char *localIP, unsigned short localPort,
const char *remoteIP, unsigned short remotePort);
UDPSocket( unsigned short localPort,
const char * remoteIP, unsigned short remotePort);
/** Set the destination port. */
void destination( unsigned short wDestPort, const char * wDestIP );
@@ -157,7 +157,7 @@ public:
unsigned short port() const;
/** Open and bind the UDP socket to a local port. */
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
void open(unsigned short localPort=0);
/** Give the return address of the most recently received packet. */
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
@@ -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

View File

@@ -30,28 +30,21 @@
#include "Threads.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
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;
readSocket->nonblocking();
UDPSocket readSocket(5934, "localhost", 5061);
readSocket.nonblocking();
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH+1] = { 0 };
int count = readSocket->read(buf, MAX_UDP_LENGTH);
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf, MAX_UDP_LENGTH);
if (count>0) {
buf[count] = 0;
CERR("read: " << buf);
COUT("read: " << buf);
rc++;
} else {
sleep(2);
@@ -60,42 +53,51 @@ void *testReaderIP(void *param)
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, MAX_UDP_LENGTH);
if (count>0) {
COUT("read: " << buf);
rc++;
} else {
sleep(2);
}
}
return NULL;
}
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;
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
sleep(1);
for (int i=0; i<gNumToSend; i++) {
CERR("write");
count = socket1.write("Hello IP land");
if (count < 0) {
COUT("FAIL: write");
exit(EXIT_FAILURE);
}
sleep(1);
socket1.write("Hello IP land");
socket1U.write("Hello Unix domain");
sleep(1);
}
readerThreadIP.join();
printf("Done\n");
readerThreadUnix.join();
}
// vim: ts=4 sw=4

View File

@@ -24,17 +24,11 @@
*/
#include <string.h>
#include <sys/types.h>
#include "Threads.h"
#include "Timeval.h"
#include "Logger.h"
#ifndef gettid
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif
using namespace std;
@@ -108,19 +102,6 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const
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::start(void *(*task)(void*), void *arg)
{

View File

@@ -141,8 +141,6 @@ class Signal {
#define START_THREAD(thread,function,argument) \
thread.start((void *(*)(void*))function, (void*)argument);
void set_selfthread_name(const char *name);
/** A C++ wrapper for pthread threads. */
class Thread {

View File

@@ -28,37 +28,18 @@
#include "Timeval.h"
#include <iostream>
#include <assert.h>
#include <sys/time.h>
using namespace std;
int main(int argc, char *argv[])
{
long last_remaining = 10000;
Timeval then(last_remaining);
assert(then.elapsed() == -last_remaining);
cerr << then << " elapsed: " << then.elapsed() << endl;
/* Check that last_remaining parameter affects setting time in the future */
usleep(10000);
double increased_time_secs = Timeval().seconds();
assert(increased_time_secs <= then.seconds());
struct timespec invariant_time = then.timespec();
int loops = 0;
Timeval then(10000);
cout << then.elapsed() << endl;
while (!then.passed()) {
struct timespec tspecnow = then.timespec();
cerr << "now: " << Timeval().seconds() << " then: " << then << " remaining: " << then.remaining() << endl;
assert(last_remaining >= then.remaining());
assert(tspecnow.tv_sec == invariant_time.tv_sec && tspecnow.tv_nsec == invariant_time.tv_nsec);
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
usleep(500000);
loops++;
}
cerr << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
assert(then.remaining() <= 0);
assert(loops >= 18);
printf("Done\n");
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* 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 <vector>
#include <string>
#include <sstream>
std::vector<std::string> comma_delimited_to_vector(char* opt)
{
std::string str = std::string(opt);
std::vector<std::string> result;
std::stringstream ss(str);
while( ss.good() )
{
std::string substr;
getline(ss, substr, ',');
result.push_back(substr);
}
return result;
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* 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
#include <vector>
#include <string>
std::vector<std::string> comma_delimited_to_vector(char* opt);

View File

@@ -32,14 +32,11 @@
#include <string.h>
#include <iostream>
#include <assert.h>
#include <stdlib.h>
// We cant use Logger.h in this file...
extern int gVectorDebug;
#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.
@@ -63,8 +60,6 @@ template <class T> class Vector {
T* mData; ///< allocated data block, if any
T* mStart; ///< start of useful data
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:
@@ -90,19 +85,9 @@ template <class T> class Vector {
/** Change the size of the Vector, discarding content. */
void resize(size_t newSize)
{
if (mData!=NULL) {
if (mFreeFunc)
mFreeFunc(mData);
else
delete[] mData;
}
if (mData!=NULL) delete[] mData;
if (newSize==0) mData=NULL;
else {
if (mAllocFunc)
mData = (T*) mAllocFunc(newSize);
else
mData = new T[newSize];
}
else mData = new T[newSize];
mStart = mData;
mEnd = mStart + newSize;
}
@@ -122,7 +107,7 @@ template <class T> class Vector {
void clone(const Vector<T>& other)
{
resize(other.size());
other.copyTo(*this);
memcpy(mData,other.mStart,other.bytes());
}
@@ -131,31 +116,29 @@ template <class T> class Vector {
//@{
/** Build an empty Vector of a given size. */
Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
{ resize(wSize); }
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
/** Build a Vector by moving another. */
Vector(Vector<T>&& other)
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc)
/** Build a Vector by shifting the data block. */
Vector(Vector<T>& other)
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
{ other.mData=NULL; }
/** 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. */
Vector(T* wData, T* wStart, T* wEnd, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(wData),mStart(wStart),mEnd(wEnd), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
Vector(T* wData, T* wStart, T* wEnd)
:mData(wData),mStart(wStart),mEnd(wEnd)
{ }
/** 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)
:mData(NULL),mStart(wStart),mEnd(wStart+span),mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
Vector(T* wStart, size_t span)
:mData(NULL),mStart(wStart),mEnd(wStart+span)
{ }
/** Build a Vector by concatenation. */
Vector(const Vector<T>& other1, const Vector<T>& other2, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
Vector(const Vector<T>& other1, const Vector<T>& other2)
:mData(NULL)
{
resize(other1.size()+other2.size());
memcpy(mStart, other1.mStart, other1.bytes());
@@ -179,8 +162,6 @@ template <class T> class Vector {
mData=other.mData;
mStart=other.mStart;
mEnd=other.mEnd;
mAllocFunc=other.mAllocFunc;
mFreeFunc=other.mFreeFunc;
other.mData=NULL;
}
@@ -223,15 +204,10 @@ template <class T> class Vector {
*/
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
{
unsigned int i;
T* dst = other.mStart + start;
T* src = mStart;
assert(dst+span<=other.mEnd);
T* base = other.mStart + start;
assert(base+span<=other.mEnd);
assert(mStart+span<=mEnd);
for (i = 0; i < span; i++, src++, dst++)
*dst = *src;
/*TODO if not non-trivially copyable type class, optimize:
memcpy(dst,mStart,span*sizeof(T)); */
memcpy(base,mStart,span*sizeof(T));
}
/** Copy all of this Vector to a segment of another Vector. */
@@ -306,7 +282,7 @@ template <class T> class Vector {
T* end() { return mEnd; }
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
//@}
};

View File

@@ -28,6 +28,10 @@
#include "Vector.h"
#include <iostream>
// We must have a gConfig now to include Vector.
#include "Configuration.h"
ConfigurationTable gConfig;
using namespace std;
typedef Vector<int> TestVector;

View File

@@ -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,
};

View File

@@ -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),
};

View File

@@ -1,11 +0,0 @@
#pragma once
extern const struct log_info log_info;
/* Debug Areas of the code */
enum {
DMAIN,
DTRXCTRL,
DDEV,
DLMS,
};

View File

@@ -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
View 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
View 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

View File

@@ -1,576 +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;
} else if (strcmp("enable", argv[0]) == 0) {
trx->cfg.multi_arfcn = true;
} else {
return CMD_WARNING;
}
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_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;
}
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);
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, " 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_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;
}

View File

@@ -1,68 +0,0 @@
#pragma once
#include <osmocom/vty/command.h>
#include "config_defs.h"
extern struct vty_app_info g_vty_info;
#define TRX_CHAN_MAX 8
/* 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 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);

View File

@@ -54,10 +54,7 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
// |-head-||---------midamble----------------------||--------------data----------------||t|
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");

View File

@@ -52,9 +52,7 @@ extern const BitVector gEdgeTrainingSequence[];
extern const BitVector gDummyBurst;
/** Random access burst synch. sequence */
extern const BitVector gRACHSynchSequenceTS0;
extern const BitVector gRACHSynchSequenceTS1;
extern const BitVector gRACHSynchSequenceTS2;
extern const BitVector gRACHSynchSequence;
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
extern const BitVector gRACHBurst;

View File

@@ -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.
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
running configure.
To run osmo-trx, the following should be installed:
libuhd (https://gnuradio.org).
To run OpenBTS, the following should be installed:
Asterisk (http://www.asterisk.org), running SIP on port 5060.
libosip2 (http://www.gnu.org/software/osip/)
libortp (http://freshmeat.net/projects/ortp/)
libusrp (http://gnuradio.org).
This is part of the GNURadio installation.
It is the only part used by OpenBTS.
OpenBTS logs to syslogd as facility LOG_LOCAL7. Please set your /etc/syslog.conf
accordingly.
For information on specific executables, see tests/README.tests and
apps/README.apps.
See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more
information.

20
LEGAL
View File

@@ -1,8 +1,5 @@
OpenBTS
The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details.
The related copyrights:
Most parts copyright 2008-2011 Free Software Foundation.
Some parts copyright 2010 Kestrel Signal Processing, Inc.
Some parts copyright 2011 Range Networks, Inc.
@@ -15,9 +12,17 @@ patented technologies. The user of this software is required to take whatever
actions are necessary to avoid patent infringement.
Trademark
"OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a
California corporation. Range reserves the right to control the use of this
trademark. Do not use this trademark in commerce without permission and do not
rebrand OpenBTS under a different trademark.
Telecom and Radio Spectrum Laws
The primary function of 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
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.
@@ -34,7 +39,7 @@ The legal restrictions listed here are not necessarily exhaustive.
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
"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
@@ -49,12 +54,13 @@ and AGPLv3.
Note to US Government Contractors
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
copyright holders, including the Free Software Foundation, Inc.
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.

View File

@@ -21,19 +21,16 @@
include $(top_srcdir)/Makefile.common
ACLOCAL_AMFLAGS = -I config
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
AM_CXXFLAGS = -Wall -pthread
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES) $(SQLITE3_CFLAGS)
AM_CXXFLAGS = -Wall -pthread -ldl
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
# Order must be preserved
SUBDIRS = \
doc \
CommonLibs \
GSM \
Transceiver52M \
contrib \
tests
Transceiver52M
EXTRA_DIST = \
autogen.sh \
@@ -42,12 +39,6 @@ EXTRA_DIST = \
COPYING \
README
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
.PHONY: release
@RELMAKE@
dox: FORCE
doxygen doxconfig

View File

@@ -18,6 +18,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
top_srcdir = $(abs_top_srcdir)
top_builddir = $(abs_top_builddir)
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
GSM_INCLUDEDIR = $(top_srcdir)/GSM
@@ -29,10 +32,4 @@ STD_DEFINES_AND_INCLUDES = \
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
GSM_LA = $(top_builddir)/GSM/libGSM.la
if ARCH_ARM
ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la
else
ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la
endif
MOSTLYCLEANFILES = *~

View File

@@ -25,11 +25,12 @@
#include <string.h>
#include <cstdio>
#include "Logger.h"
#include "Channelizer.h"
extern "C" {
#include "fft.h"
#include "convolve.h"
#include "common/fft.h"
#include "common/convolve.h"
}
static void deinterleave(const float *in, size_t ilen,

View File

@@ -29,7 +29,7 @@
#include "ChannelizerBase.h"
extern "C" {
#include "fft.h"
#include "common/fft.h"
}
static float sinc(float x)
@@ -239,7 +239,7 @@ ChannelizerBase::~ChannelizerBase()
for (size_t i = 0; i < m; i++) {
free(subFilters[i]);
delete[] hist[i];
delete hist[i];
}
fft_free(fftInput);

View File

@@ -21,10 +21,22 @@
include $(top_srcdir)/Makefile.common
SUBDIRS = arch device
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common
AM_CXXFLAGS = -ldl -lpthread
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
SUBDIRS = arm x86
if ARCH_ARM
ARCH_LA = arm/libarch.la
else
ARCH_LA = x86/libarch.la
endif
if USRP1
AM_CPPFLAGS += $(USRP_CFLAGS)
else
AM_CPPFLAGS += $(UHD_CFLAGS)
endif
rev2dir = $(datadir)/usrp/rev2
rev4dir = $(datadir)/usrp/rev4
@@ -32,9 +44,11 @@ rev4dir = $(datadir)/usrp/rev4
dist_rev2_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 = \
radioInterface.cpp \
@@ -46,66 +60,66 @@ COMMON_SOURCES = \
Transceiver.cpp \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp
Synthesis.cpp \
common/fft.c
libtransceiver_common_la_SOURCES = \
libtransceiver_la_SOURCES = \
$(COMMON_SOURCES) \
Resampler.cpp \
radioInterfaceResamp.cpp \
radioInterfaceMulti.cpp
bin_PROGRAMS = \
osmo-trx \
osmo-trx-gen \
osmo-trx-dec
noinst_HEADERS = \
Complex.h \
radioInterface.h \
radioVector.h \
radioClock.h \
radioDevice.h \
radioBuffer.h \
sigProcLib.h \
signalVector.h \
Transceiver.h \
USRPDevice.h \
Resampler.h \
ChannelizerBase.h \
Channelizer.h \
Synthesis.h
Synthesis.h \
common/convolve.h \
common/convert.h \
common/scale.h \
common/mult.h \
common/fft.h
COMMON_LDADD = \
libtransceiver_common.la \
osmo_trx_SOURCES = osmo-trx.cpp
osmo_trx_LDADD = \
libtransceiver.la \
$(ARCH_LA) \
$(GSM_LA) \
$(COMMON_LA) \
$(FFTWF_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS)
$(COMMON_LA) $(SQLITE3_LIBS)
bin_PROGRAMS =
osmo_trx_gen_SOURCES = osmo-trx-gen.cpp
osmo_trx_gen_LDADD = \
libtransceiver.la \
$(ARCH_LA) \
$(GSM_LA) \
$(COMMON_LA) $(SQLITE_LA)
if DEVICE_UHD
bin_PROGRAMS += osmo-trx-uhd
osmo_trx_uhd_SOURCES = osmo-trx.cpp
osmo_trx_uhd_LDADD = \
$(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)
osmo_trx_dec_SOURCES = osmo-trx-dec.cpp
osmo_trx_dec_LDADD = \
libtransceiver.la \
$(ARCH_LA) \
$(GSM_LA) \
$(COMMON_LA) $(SQLITE_LA)
if USRP1
libtransceiver_la_SOURCES += USRPDevice.cpp
osmo_trx_LDADD += $(USRP_LIBS)
else
libtransceiver_la_SOURCES += UHDDevice.cpp
osmo_trx_LDADD += $(UHD_LIBS) $(FFTWF_LIBS)
endif

View File

@@ -22,7 +22,6 @@
#include <string.h>
#include <malloc.h>
#include <iostream>
#include <algorithm>
#include "Resampler.h"
@@ -36,8 +35,6 @@ extern "C" {
#define MAX_OUTPUT_LEN 4096
using namespace std;
static float sinc(float x)
{
if (x == 0.0)
@@ -46,19 +43,32 @@ static float sinc(float x)
return sin(M_PI * x) / (M_PI * x);
}
void Resampler::initFilters(float bw)
bool Resampler::initFilters(float bw)
{
float cutoff;
size_t proto_len = p * filt_len;
float *proto, val, cutoff;
float sum = 0.0f, scale = 0.0f;
float midpt = (float) (proto_len - 1.0) / 2.0;
/*
* Allocate partition filters and the temporary prototype filter
* according to numerator of the rational rate. Coefficients are
* real only and must be 16-byte memory aligned for SSE usage.
*/
auto proto = vector<float>(p * filt_len);
for (auto &part : partitions)
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
proto = new float[proto_len];
if (!proto)
return false;
partitions = (float **) malloc(sizeof(float *) * p);
if (!partitions) {
delete[] proto;
return false;
}
for (size_t i = 0; i < p; i++) {
partitions[i] = (float *)
memalign(16, filt_len * 2 * sizeof(float));
}
/*
* Generate the prototype filter with a Blackman-harris window.
@@ -75,26 +85,47 @@ void Resampler::initFilters(float bw)
else
cutoff = (float) q;
float midpt = (proto.size() - 1) / 2.0;
for (size_t i = 0; i < proto.size(); i++) {
for (size_t i = 0; i < proto_len; i++) {
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
proto[i] *= a0 -
a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
a3 * cos(6 * M_PI * i / (proto.size() - 1));
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
a3 * cos(6 * M_PI * i / (proto_len - 1));
sum += proto[i];
}
scale = p / sum;
/* Populate filter partitions from the prototype filter */
for (size_t i = 0; i < filt_len; i++) {
for (size_t n = 0; n < p; n++)
partitions[n][i] = complex<float>(proto[i * p + n] * scale);
for (size_t n = 0; n < p; n++) {
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
partitions[n][2 * i + 1] = 0.0f;
}
}
/* Store filter taps in reverse */
for (auto &part : partitions)
reverse(&part[0], &part[filt_len]);
/* For convolution, we store the filter taps in reverse */
for (size_t n = 0; n < p; n++) {
for (size_t i = 0; i < filt_len / 2; i++) {
val = partitions[n][2 * i];
partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
partitions[n][2 * (filt_len - 1 - i)] = val;
}
}
delete[] proto;
return true;
}
void Resampler::releaseFilters()
{
if (partitions) {
for (size_t i = 0; i < p; i++)
free(partitions[i]);
}
free(partitions);
partitions = NULL;
}
static bool check_vec_len(int in_len, int out_len, int p, int q)
@@ -128,6 +159,14 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
return true;
}
void Resampler::computePath()
{
for (int i = 0; i < MAX_OUTPUT_LEN; i++) {
in_index[i] = (q * i) / p;
out_path[i] = (q * i) % p;
}
}
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
{
int n, path;
@@ -141,8 +180,8 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
path = out_path[i];
convolve_real(in, in_len,
reinterpret_cast<float *>(partitions[path]),
filt_len, &out[2 * i], out_len - i,
partitions[path], filt_len,
&out[2 * i], out_len - i,
n, 1, 1, 0);
}
@@ -151,18 +190,14 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
bool Resampler::init(float bw)
{
if (p == 0 || q == 0 || filt_len == 0) return false;
/* Filterbank filter internals */
initFilters(bw);
if (!initFilters(bw))
return false;
/* Precompute filterbank paths */
int i = 0;
for (auto &index : in_index)
index = (q * i++) / p;
i = 0;
for (auto &path : out_path)
path = (q * i++) % p;
in_index = new size_t[MAX_OUTPUT_LEN];
out_path = new size_t[MAX_OUTPUT_LEN];
computePath();
return true;
}
@@ -173,7 +208,7 @@ size_t Resampler::len()
}
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
: in_index(NULL), out_path(NULL), partitions(NULL)
{
this->p = p;
this->q = q;
@@ -182,6 +217,8 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len)
Resampler::~Resampler()
{
for (auto &part : partitions)
free(part);
releaseFilters();
delete in_index;
delete out_path;
}

View File

@@ -20,9 +20,6 @@
#ifndef _RESAMPLER_H_
#define _RESAMPLER_H_
#include <vector>
#include <complex>
class Resampler {
public:
/* Constructor for rational sample rate conversion
@@ -66,11 +63,14 @@ private:
size_t p;
size_t q;
size_t filt_len;
std::vector<size_t> in_index;
std::vector<size_t> out_path;
std::vector<std::complex<float> *> partitions;
size_t *in_index;
size_t *out_path;
void initFilters(float bw);
float **partitions;
bool initFilters(float bw);
void releaseFilters();
void computePath();
};
#endif /* _RESAMPLER_H_ */

View File

@@ -24,13 +24,13 @@
#include <assert.h>
#include <string.h>
#include <cstdio>
#include <iostream>
#include "Logger.h"
#include "Synthesis.h"
extern "C" {
#include "fft.h"
#include "convolve.h"
#include "common/fft.h"
#include "common/convolve.h"
}
static void interleave(float **in, size_t ilen,

View File

@@ -27,10 +27,6 @@
#include "Transceiver.h"
#include <Logger.h>
extern "C" {
#include "osmo_signal.h"
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -39,6 +35,12 @@ using namespace GSM;
#define USB_LATENCY_INTRVL 10,0
#if USE_UHD
# define USB_LATENCY_MIN 6,7
#else
# define USB_LATENCY_MIN 1,1
#endif
/* Number of running values use in noise average */
#define NOISE_CNT 20
@@ -69,7 +71,7 @@ TransceiverState::~TransceiverState()
}
}
bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
{
signalVector *burst;
@@ -79,19 +81,19 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
for (size_t n = 0; n < 8; n++) {
for (size_t i = 0; i < 102; i++) {
switch (filler) {
case FILLER_DUMMY:
case Transceiver::FILLER_DUMMY:
burst = generateDummyBurst(sps, n);
break;
case FILLER_NORM_RAND:
burst = genRandNormalBurst(rtsc, sps, n);
case Transceiver::FILLER_NORM_RAND:
burst = genRandNormalBurst(rtsc, sps, n, mPrbs);
break;
case FILLER_EDGE_RAND:
case Transceiver::FILLER_EDGE_RAND:
burst = generateEdgeBurst(rtsc);
break;
case FILLER_ACCESS_RAND:
case Transceiver::FILLER_ACCESS_RAND:
burst = genRandAccessBurst(rach_delay, sps, n);
break;
case FILLER_ZERO:
case Transceiver::FILLER_ZERO:
default:
burst = generateEmptyBurst(sps, n);
}
@@ -100,8 +102,8 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
fillerTable[i][n] = burst;
}
if ((filler == FILLER_NORM_RAND) ||
(filler == FILLER_EDGE_RAND)) {
if ((filler == Transceiver::FILLER_NORM_RAND) ||
(filler == Transceiver::FILLER_EDGE_RAND)) {
chanType[n] = TSC;
}
}
@@ -110,17 +112,16 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
}
Transceiver::Transceiver(int wBasePort,
const char *TRXAddress,
const char *GSMcoreAddress,
const char *wTRXAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset)
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
: mBasePort(wBasePort), mAddr(wTRXAddress),
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset), sig_cbfn(NULL),
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
rssiOffset(wRssiOffset),
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
mWriteBurstToDiskMask(0)
{
@@ -159,7 +160,7 @@ Transceiver::~Transceiver()
* are still expected to report clock indications through control channel
* activity.
*/
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge)
bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
{
int d_srcport, d_dstport, c_srcport, c_dstport;
@@ -196,8 +197,8 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool
d_srcport = mBasePort + 2 * i + 2;
d_dstport = mBasePort + 2 * i + 102;
mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
}
/* Randomize the central clock */
@@ -223,17 +224,6 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool
return true;
}
void Transceiver::setSignalHandler(osmo_signal_cbfn cbfn)
{
if (this->sig_cbfn)
osmo_signal_unregister_handler(SS_TRANSC, this->sig_cbfn, NULL);
if (cbfn) {
this->sig_cbfn = cbfn;
osmo_signal_register_handler(SS_TRANSC, this->sig_cbfn, NULL);
}
}
/*
* Start the transceiver
*
@@ -283,7 +273,7 @@ bool Transceiver::start()
TxUpperLoopAdapter, (void*) chan);
}
mForceClockInterface = true;
writeClockInterface();
mOn = true;
return true;
}
@@ -307,10 +297,6 @@ void Transceiver::stop()
LOG(NOTICE) << "Stopping the transceiver";
mTxLowerLoopThread->cancel();
mRxLowerLoopThread->cancel();
mTxLowerLoopThread->join();
mRxLowerLoopThread->join();
delete mTxLowerLoopThread;
delete mRxLowerLoopThread;
for (size_t i = 0; i < mChans; i++) {
mRxServiceLoopThreads[i]->cancel();
@@ -329,6 +315,11 @@ void Transceiver::stop()
mTxPriorityQueues[i].clear();
}
mTxLowerLoopThread->join();
mRxLowerLoopThread->join();
delete mTxLowerLoopThread;
delete mRxLowerLoopThread;
mOn = false;
LOG(NOTICE) << "Transceiver stopped";
}
@@ -388,8 +379,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
state = &mStates[i];
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->USRP interface ("
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
if (state->mRetrans)
updateFillerTable(i, burst);
delete burst;
@@ -440,7 +430,7 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
case V:
state->fillerModulus[timeslot] = 51;
break;
//case V:
//case V:
case VII:
state->fillerModulus[timeslot] = 102;
break;
@@ -555,7 +545,7 @@ void writeToFile(radioVector *radio_burst, size_t chan)
/*
* Pull bursts from the FIFO and handle according to the slot
* and burst correlation type. Equalzation is currently disabled.
* and burst correlation type. Equalzation is currently disabled.
*/
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
@@ -617,7 +607,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
avg = sqrt(avg / radio_burst->chans());
wTime = time;
RSSI = 20.0 * log10(rxFullScale / avg);
RSSI = 20.0 * log10(avg / rxFullScale);
/* RSSI estimation are valid */
isRssiValid = true;
@@ -666,67 +656,42 @@ void Transceiver::reset()
mTxPriorityQueues[i].clear();
}
#define MAX_PACKET_LENGTH 100
/**
* Matches a buffer with a command.
* @param buf a buffer to look command in
* @param cmd a command to look in buffer
* @param params pointer to arguments, or NULL
* @return true if command matches, otherwise false
*/
static bool match_cmd(char *buf,
const char *cmd, char **params)
{
size_t cmd_len = strlen(cmd);
/* Check a command itself */
if (strncmp(buf, cmd, cmd_len))
return false;
/* A command has arguments */
if (params != NULL) {
/* Make sure there is a space */
if (buf[cmd_len] != ' ')
return false;
/* Update external pointer */
*params = buf + cmd_len + 1;
}
return true;
}
void Transceiver::driveControl(size_t chan)
{
char buffer[MAX_PACKET_LENGTH + 1];
char response[MAX_PACKET_LENGTH + 1];
char *command, *params;
int msgLen;
int MAX_PACKET_LENGTH = 100;
/* Attempt to read from control socket */
msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
if (msgLen < 1)
return;
// check control socket
char buffer[MAX_PACKET_LENGTH];
int msgLen = -1;
buffer[0] = '\0';
/* Zero-terminate received string */
buffer[msgLen] = '\0';
msgLen = mCtrlSockets[chan]->read(buffer, sizeof(buffer));
/* Verify a command signature */
if (strncmp(buffer, "CMD ", 4)) {
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
if (msgLen < 1) {
return;
}
/* Set command pointer */
command = buffer + 4;
LOGC(DTRXCTRL, INFO) << "chan " << chan << ": command is '" << command << "'";
char cmdcheck[4];
char command[MAX_PACKET_LENGTH];
char response[MAX_PACKET_LENGTH];
if (match_cmd(command, "POWEROFF", NULL)) {
sscanf(buffer,"%3s %s",cmdcheck,command);
if (!chan)
writeClockInterface();
if (strcmp(cmdcheck,"CMD")!=0) {
LOG(WARNING) << "bogus message on control interface";
return;
}
LOG(INFO) << "command is " << buffer;
if (strcmp(command,"POWEROFF")==0) {
stop();
sprintf(response,"RSP POWEROFF 0");
} else if (match_cmd(command, "POWERON", NULL)) {
}
else if (strcmp(command,"POWERON")==0) {
if (!start()) {
sprintf(response,"RSP POWERON 1");
} else {
@@ -736,43 +701,41 @@ void Transceiver::driveControl(size_t chan)
mHandover[i][j] = false;
}
}
} else if (match_cmd(command, "HANDOVER", &params)) {
unsigned ts = 0, ss = 0;
sscanf(params, "%u %u", &ts, &ss);
if (ts > 7 || ss > 7) {
sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
} else {
mHandover[ts][ss] = true;
sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
}
} else if (match_cmd(command, "NOHANDOVER", &params)) {
unsigned ts = 0, ss = 0;
sscanf(params, "%u %u", &ts, &ss);
if (ts > 7 || ss > 7) {
sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
} else {
mHandover[ts][ss] = false;
sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
}
} else if (match_cmd(command, "SETMAXDLY", &params)) {
}
else if (strcmp(command,"HANDOVER")==0){
int ts=0,ss=0;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
mHandover[ts][ss] = true;
sprintf(response,"RSP HANDOVER 0 %d %d",ts,ss);
}
else if (strcmp(command,"NOHANDOVER")==0){
int ts=0,ss=0;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
mHandover[ts][ss] = false;
sprintf(response,"RSP NOHANDOVER 0 %d %d",ts,ss);
}
else if (strcmp(command,"SETMAXDLY")==0) {
//set expected maximum time-of-arrival
int maxDelay;
sscanf(params, "%d", &maxDelay);
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
} else if (match_cmd(command, "SETMAXDLYNB", &params)) {
}
else if (strcmp(command,"SETMAXDLYNB")==0) {
//set expected maximum time-of-arrival
int maxDelay;
sscanf(params, "%d", &maxDelay);
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
} else if (match_cmd(command, "SETRXGAIN", &params)) {
}
else if (strcmp(command,"SETRXGAIN")==0) {
//set expected maximum time-of-arrival
int newGain;
sscanf(params, "%d", &newGain);
sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
newGain = mRadioInterface->setRxGain(newGain, chan);
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
} else if (match_cmd(command, "NOISELEV", NULL)) {
}
else if (strcmp(command,"NOISELEV")==0) {
if (mOn) {
float lev = mStates[chan].mNoiseLev;
sprintf(response,"RSP NOISELEV 0 %d",
@@ -781,78 +744,86 @@ void Transceiver::driveControl(size_t chan)
else {
sprintf(response,"RSP NOISELEV 1 0");
}
} else if (match_cmd(command, "SETPOWER", &params)) {
}
else if (!strcmp(command, "SETPOWER")) {
int power;
sscanf(params, "%d", &power);
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP SETPOWER 0 %d", power);
} else if (match_cmd(command, "ADJPOWER", &params)) {
}
else if (!strcmp(command,"ADJPOWER")) {
int power, step;
sscanf(params, "%d", &step);
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
power = mStates[chan].mPower + step;
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP ADJPOWER 0 %d", power);
} else if (match_cmd(command, "RXTUNE", &params)) {
}
else if (strcmp(command,"RXTUNE")==0) {
// tune receiver
int freqKhz;
sscanf(params, "%d", &freqKhz);
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
mRxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
LOG(ALERT) << "RX failed to tune";
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
}
else
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
} else if (match_cmd(command, "TXTUNE", &params)) {
}
else if (strcmp(command,"TXTUNE")==0) {
// tune txmtr
int freqKhz;
sscanf(params, "%d", &freqKhz);
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
mTxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
LOG(ALERT) << "TX failed to tune";
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
}
else
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
} else if (match_cmd(command, "SETTSC", &params)) {
}
else if (!strcmp(command,"SETTSC")) {
// set TSC
unsigned TSC;
sscanf(params, "%u", &TSC);
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
if (TSC > 7) {
sprintf(response, "RSP SETTSC 1 %d", TSC);
} else {
LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
mTSC = TSC;
sprintf(response,"RSP SETTSC 0 %d", TSC);
}
} else if (match_cmd(command, "SETSLOT", &params)) {
}
else if (strcmp(command,"SETSLOT")==0) {
// set slot type
int corrCode;
int timeslot;
sscanf(params, "%d %d", &timeslot, &corrCode);
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
if ((timeslot < 0) || (timeslot > 7)) {
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
LOG(WARNING) << "bogus message on control interface";
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
return;
}
}
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
setModulus(timeslot, chan);
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
} else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
}
else if (strcmp(command,"_SETBURSTTODISKMASK")==0) {
// debug command! may change or disapear without notice
// set a mask which bursts to dump to disk
int mask;
sscanf(params, "%d", &mask);
sscanf(buffer,"%3s %s %d",cmdcheck,command,&mask);
mWriteBurstToDiskMask = mask;
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
} else {
LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
}
else {
LOG(WARNING) << "bogus command " << command << " on control interface.";
sprintf(response,"RSP ERR 1");
}
LOGC(DTRXCTRL, INFO) << "chan " << chan << ": response is '" << response << "'";
mCtrlSockets[chan]->write(response, strlen(response) + 1);
}
@@ -882,14 +853,14 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
int RSSI = (int) buffer[5];
BitVector newBurst(burstLen);
BitVector::iterator itr = newBurst.begin();
char *bufferItr = buffer+6;
while (itr < newBurst.end())
while (itr < newBurst.end())
*itr++ = *bufferItr++;
GSM::Time currTime = GSM::Time(frameNum,timeSlot);
addRadioVector(chan, newBurst, RSSI, currTime);
@@ -901,15 +872,11 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
void Transceiver::driveReceiveRadio()
{
int rc = mRadioInterface->driveReceiveRadio();
if (rc == 0) {
if (!mRadioInterface->driveReceiveRadio()) {
usleep(100000);
} else if (rc < 0) {
LOG(FATAL) << "radio Interface receive failed, requesting stop.";
osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this);
} else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
mForceClockInterface = false;
writeClockInterface();
} else {
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
writeClockInterface();
}
}
@@ -979,7 +946,7 @@ void Transceiver::driveTxFIFO()
{
/**
Features a carefully controlled latency mechanism, to
Features a carefully controlled latency mechanism, to
assure that transmit packets arrive at the radio/USRP
before they need to be transmitted.
@@ -990,7 +957,7 @@ void Transceiver::driveTxFIFO()
RadioClock *radioClock = (mRadioInterface->getClock());
if (mOn) {
//radioClock->wait(); // wait until clock updates
LOG(DEBUG) << "radio clock " << radioClock->get();
@@ -1002,15 +969,14 @@ void Transceiver::driveTxFIFO()
// only update latency at the defined frame interval
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
<< radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
LOG(INFO) << "new latency: " << mTransmitLatency;
mLatencyUpdateTime = radioClock->get();
}
}
else {
// if underrun hasn't occurred in the last sec (216 frames) drop
// transmit latency by a timeslot
if (mTransmitLatency > mRadioInterface->minLatency()) {
if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
mTransmitLatency.decTN();
LOG(INFO) << "reduced latency: " << mTransmitLatency;
@@ -1046,15 +1012,11 @@ void Transceiver::writeClockInterface()
void *RxUpperLoopAdapter(TransceiverChannel *chan)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
delete chan;
snprintf(thread_name, 16, "RxUpper%zu", num);
set_selfthread_name(thread_name);
trx->setPriority(0.42);
while (1) {
@@ -1066,8 +1028,6 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan)
void *RxLowerLoopAdapter(Transceiver *transceiver)
{
set_selfthread_name("RxLower");
transceiver->setPriority(0.45);
while (1) {
@@ -1079,8 +1039,6 @@ void *RxLowerLoopAdapter(Transceiver *transceiver)
void *TxLowerLoopAdapter(Transceiver *transceiver)
{
set_selfthread_name("TxLower");
transceiver->setPriority(0.44);
while (1) {
@@ -1092,15 +1050,11 @@ void *TxLowerLoopAdapter(Transceiver *transceiver)
void *ControlServiceLoopAdapter(TransceiverChannel *chan)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
delete chan;
snprintf(thread_name, 16, "CtrlService%zu", num);
set_selfthread_name(thread_name);
while (1) {
trx->driveControl(num);
pthread_testcancel();
@@ -1110,15 +1064,11 @@ void *ControlServiceLoopAdapter(TransceiverChannel *chan)
void *TxUpperLoopAdapter(TransceiverChannel *chan)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
delete chan;
snprintf(thread_name, 16, "TxUpper%zu", num);
set_selfthread_name(thread_name);
trx->setPriority(0.40);
while (1) {

View File

@@ -30,11 +30,6 @@
#include <sys/types.h>
#include <sys/socket.h>
extern "C" {
#include <osmocom/core/signal.h>
#include "config_defs.h"
}
class Transceiver;
/** Channel descriptor for transceiver object and channel number pair */
@@ -59,7 +54,7 @@ struct TransceiverState {
~TransceiverState();
/* Initialize a multiframe slot in the filler table */
bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
int chanType[8];
@@ -89,22 +84,23 @@ struct TransceiverState {
/* Shadowed downlink attenuation */
int mPower;
/* Pseudorandom bit sequence */
PRBS9 mPrbs;
};
/** The Transceiver class, responsible for physical layer of basestation */
class Transceiver {
public:
/** Transceiver constructor
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX, as a string
@param GSMcoreAddress IP address of the GSM core, as a string
@param TRXAddress IP address of the TRX manager, as a string
@param wSPS number of samples per GSM symbol
@param wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
Transceiver(int wBasePort,
const char *TRXAddress,
const char *GSMcoreAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
@@ -114,7 +110,7 @@ public:
~Transceiver();
/** Start the control loop */
bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge);
bool init(int filler, size_t rtsc, unsigned rach_delay, bool edge);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -129,8 +125,6 @@ public:
/** accessor for number of channels */
size_t numChans() const { return mChans; };
void setSignalHandler(osmo_signal_cbfn cbfn);
/** Codes for channel combinations */
typedef enum {
FILL, ///< Channel is transmitted, but unused
@@ -151,10 +145,17 @@ public:
LOOPBACK ///< similar go VII, used in loopback testing
} ChannelCombination;
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
FILLER_NORM_RAND,
FILLER_EDGE_RAND,
FILLER_ACCESS_RAND,
};
private:
int mBasePort;
std::string mLocalAddr;
std::string mRemoteAddr;
std::string mAddr;
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
@@ -171,7 +172,7 @@ private:
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
RadioInterface *mRadioInterface; ///< associated radioInterface object
@@ -180,8 +181,6 @@ private:
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);
@@ -212,7 +211,6 @@ private:
bool mEdge;
bool mOn; ///< flag to indicate that transceiver is powered on
bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency
@@ -280,3 +278,4 @@ void *ControlServiceLoopAdapter(TransceiverChannel *);
/** transmit queueing thread loop */
void *TxUpperLoopAdapter(TransceiverChannel *);

View File

@@ -27,16 +27,17 @@
Compilation Flags
SWLOOPBACK compile for software loopback testing
*/
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "Logger.h"
#include "Threads.h"
#include "USRPDevice.h"
#include <Logger.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -58,15 +59,12 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
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)
USRPDevice::USRPDevice(size_t sps)
{
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;
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
* compensate for advance rather than delay.
*/
if (tx_sps == 1)
if (sps == 1)
pingOffset = 272;
else if (tx_sps == 4)
else if (sps == 4)
pingOffset = 269 - 7500;
else
pingOffset = 0;
#ifdef SWLOOPBACK
#ifdef SWLOOPBACK
samplePeriod = 1.0e6/actualSampleRate;
loopbackBufferSize = 0;
gettimeofday(&lastReadTime,NULL);
@@ -95,29 +93,29 @@ int USRPDevice::open(const std::string &, int, bool)
{
writeLock.unlock();
LOGC(DDEV, INFO) << "opening USRP device..";
#ifndef SWLOOPBACK
LOG(INFO) << "opening USRP device..";
#ifndef SWLOOPBACK
string rbf = "std_inband.rbf";
//string rbf = "inband_1rxhb_1tx.rbf";
//string rbf = "inband_1rxhb_1tx.rbf";
m_uRx.reset();
if (!skipRx) {
try {
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,
1024, 16 * 8, rbf));
m_uRx->set_fpga_master_clock_freq(masterClockRate);
}
catch(...) {
LOGC(DDEV, ALERT) << "make failed on Rx";
LOG(ALERT) << "make failed on Rx";
m_uRx.reset();
return -1;
}
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;
m_uRx.reset();
return -1;
@@ -132,14 +130,14 @@ int USRPDevice::open(const std::string &, int, bool)
}
catch(...) {
LOGC(DDEV, ALERT) << "make failed on Tx";
LOG(ALERT) << "make failed on Tx";
m_uTx.reset();
return -1;
}
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;
m_uTx.reset();
return -1;
@@ -147,7 +145,7 @@ int USRPDevice::open(const std::string &, int, bool)
if (!skipRx) m_uRx->stop();
m_uTx->stop();
#endif
switch (dboardConfig) {
@@ -178,19 +176,19 @@ int USRPDevice::open(const std::string &, int, bool)
samplesRead = 0;
samplesWritten = 0;
started = false;
return NORMAL;
}
bool USRPDevice::start()
bool USRPDevice::start()
{
LOGC(DDEV, INFO) << "starting USRP...";
#ifndef SWLOOPBACK
LOG(INFO) << "starting USRP...";
#ifndef SWLOOPBACK
if (!m_uRx && !skipRx) return false;
if (!m_uTx) return false;
if (!skipRx) m_uRx->stop();
m_uTx->stop();
@@ -220,8 +218,8 @@ bool USRPDevice::start()
hi32Timestamp = 0;
isAligned = false;
if (!skipRx)
if (!skipRx)
started = (m_uRx->start() && m_uTx->start());
else
started = m_uTx->start();
@@ -232,14 +230,14 @@ bool USRPDevice::start()
#endif
}
bool USRPDevice::stop()
bool USRPDevice::stop()
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uRx) return false;
if (!m_uTx) return false;
delete[] currData;
started = !(m_uRx->stop() && m_uTx->stop());
return !started;
#else
@@ -260,7 +258,7 @@ double USRPDevice::minTxGain()
double USRPDevice::maxRxGain()
{
return m_dbRx->gain_max();
}
}
double USRPDevice::minRxGain()
{
@@ -270,7 +268,7 @@ double USRPDevice::minRxGain()
double USRPDevice::setTxGain(double dB, size_t chan)
{
if (chan) {
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
@@ -280,10 +278,10 @@ double USRPDevice::setTxGain(double dB, size_t chan)
if (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))
LOGC(DDEV, ERR) << "Error setting TX gain";
LOG(ERR) << "Error setting TX gain";
writeLock.unlock();
@@ -294,7 +292,7 @@ double USRPDevice::setTxGain(double dB, size_t chan)
double USRPDevice::setRxGain(double dB, size_t chan)
{
if (chan) {
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
@@ -306,86 +304,38 @@ double USRPDevice::setRxGain(double dB, size_t chan)
if (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))
LOGC(DDEV, ERR) << "Error setting RX gain";
LOG(ERR) << "Error setting RX gain";
writeLock.unlock();
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
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uRx)
return 0;
short *buf = bufs[0];
timestamp += timestampOffset;
if (timestamp + len < timeStart) {
memset(buf,0,len*2*sizeof(short));
return len;
}
if (underrun) *underrun = false;
uint32_t readBuf[2000];
while (1) {
//guestimate USB read size
int readLen=0;
@@ -395,21 +345,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
if (readLen > 8000) readLen= (8000/512)*512;
}
// read USRP packets, parse and save A/D data as needed
readLen = m_uRx->read((void *)readBuf,readLen,overrun);
for (int pktNum = 0; pktNum < (readLen/512); pktNum++) {
for(int pktNum = 0; pktNum < (readLen/512); pktNum++) {
// tmpBuf points to start of a USB packet
uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
uint32_t chan = (word0 >> 16) & 0x1f;
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);
if (incrementHi32 && (timeStart!=0)) {
LOGC(DDEV, DEBUG) << "high 32 increment!!!";
LOG(DEBUG) << "high 32 increment!!!";
hi32Timestamp++;
}
pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
@@ -421,24 +371,24 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
timestamp -= timestampOffset;
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset;
LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
timestamp += timestampOffset;
isAligned = true;
}
continue;
}
if (chan != 0) {
LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
continue;
}
if ((word0 >> 28) & 0x04) {
if (underrun) *underrun = true;
LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
if (underrun) *underrun = true;
LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
}
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
if (!isAligned) continue;
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
while (cursorStart*2 > currDataSize) {
cursorStart -= currDataSize/2;
@@ -451,25 +401,25 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
else {
memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
}
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
}
}
LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
}
}
// copy desired data to buf
unsigned bufStart = dataStart+(timestamp-timeStart);
if (bufStart + len < currDataSize/2) {
LOGC(DDEV, DEBUG) << "bufStart: " << bufStart;
if (bufStart + len < currDataSize/2) {
LOG(DEBUG) << "bufStart: " << bufStart;
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
memset(data+bufStart*2,0,len*2*sizeof(short));
}
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);
LOGC(DDEV, DEBUG) << "firstLength: " << firstLength;
LOG(DEBUG) << "firstLength: " << firstLength;
memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
memset(data+bufStart*2,0,firstLength*2*sizeof(short));
memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
@@ -479,21 +429,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
timeStart = timestamp + len;
return len;
#else
if (loopbackBufferSize < 2) return 0;
int numSamples = 0;
struct timeval currTime;
gettimeofday(&currTime,NULL);
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
(currTime.tv_usec - lastReadTime.tv_usec);
if (timeElapsed < samplePeriod) {return 0;}
int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
if (numSamplesToRead < len) return 0;
if (numSamplesToRead > len) numSamplesToRead = len;
if (numSamplesToRead > loopbackBufferSize/2) {
firstRead =false;
firstRead =false;
numSamplesToRead = loopbackBufferSize/2;
}
memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
@@ -511,7 +461,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
firstRead = true;
}
samplesRead += numSamples;
return numSamples;
#endif
}
@@ -522,7 +472,7 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
{
writeLock.lock();
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uTx)
return 0;
@@ -569,14 +519,14 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
samplesWritten += retVal;
loopbackBufferSize += retVal*2;
return retVal;
#endif
}
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
short data[] = {0x00,0x02,0x00,0x00};
uint32_t *wordPtr = (uint32_t *) data;
*wordPtr = host_to_usrp_u32(*wordPtr);
@@ -593,25 +543,25 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
#endif
}
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
{
usrp_tune_result result;
if (chan) {
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
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
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
return true;
}
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
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
@@ -624,19 +574,19 @@ bool USRPDevice::setRxFreq(double wFreq, size_t chan)
usrp_tune_result result;
if (chan) {
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
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
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
return true;
}
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
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
@@ -651,21 +601,7 @@ bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
size_t chans, double)
{
if (tx_sps != rx_sps) {
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);
return new USRPDevice(tx_sps);
}

View File

@@ -48,6 +48,7 @@ private:
usrp_subdev_spec rxSubdevSpec;
usrp_subdev_spec txSubdevSpec;
int sps;
double actualSampleRate; ///< the actual USRP sampling rate
unsigned int decimRate; ///< the USRP decimation rate
@@ -82,10 +83,10 @@ private:
double rxGain;
#ifdef SWLOOPBACK
#ifdef SWLOOPBACK
short loopbackBuffer[1000000];
int loopbackBufferSize;
double samplePeriod;
double samplePeriod;
struct timeval startTime;
struct timeval lastReadTime;
@@ -95,9 +96,7 @@ private:
public:
/** Object constructor */
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
USRPDevice(size_t sps);
/** Instantiate the USRP */
int open(const std::string &, int, bool);
@@ -180,30 +179,14 @@ private:
/** return minimum Rx Gain **/
double minTxGain(void);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setRxAntenna(const std::string &ant, size_t chan = 0);
/* return the used RX path */
std::string getRxAntenna(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setTxAntenna(const std::string &ant, size_t chan = 0);
/* return the used RX path */
std::string getTxAntenna(size_t chan = 0);
/** return 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 */
inline double getTxFreq(size_t chan = 0) { return 0; }
inline double getRxFreq(size_t chan = 0) { return 0; }
inline double getSampleRate() { return actualSampleRate; }
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten; }
};
#endif // _USRP_DEVICE_H_

View File

@@ -1,8 +0,0 @@
include $(top_srcdir)/Makefile.common
SUBDIRS = common
if ARCH_ARM
SUBDIRS += arm
else
SUBDIRS += x86
endif

View File

@@ -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

View File

@@ -1,17 +1,17 @@
if ARCH_ARM
if ARCH_ARM_A15
ARCH_FLAGS = -mfpu=neon-vfpv4
else
ARCH_FLAGS = -mfpu=neon
endif
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I../common
AM_CCASFLAGS = $(ARCH_FLAGS)
noinst_LTLIBRARIES = libarch.la
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
libarch_la_SOURCES = \
../common/convolve_base.c \
convert.c \
convert_neon.S \
convolve.c \
@@ -20,3 +20,4 @@ libarch_la_SOURCES = \
scale_neon.S \
mult.c \
mult_neon.S
endif

View File

@@ -28,9 +28,6 @@
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
void neon_convert_si16_ps_4n(float *, const short *, int);
void convert_init(void) {
}
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_si16_ps(float *out,
const short *in,
@@ -57,6 +54,7 @@ static void neon_convert_ps_si16(short *out,
for (int i = 0; i < len % 4; i++)
out[start + i] = (short) (in[start + i] * (*scale));
}
#endif
void convert_float_short(short *out, const float *in, float scale, int len)
{

View File

@@ -1,7 +1,7 @@
#ifndef _CONVOLVE_H_
#define _CONVOLVE_H_
void *convolve_h_alloc(size_t num);
void *convolve_h_alloc(int num);
int convolve_real(const float *x, int x_len,
const float *h, int h_len,

View File

@@ -146,7 +146,7 @@ int base_convolve_complex(const float *x, int x_len,
}
/* Aligned filter tap allocation */
void *convolve_h_alloc(size_t len)
void *convolve_h_alloc(int len)
{
#ifdef HAVE_SSE3
return memalign(16, len * 2 * sizeof(float));

View File

@@ -1,17 +0,0 @@
include $(top_srcdir)/Makefile.common
noinst_HEADERS = radioDevice.h
SUBDIRS =
if DEVICE_USRP1
SUBDIRS += usrp1
endif
if DEVICE_UHD
SUBDIRS += uhd
endif
if DEVICE_LMS
SUBDIRS += lms
endif

View File

@@ -1,682 +0,0 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
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 <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "Logger.h"
#include "Threads.h"
#include "LMSDevice.h"
#include <boost/algorithm/string.hpp>
#include <lime/LimeSuite.h>
#include <osmocom/core/utils.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
using namespace std;
constexpr double LMSDevice::masterClockRate;
#define MAX_ANTENNA_LIST_SIZE 10
#define LMS_SAMPLE_RATE GSMRATE*32
#define GSM_CARRIER_BW 270000.0 /* 270kHz */
#define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */
#define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED)
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, 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),
m_lms_dev(NULL)
{
LOGC(DDEV, INFO) << "creating LMS device...";
m_lms_stream_rx.resize(chans);
m_lms_stream_tx.resize(chans);
m_last_rx_underruns.resize(chans, 0);
m_last_rx_overruns.resize(chans, 0);
m_last_tx_underruns.resize(chans, 0);
m_last_tx_overruns.resize(chans, 0);
}
LMSDevice::~LMSDevice()
{
LOGC(DDEV, INFO) << "Closing LMS device";
if (m_lms_dev) {
LMS_Close(m_lms_dev);
m_lms_dev = NULL;
}
}
static void lms_log_callback(int lvl, const char *msg)
{
/* map lime specific log levels */
static const int lvl_map[5] = {
[0] = LOGL_FATAL,
[LMS_LOG_ERROR] = LOGL_ERROR,
[LMS_LOG_WARNING] = LOGL_NOTICE,
[LMS_LOG_INFO] = LOGL_INFO,
[LMS_LOG_DEBUG] = LOGL_DEBUG,
};
/* protect against future higher log level values (lower importance) */
if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map))
lvl = ARRAY_SIZE(lvl_map)-1;
LOGLV(DLMS, lvl_map[lvl]) << msg;
}
static void thread_enable_cancel(bool cancel)
{
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}
static void print_range(const char* name, lms_range_t *range)
{
LOGC(DDEV, INFO) << name << ": Min=" << range->min << " Max=" << range->max
<< " Step=" << range->step;
}
/*! Find the device string that matches all filters from \a args.
* \param[in] info_list device addresses found by LMS_GetDeviceList()
* \param[in] count length of info_list
* \param[in] args dev-args value from osmo-trx.cfg, containing comma separated key=value pairs
* \return index of first matching item or -1 (no match) */
int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args)
{
unsigned int i, j;
vector<string> filters;
boost::split(filters, args, boost::is_any_of(","));
/* iterate over device addresses */
for (i=0; i < count; i++) {
/* check if all filters match */
bool match = true;
for (j=0; j < filters.size(); j++) {
if (!strstr(info_list[i], filters[j].c_str())) {
match = false;
break;
}
}
if (match)
return i;
}
return -1;
}
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
{
//lms_info_str_t dev_str;
lms_info_str_t* info_list;
lms_range_t range_lpfbw_rx, range_lpfbw_tx, range_sr;
float_type sr_host, sr_rf, lpfbw_rx, lpfbw_tx;
uint16_t dac_val;
unsigned int i, n;
int rc, dev_id;
LOGC(DDEV, INFO) << "Opening LMS device..";
LMS_RegisterLogHandler(&lms_log_callback);
if ((n = LMS_GetDeviceList(NULL)) < 0)
LOGC(DDEV, ERROR) << "LMS_GetDeviceList(NULL) failed";
LOGC(DDEV, INFO) << "Devices found: " << n;
if (n < 1)
return -1;
info_list = new lms_info_str_t[n];
if (LMS_GetDeviceList(info_list) < 0)
LOGC(DDEV, ERROR) << "LMS_GetDeviceList(info_list) failed";
for (i = 0; i < n; i++)
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
dev_id = info_list_find(info_list, n, args);
if (dev_id == -1) {
LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
delete [] info_list;
return -1;
}
LOGC(DDEV, INFO) << "Using device: " << info_list[dev_id];
rc = LMS_Open(&m_lms_dev, info_list[dev_id], NULL);
if (rc != 0) {
LOGC(DDEV, ERROR) << "LMS_GetDeviceList() failed)";
delete [] info_list;
return -1;
}
delete [] info_list;
LOGC(DDEV, INFO) << "Init LMS device";
if (LMS_Init(m_lms_dev) != 0) {
LOGC(DDEV, ERROR) << "LMS_Init() failed";
goto out_close;
}
if (LMS_GetSampleRateRange(m_lms_dev, LMS_CH_RX, &range_sr))
goto out_close;
print_range("Sample Rate", &range_sr);
LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps;
if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0)
goto out_close;
if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf))
goto out_close;
LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf;
/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */
ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */
switch (ref) {
case REF_INTERNAL:
LOGC(DDEV, INFO) << "Setting Internal clock reference";
/* Ugly API: Selecting clock source implicit by writing to VCTCXO DAC ?!? */
if (LMS_VCTCXORead(m_lms_dev, &dac_val) < 0)
goto out_close;
LOGC(DDEV, INFO) << "Setting VCTCXO to " << dac_val;
if (LMS_VCTCXOWrite(m_lms_dev, dac_val) < 0)
goto out_close;
break;
case REF_EXTERNAL:
LOGC(DDEV, INFO) << "Setting External clock reference to " << 10000000.0;
/* Assume an external 10 MHz reference clock */
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
goto out_close;
break;
default:
LOGC(DDEV, ALERT) << "Invalid reference type";
goto out_close;
}
if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_rx))
goto out_close;
print_range("LPFBWRange Rx", &range_lpfbw_rx);
if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_tx))
goto out_close;
print_range("LPFBWRange Tx", &range_lpfbw_tx);
lpfbw_rx = OSMO_MIN(OSMO_MAX(1.4001e6, range_lpfbw_rx.min), range_lpfbw_rx.max);
lpfbw_tx = OSMO_MIN(OSMO_MAX(5.2e6, range_lpfbw_tx.min), range_lpfbw_tx.max);
LOGC(DDEV, INFO) << "LPFBW: Rx=" << lpfbw_rx << " Tx=" << lpfbw_tx;
if (!set_antennas()) {
LOGC(DDEV, ALERT) << "LMS antenna setting failed";
return -1;
}
/* Perform Rx and Tx calibration */
for (i=0; i<chans; i++) {
LOGC(DDEV, INFO) << "Setting LPFBW chan " << i;
if (LMS_SetLPFBW(m_lms_dev, LMS_CH_RX, i, lpfbw_rx) < 0)
goto out_close;
if (LMS_SetLPFBW(m_lms_dev, LMS_CH_TX, i, lpfbw_tx) < 0)
goto out_close;
LOGC(DDEV, INFO) << "Calibrating chan " << i;
if (LMS_Calibrate(m_lms_dev, LMS_CH_RX, i, LMS_CALIBRATE_BW_HZ, 0) < 0)
goto out_close;
if (LMS_Calibrate(m_lms_dev, LMS_CH_TX, i, LMS_CALIBRATE_BW_HZ, 0) < 0)
goto out_close;
}
samplesRead = 0;
samplesWritten = 0;
started = false;
return NORMAL;
out_close:
LOGC(DDEV, ALERT) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
LMS_Close(m_lms_dev);
m_lms_dev = NULL;
return -1;
}
bool LMSDevice::start()
{
LOGC(DDEV, INFO) << "starting LMS...";
unsigned int i;
if (started) {
LOGC(DDEV, ERR) << "Device already started";
return false;
}
/* configure the channels/streams */
for (i=0; i<chans; i++) {
if (LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, true) < 0)
return false;
if (LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, true) < 0)
return false;
// Set gains to midpoint
setTxGain((minTxGain() + maxTxGain()) / 2, i);
setRxGain((minRxGain() + maxRxGain()) / 2, i);
m_lms_stream_rx[i] = {};
m_lms_stream_rx[i].isTx = false;
m_lms_stream_rx[i].channel = i;
m_lms_stream_rx[i].fifoSize = 1024 * 1024;
m_lms_stream_rx[i].throughputVsLatency = 0.3;
m_lms_stream_rx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
m_lms_stream_tx[i] = {};
m_lms_stream_tx[i].isTx = true;
m_lms_stream_tx[i].channel = i;
m_lms_stream_tx[i].fifoSize = 1024 * 1024;
m_lms_stream_tx[i].throughputVsLatency = 0.3;
m_lms_stream_tx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
if (LMS_SetupStream(m_lms_dev, &m_lms_stream_rx[i]) < 0)
return false;
if (LMS_SetupStream(m_lms_dev, &m_lms_stream_tx[i]) < 0)
return false;
}
/* now start the streams in a second loop, as we can no longer call
* LMS_SetupStream() after LMS_StartStream() of the first stream */
for (i = 0; i < chans; i++) {
if (LMS_StartStream(&m_lms_stream_rx[i]) < 0)
return false;
if (LMS_StartStream(&m_lms_stream_tx[i]) < 0)
return false;
}
flush_recv(10);
started = true;
return true;
}
bool LMSDevice::stop()
{
unsigned int i;
if (!started)
return true;
for (i=0; i<chans; i++) {
LMS_StopStream(&m_lms_stream_tx[i]);
LMS_StopStream(&m_lms_stream_rx[i]);
}
for (i=0; i<chans; i++) {
LMS_DestroyStream(m_lms_dev, &m_lms_stream_tx[i]);
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, false);
LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, false);
}
started = false;
return true;
}
double LMSDevice::maxTxGain()
{
return 73.0;
}
double LMSDevice::minTxGain()
{
return 0.0;
}
double LMSDevice::maxRxGain()
{
return 73.0;
}
double LMSDevice::minRxGain()
{
return 0.0;
}
double LMSDevice::setTxGain(double dB, size_t chan)
{
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
LOGC(DDEV, NOTICE) << "chan " << chan <<": Setting TX gain to " << dB << " dB";
if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
LOGC(DDEV, ERR) << "chan " << chan <<": Error setting TX gain to " << dB << " dB";
return dB;
}
double LMSDevice::setRxGain(double dB, size_t chan)
{
if (dB > maxRxGain())
dB = maxRxGain();
if (dB < minRxGain())
dB = minRxGain();
LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting RX gain to " << dB << " dB";
if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0)
LOGC(DDEV, ERR) << "chan "<< chan << ": Error setting RX gain to " << dB << " dB";
return dB;
}
int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
const char* c_name = name.c_str();
int num_names;
int i;
num_names = LMS_GetAntennaList(m_lms_dev, dir_tx, chan, name_list);
for (i = 0; i < num_names; i++) {
if (!strcmp(c_name, name_list[i]))
return i;
}
return -1;
}
bool LMSDevice::flush_recv(size_t num_pkts)
{
#define CHUNK 625
int len = CHUNK * tx_sps;
short *buffer = new short[len * 2];
int rc;
lms_stream_meta_t rx_metadata = {};
rx_metadata.flushPartialPacket = false;
rx_metadata.waitForTimestamp = false;
ts_initial = 0;
while (!ts_initial || (num_pkts-- > 0)) {
rc = LMS_RecvStream(&m_lms_stream_rx[0], &buffer[0], len, &rx_metadata, 100);
LOGC(DDEV, DEBUG) << "Flush: Recv buffer of len " << rc << " at " << std::hex << rx_metadata.timestamp;
if (rc != len) {
LOGC(DDEV, ALERT) << "LMS: Device receive timed out";
delete[] buffer;
return false;
}
ts_initial = rx_metadata.timestamp + len;
}
LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
delete[] buffer;
return true;
}
bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan)
{
int idx;
if (chan >= rx_paths.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
idx = get_ant_idx(ant, LMS_CH_RX, chan);
if (idx < 0) {
LOGC(DDEV, ALERT) << "Invalid Rx Antenna";
return false;
}
if (LMS_SetAntenna(m_lms_dev, LMS_CH_RX, chan, idx) < 0) {
LOGC(DDEV, ALERT) << "Unable to set Rx Antenna";
}
return true;
}
std::string LMSDevice::getRxAntenna(size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
int idx;
if (chan >= rx_paths.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return "";
}
idx = LMS_GetAntenna(m_lms_dev, LMS_CH_RX, chan);
if (idx < 0) {
LOGC(DDEV, ALERT) << "Error getting Rx Antenna";
return "";
}
if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, name_list) < idx) {
LOGC(DDEV, ALERT) << "Error getting Rx Antenna List";
return "";
}
return name_list[idx];
}
bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan)
{
int idx;
if (chan >= tx_paths.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
idx = get_ant_idx(ant, LMS_CH_TX, chan);
if (idx < 0) {
LOGC(DDEV, ALERT) << "Invalid Rx Antenna";
return false;
}
if (LMS_SetAntenna(m_lms_dev, LMS_CH_TX, chan, idx) < 0) {
LOGC(DDEV, ALERT) << "Unable to set Rx Antenna";
}
return true;
}
std::string LMSDevice::getTxAntenna(size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
int idx;
if (chan >= tx_paths.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return "";
}
idx = LMS_GetAntenna(m_lms_dev, LMS_CH_TX, chan);
if (idx < 0) {
LOGC(DDEV, ALERT) << "Error getting Tx Antenna";
return "";
}
if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, name_list) < idx) {
LOGC(DDEV, ALERT) << "Error getting Tx Antenna List";
return "";
}
return name_list[idx];
}
bool LMSDevice::requiresRadioAlign()
{
return false;
}
GSM::Time LMSDevice::minLatency() {
/* Empirical data from a handful of
relatively recent machines shows that the B100 will underrun when
the transmit threshold is reduced to a time of 6 and a half frames,
so we set a minimum 7 frame threshold. */
return GSM::Time(6,7);
}
void LMSDevice::update_stream_stats(size_t chan, bool * underrun, bool * overrun)
{
lms_stream_status_t status;
if (LMS_GetStreamStatus(&m_lms_stream_rx[chan], &status) == 0) {
if (status.underrun > m_last_rx_underruns[chan])
*underrun = true;
m_last_rx_underruns[chan] = status.underrun;
if (status.overrun > m_last_rx_overruns[chan])
*overrun = true;
m_last_rx_overruns[chan] = status.overrun;
}
}
// NOTE: Assumes sequential reads
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
{
int rc = 0;
unsigned int i;
lms_stream_meta_t rx_metadata = {};
rx_metadata.flushPartialPacket = false;
rx_metadata.waitForTimestamp = false;
rx_metadata.timestamp = 0;
if (bufs.size() != chans) {
LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
return -1;
}
*overrun = false;
*underrun = false;
for (i = 0; i<chans; i++) {
thread_enable_cancel(false);
rc = LMS_RecvStream(&m_lms_stream_rx[i], bufs[i], len, &rx_metadata, 100);
update_stream_stats(i, underrun, overrun);
if (rc != len) {
LOGC(DDEV, ALERT) << "LMS: Device receive timed out (" << rc << " vs exp " << len << ").";
thread_enable_cancel(true);
return -1;
}
if (timestamp != (TIMESTAMP)rx_metadata.timestamp)
LOGC(DDEV, ALERT) << "chan "<< i << " recv buffer of len " << rc << " expect " << std::hex << timestamp << " got " << std::hex << (TIMESTAMP)rx_metadata.timestamp << " (" << std::hex << rx_metadata.timestamp <<") diff=" << rx_metadata.timestamp - timestamp;
thread_enable_cancel(true);
}
samplesRead += rc;
if (((TIMESTAMP) rx_metadata.timestamp) < timestamp)
rc = 0;
return rc;
}
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
bool * underrun, unsigned long long timestamp,
bool isControl)
{
int rc = 0;
unsigned int i;
lms_stream_status_t status;
lms_stream_meta_t tx_metadata = {};
tx_metadata.flushPartialPacket = false;
tx_metadata.waitForTimestamp = true;
tx_metadata.timestamp = timestamp - ts_offset; /* Shift Tx time by offset */
if (isControl) {
LOGC(DDEV, ERR) << "Control packets not supported";
return 0;
}
if (bufs.size() != chans) {
LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
return -1;
}
*underrun = false;
for (i = 0; i<chans; i++) {
LOGC(DDEV, DEBUG) << "chan "<< i << " send buffer of len " << len << " timestamp " << std::hex << tx_metadata.timestamp;
thread_enable_cancel(false);
rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100);
if (rc != len) {
LOGC(DDEV, ALERT) << "LMS: Device send timed out";
}
if (LMS_GetStreamStatus(&m_lms_stream_tx[i], &status) == 0) {
if (status.underrun > m_last_tx_underruns[i])
*underrun = true;
m_last_tx_underruns[i] = status.underrun;
}
thread_enable_cancel(true);
}
samplesWritten += rc;
return rc;
}
bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
{
return true;
}
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
{
LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting Tx Freq to " << wFreq << " Hz";
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
LOGC(DDEV, ERROR) << "chan "<< chan << ": Error setting Tx Freq to " << wFreq << " Hz";
return false;
}
return true;
}
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
{
LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting Rx Freq to " << wFreq << " Hz";
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
LOGC(DDEV, ERROR) << "chan "<< chan << ": Error setting Rx Freq to " << wFreq << " Hz";
return false;
}
return true;
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
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) {
LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
return NULL;
}
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
return NULL;
}
return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -1,211 +0,0 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* 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 _LMS_DEVICE_H_
#define _LMS_DEVICE_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include <sys/time.h>
#include <math.h>
#include <limits.h>
#include <string>
#include <iostream>
#include <lime/LimeSuite.h>
/* Definition of LIMESDR_TX_AMPL limits maximum amplitude of I and Q
* channels separately. Hence LIMESDR_TX_AMPL value must be 1/sqrt(2) =
* 0.7071.... to get an amplitude of 1 of the complex signal:
* A^2 = I^2 + Q^2
* A^2 = (1/sqrt(2))^2 + (1/sqrt(2))^2
* A^2 = 1/2 + 1/2
* A^2 = 1 */
#define LIMESDR_TX_AMPL 0.707
/** A class to handle a LimeSuite supported device */
class LMSDevice:public RadioDevice {
private:
static constexpr double masterClockRate = 52.0e6;
lms_device_t *m_lms_dev;
std::vector<lms_stream_t> m_lms_stream_rx;
std::vector<lms_stream_t> m_lms_stream_tx;
std::vector<uint32_t> m_last_rx_underruns;
std::vector<uint32_t> m_last_rx_overruns;
std::vector<uint32_t> m_last_tx_underruns;
std::vector<uint32_t> m_last_tx_overruns;
double actualSampleRate; ///< the actual USRP sampling rate
unsigned long long samplesRead; ///< number of samples read from LMS
unsigned long long samplesWritten; ///< number of samples sent to LMS
bool started; ///< flag indicates LMS has started
bool skipRx; ///< set if LMS is transmit-only.
TIMESTAMP ts_initial, ts_offset;
double rxGain;
int get_ant_idx(const std::string & name, bool dir_tx, size_t chan);
bool flush_recv(size_t num_pkts);
void update_stream_stats(size_t chan, bool * underrun, bool * overrun);
public:
/** Object constructor */
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~LMSDevice();
/** Instantiate the LMS */
int open(const std::string &args, int ref, bool swap_channels);
/** Start the LMS */
bool start();
/** Stop the LMS */
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) {
}
enum TxWindowType getWindowType() {
return TX_WINDOW_LMS1;
}
/**
Read samples from the LMS.
@param buf preallocated buf to contain read result
@param len number of samples desired
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(std::vector < short *>&buf, int len, bool * overrun,
TIMESTAMP timestamp = 0xffffffff, bool * underrun =
NULL, unsigned *RSSI = NULL);
/**
Write samples to the LMS.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(std::vector < short *>&bufs, int len, bool * underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl =
false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
/** Set the transmitter frequency */
bool setTxFreq(double wFreq, size_t chan = 0);
/** Set the receiver frequency */
bool setRxFreq(double wFreq, size_t chan = 0);
/** Returns the starting write Timestamp*/
TIMESTAMP initialWriteTimestamp(void) {
return ts_initial;
}
/** Returns the starting read Timestamp*/
TIMESTAMP initialReadTimestamp(void) {
return ts_initial;
}
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue() {
return(double) SHRT_MAX * LIMESDR_TX_AMPL;
}
/** returns the full-scale receive amplitude **/
double fullScaleOutputValue() {
return (double) SHRT_MAX;
}
/** sets the receive chan gain, returns the gain setting **/
double setRxGain(double dB, size_t chan = 0);
/** get the current receive gain */
double getRxGain(size_t chan = 0) {
return rxGain;
}
/** return maximum Rx Gain **/
double maxRxGain(void);
/** return minimum Rx Gain **/
double minRxGain(void);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
/** return maximum Tx Gain **/
double maxTxGain(void);
/** return minimum Rx Gain **/
double minTxGain(void);
/** 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 */
inline double getTxFreq(size_t chan = 0) {
return 0;
}
inline double getRxFreq(size_t chan = 0) {
return 0;
}
inline double getSampleRate() {
return actualSampleRate;
}
inline double numberRead() {
return samplesRead;
}
inline double numberWritten() {
return samplesWritten;
}
};
#endif // _LMS_DEVICE_H_

View File

@@ -1,10 +0,0 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
noinst_HEADERS = LMSDevice.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = LMSDevice.cpp

View File

@@ -1,8 +0,0 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = UHDDevice.cpp

View File

@@ -1,10 +0,0 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS)
noinst_HEADERS = USRPDevice.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = USRPDevice.cpp

View File

@@ -0,0 +1,520 @@
/*
* Copyright (C) 2016-2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <limits.h>
#include <fstream>
#include <iomanip>
#include "Logger.h"
#include "sigProcLib.h"
#include "signalVector.h"
#include "Transceiver.h"
#include "Configuration.h"
extern "C" {
#include "convolve.h"
#include "convert.h"
}
#define DEFAULT_RX_SPS 1
#define DEFAULT_SEARCH_WINDOW 30
// Tail + data + stealing + midamble + guard (without the last 0.25)
#define BURST_LEN_FULL 156
// Tail + data + stealing + midamble
#define BURST_LEN_ACTIVE 148
// Tail + data + stealing + midamble - 2*0.5
#define BURST_LEN_USEFUL 147
// Size of a sample in bytes as stores in a file
#define SAMPLE_SIZE_BYTES (2 * sizeof(float))
// Burst length in bytes as stored in a file
#define BURST_LEN_BYTES (BURST_LEN_FULL * SAMPLE_SIZE_BYTES)
ConfigurationTable gConfig;
struct trx_config {
std::string log_level;
unsigned sps;
unsigned tsc;
unsigned max_expected_delay_nb;
unsigned max_expected_delay_ab;
double full_scale;
bool edge;
CorrType type;
std::string filename;
unsigned ber_burst_avg; ///< Average BER over this many bursts.
///< Set to 0 to average for the whole duration.
};
class NormalBurstSoftbitMask {
public:
NormalBurstSoftbitMask(SoftVector &softBits)
: mSoftBits(softBits)
{
}
SoftVector &bits() { return mSoftBits; }
SoftVector tailBitsL() { return mSoftBits.segment(0,3); }
SoftVector dataBitsL() { return mSoftBits.segment(3,57); }
SoftVector stealingBitsL() { return mSoftBits.segment(60, 1); }
SoftVector midambleBits() { return mSoftBits.segment(61, 26); }
SoftVector stealingBitsR() { return mSoftBits.segment(87, 1); }
SoftVector dataBitsR() { return mSoftBits.segment(88,57); }
SoftVector tailBitsR() { return mSoftBits.segment(145,3); }
SoftVector guardBits() { return mSoftBits.segment(148,8); }
protected:
SoftVector &mSoftBits;
};
class SoftBurst {
public:
SoftBurst(SoftVector *softBits, double toa=0)
: mSoftBits(softBits), mTOA(toa)
{
assert(mSoftBits != NULL);
}
~SoftBurst()
{
delete mSoftBits;
}
void TOA(double TOA) { mTOA = TOA; }
double TOA() { return mTOA; }
NormalBurstSoftbitMask normalBurstMask() { return NormalBurstSoftbitMask(*mSoftBits); }
protected:
SoftVector *mSoftBits;
double mTOA;
};
class BEREstimator {
public:
BEREstimator(const PRBS& prbs)
: mPRBS(prbs), mTotalBits(0), mErrorBits(0), mSynchronized(false)
{}
unsigned synchronize(const BitVector &bits)
{
for (unsigned i=0; i<mPRBS.size(); i++) {
mPRBS.processBit(bits[i]);
}
mSynchronized = true;
return mPRBS.size();
}
void process(const BitVector &bits, size_t start_from = 0)
{
for (size_t i=start_from; i<bits.size(); i++) {
mTotalBits++;
if (mPRBS.generateBit() != bits.bit(i)) {
mErrorBits++;
}
}
}
void sync_and_process(const BitVector &bits)
{
unsigned skip = 0;
if (!mSynchronized) {
skip = synchronize(bits);
}
process(bits, skip);
}
void skip(size_t num)
{
for (size_t i=0; i<num; i++) {
mTotalBits++;
mErrorBits++;
mPRBS.generateBit();
}
}
void reset()
{
mTotalBits = 0;
mErrorBits = 0;
}
unsigned totalBits() const { return mTotalBits; }
unsigned errorBits() const { return mErrorBits; }
double BER() const { return mErrorBits/(double)mTotalBits; }
bool isSynchronized() const {return mSynchronized; }
protected:
PRBS mPRBS;
unsigned mTotalBits;
unsigned mErrorBits;
bool mSynchronized;
};
double getBurstRSSI(const signalVector &burst, unsigned sps, double full_scale)
{
/* Calculate average power of the burst */
float avg = energyDetect(burst, 20 * sps);
return 20.0 * log10(sqrt(avg) / full_scale);
}
void printDetectionResult(int rc)
{
if (rc > 0) {
std::cout << "Detected correlation type: " << (CorrType)rc << std::endl;
} else {
if (rc == -SIGERR_CLIP) {
std::cout << "Clipping detected on received RACH or Normal Burst" << std::endl;
} else if (rc != SIGERR_NONE) {
std::cout << "Unhandled RACH or Normal Burst detection error" << std::endl;
} else {
// std::cout << "No burst detected" << std::endl;
}
}
}
SoftVector *demodulateBurst(const signalVector &burst,
CorrType expected_type,
unsigned sps, unsigned tsc,
unsigned max_expected_delay,
double &timingOffset)
{
complex amp;
float toa;
int rc;
CorrType detected_type;
/* Detect normal or RACH bursts */
rc = detectAnyBurst(burst, tsc, BURST_THRESH, sps, expected_type, amp, toa,
max_expected_delay);
printDetectionResult(rc);
if (rc <= 0) {
return NULL;
}
// Convert samples to symbols
timingOffset = toa / sps;
// rc > 0 means it's a detected CorrType
detected_type = (CorrType)rc;
return demodAnyBurst(burst, sps, amp, toa, detected_type);
}
static bool processBurst(const trx_config &config, signalVector &burst,
unsigned max_expected_delay,
double &RSSI,
double &timingOffset,
BEREstimator &berEstimator)
{
RSSI = getBurstRSSI(burst, config.sps, config.full_scale);
SoftVector *softBits = demodulateBurst(burst, config.type, config.sps,config.tsc,
max_expected_delay, timingOffset);
/* Print burst information and content */
if (softBits == NULL) {
std::cout << "Skipped frame" << std::endl;
// TODO: This is different for EDGE
berEstimator.skip(57*2);
return false;
}
SoftBurst softBurst(softBits, timingOffset);
NormalBurstSoftbitMask nb = softBurst.normalBurstMask();
berEstimator.sync_and_process(nb.dataBitsL().sliced());
berEstimator.sync_and_process(nb.dataBitsR().sliced());
std::cout << "TOA: " << softBurst.TOA() << " symbols" << std::endl;
// Exclude tail and guard bits from the energy calculation
std::cout << "Energy: " << softBits->segment(3,142).getEnergy() << std::endl;
//std::cout << "Demodulated burst: " << *softBits << std::endl;
std::cout << " tail|--------------------------data---------------------------|f|--------midamble----------|f|--------------------------data---------------------------|tai|-guard--" << std::endl;
// " 000 010001011011110011101001100100000001010001011000100100010 0 11101111000100101110111100 0 011010111011101010011010111000101100001110101011011001011 000 1''..---"
std::cout << "Demodulated burst:"
<< " " << nb.tailBitsL()
<< " " << nb.dataBitsL()
<< " " << nb.stealingBitsL()
<< " " << nb.midambleBits()
<< " " << nb.stealingBitsR()
<< " " << nb.dataBitsR()
<< " " << nb.tailBitsR()
<< " " << nb.guardBits()
<< std::endl;
return true;
}
// Setup configuration values
static void print_config(struct trx_config *config)
{
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Source file name............. " << config->filename << std::endl;
ost << " Log Level.................... " << config->log_level << std::endl;
ost << " Rx Samples-per-Symbol........ " << config->sps << std::endl;
ost << " EDGE support................. " << (config->edge ? "Enabled" : "Disabled") << std::endl;
ost << " Burst type................... " << config->type << std::endl;
ost << " Burst TSC.................... " << config->tsc << std::endl;
ost << " Normal Burst search window... " << config->max_expected_delay_nb << std::endl;
ost << " Access Burst search window... " << config->max_expected_delay_ab << std::endl;
ost << " Signal full scale............ " << config->full_scale << std::endl;
ost << " BER average window (bursts).. " << config->ber_burst_avg << std::endl;
std::cout << ost << std::endl;
}
static void print_help()
{
fprintf(stdout, "Options:\n"
" -h This text\n"
" -l LEVEL Logging level (%s)\n"
" -e Enable EDGE receiver\n"
" -s SPS Samples-per-symbol (1 or 4, default: %d)\n"
" -t TSC Burst training sequence (0 to 7, default: 0)\n"
" -f FILE File to read\n"
" -w SYMBOLS Normal Burst search window (0 to 156, default: %d)\n"
" -W SYMBOLS Access Burst search window (0 to 156, default: %d)\n"
" -b BURSTS BER average window. Set to 0 to average over the whole file (default: 1)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG",
DEFAULT_RX_SPS,
DEFAULT_SEARCH_WINDOW, DEFAULT_SEARCH_WINDOW);
}
static bool handle_options(int argc, char **argv, struct trx_config *config)
{
int option;
config->log_level = "NOTICE";
config->sps = DEFAULT_RX_SPS;
config->tsc = 0;
config->max_expected_delay_nb = DEFAULT_SEARCH_WINDOW;
config->max_expected_delay_ab = DEFAULT_SEARCH_WINDOW;
config->full_scale = SHRT_MAX;
config->edge = false;
config->type = TSC;
config->ber_burst_avg = 1;
while ((option = getopt(argc, argv, "ls:et:f:w:W:b:h")) != -1) {
switch (option) {
case 'l':
config->log_level = optarg;
break;
case 's':
config->sps = atoi(optarg);
break;
case 'e':
config->edge = true;
break;
case 't':
config->tsc = atoi(optarg);
break;
case 'f':
config->filename = optarg;
break;
case 'w':
config->max_expected_delay_nb = atoi(optarg);
break;
case 'W':
config->max_expected_delay_ab = atoi(optarg);
break;
case 'b':
config->ber_burst_avg = atoi(optarg);
break;
case 'h':
default:
print_help();
exit(0);
}
}
if ((config->sps != 1) && (config->sps != 4)) {
printf("ERROR: Unsupported samples-per-symbol %i\n\n", config->sps);
return false;
}
if (config->edge && (config->sps != 4)) {
printf("ERROR: EDGE only supported at 4 samples per symbol\n\n");
return false;
}
if (config->tsc > 7) {
printf("ERROR: Invalid training sequence %i\n\n", config->tsc);
return false;
}
if (config->filename.length() == 0) {
printf("ERROR: No input file specified\n\n");
return false;
}
if (config->max_expected_delay_nb > 156 || config->max_expected_delay_nb < 0 ||
config->max_expected_delay_ab > 156 || config->max_expected_delay_ab < 0) {
printf("ERROR: Invalid search window size, must be withit [1..156] range\n\n");
return false;
}
return true;
}
int main(int argc, char *argv[])
{
struct trx_config config;
#ifdef HAVE_SSE3
printf("Info: SSE3 support compiled in");
if (__builtin_cpu_supports("sse3"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#endif
#ifdef HAVE_SSE4_1
printf("Info: SSE4.1 support compiled in");
if (__builtin_cpu_supports("sse4.1"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#endif
convolve_init();
convert_init();
// Process command line options and print config to screen
if (!handle_options(argc, argv, &config)) {
print_help();
exit(0);
}
print_config(&config);
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
if (!sigProcLibSetup()) {
LOG(ALERT) << "Failed to initialize signal processing library";
return -1;
}
double RSSI;
double timingOffset, timingOffsetPrev = 0.0;
signalVector burst(2*BURST_LEN_FULL);
GSM::Time gsmTime;
bool syncedTo157bits = false; // We should syncronize to 156-157 frame structure only once
bool burst156_157 = false; // Set to true to enable 156-156-156-157 frame
int bitsReadExtra = 0; // set to 1 every 4 bursts and when TOA>1.0
int bitsToSkip = 0; // set to 1 when TOA<0.0
unsigned berBurstsAveraged = 0;
PRBS9 prbs;
BEREstimator berEstimator(prbs);
// Configure output stream
std::cout << std::fixed;
std::cout << std::setprecision(2);
std::ifstream file (config.filename.c_str(), std::ifstream::binary);
// Read the first burst, but do not process it, because we need at least two bursts
// worth of data for reliable initial detection.
file.read((char*)burst.begin(), config.sps * BURST_LEN_BYTES);
{signalVector t = burst.segment(0, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
#if 0
/* Distort signal */
{
signalVector burst_read = burst.segment(85,156);
std::ifstream file (config.filename.c_str(), std::ifstream::binary);
file.read((char*)burst_read.begin(), burst_read.size() * 2 * sizeof(float));
file.close();
}
#endif
#if 1
// Read more data and try burst detection until successful
while(file.read((char*)(burst.begin()+config.sps*BURST_LEN_FULL), config.sps*BURST_LEN_BYTES))
{
{signalVector t = burst.segment(BURST_LEN_FULL, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
bool found = processBurst(config, burst, BURST_LEN_FULL, RSSI, timingOffset, berEstimator);
std::cout << "RSSI: " << RSSI << " dBFS" << std::endl;
if (found) {
gsmTime.incTN();
berBurstsAveraged++;
break;
}
burst.segmentMove(config.sps*BURST_LEN_FULL, 0, config.sps*BURST_LEN_FULL);
}
// Align stream to burst
int offsetInt = (int)timingOffset;
burst.segmentMove(config.sps*(BURST_LEN_FULL+offsetInt), 0, config.sps*(BURST_LEN_FULL-offsetInt));
{signalVector t = burst.segment(0, BURST_LEN_FULL-offsetInt); scaleVector(t, complex(1.0/SHRT_MAX)); }
file.read((char*)(burst.begin()+config.sps*(BURST_LEN_FULL-offsetInt)), config.sps*offsetInt*SAMPLE_SIZE_BYTES);
#endif
// Resize burst vector to hold only one burst, because demodulation code
// always decode the full vector size.
burst.shrink(BURST_LEN_FULL+1);
// Process the rest of the stream
do {
{signalVector t = burst.segment(0, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
processBurst(config, burst, (config.type==RACH)?config.max_expected_delay_ab:config.max_expected_delay_ab,
RSSI, timingOffset, berEstimator);
if (burst156_157 && !syncedTo157bits && timingOffset - timingOffsetPrev > .75) {
std::cout << "TOA adjust: Found a 157-bit burst, reset TN to mark it" << std::endl;
gsmTime.TN(2);
timingOffset -= 1.0;
// Make sure we do this adjustment only once.
syncedTo157bits = true;
} else {
gsmTime.incTN();
}
bitsToSkip = 0;
bitsReadExtra = 0;
if (timingOffset < 0.0) {
std::cout << "TOA adjust: skip a bit" << std::endl;
burst[0] = 0;
bitsToSkip = 1;
bitsReadExtra--;
}
bitsReadExtra += (gsmTime.TN()%4 == 0);
if (timingOffset > 1.1) {
std::cout << "TOA adjust: add extra bit" << std::endl;
bitsReadExtra++;
}
std::cout << "Clock: " << gsmTime;
std::cout << " RSSI: " << RSSI << " dBFS";
std::cout << " Error bits: " << berEstimator.errorBits() << " Total bits: " << berEstimator.totalBits()
<< " BER: " << 100.0*berEstimator.errorBits() / berEstimator.totalBits() << "%" << std::endl;
berBurstsAveraged++;
// Never reset if config.ber_burst_avg is 0
if (config.ber_burst_avg > 0 && berBurstsAveraged >= config.ber_burst_avg) {
berBurstsAveraged = 0;
berEstimator.reset();
}
std::cout << "bitsReadExtra: " << bitsReadExtra << " bitsToSkip: " << bitsToSkip << std::endl;
timingOffsetPrev = timingOffset;
} while(file.read((char*)(burst.begin()+bitsToSkip), config.sps*(BURST_LEN_BYTES+SAMPLE_SIZE_BYTES*bitsReadExtra)));
std::cout << "End of file reached" << std::endl;
file.close();
return 0;
}

View File

@@ -0,0 +1,334 @@
/*
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <limits.h>
#include <fstream>
#include <iomanip>
#include <endian.h> // for byte order manipulation
#include "Logger.h"
#include "sigProcLib.h"
#include "GSMCommon.h"
#include "BitVector.h"
#include "Configuration.h"
extern "C" {
#include "convolve.h"
#include "convert.h"
}
#define DEFAULT_SPS 4
#define DEFAULT_SEARCH_WINDOW 30
// Tail + data + stealing + midamble + guard (without the last 0.25)
#define BURST_LEN_FULL 156
// Tail + data + stealing + midamble
#define BURST_LEN_ACTIVE 148
// Tail + data + stealing + midamble - 2*0.5
#define BURST_LEN_USEFUL 147
// Size of a sample in bytes as stores in a file
#define SAMPLE_SIZE_BYTES (2 * sizeof(float))
// Burst length in bytes as stored in a file
#define BURST_LEN_BYTES (BURST_LEN_FULL * SAMPLE_SIZE_BYTES)
ConfigurationTable gConfig;
enum FileType {
FLOAT_NORM_LE, ///< Float -1..+1 Little Endian
FLOAT16_LE, ///< Float -32767..+32767 Little Endian
SIGNED16_LE, ///< Integer -32767..+32767 Little Endian
SIGNED16_BE, ///< Integer -32767..+32767 Big Endian (Keysight waveform format)
};
struct trx_config {
std::string log_level;
unsigned sps;
unsigned tsc;
double full_scale;
bool edge;
CorrType type;
std::string filename;
FileType file_type;
};
std::ostream& operator<<(std::ostream& os, FileType ftype)
{
switch(ftype)
{
case FLOAT_NORM_LE:
os << "float";
break;
case FLOAT16_LE:
os << "float16";
break;
case SIGNED16_LE:
os << "signed16";
break;
case SIGNED16_BE:
os << "signed16be";
break;
default:
assert(!"unknown file type");
}
return os;
}
void writeBurstFloatNorm(std::ofstream& os, const signalVector& v)
{
os.write((char*)v.begin(), v.size() * 2 * sizeof(float));
}
void writeBurstFloat16LE(std::ofstream& os, const signalVector& v)
{
const complex *c = v.begin();
for (size_t i=0; i<v.size(); i++, c++) {
float iq[2];
iq[0] = c->real()*SHRT_MAX;
iq[1] = c->imag()*SHRT_MAX;
os.write((char*)&iq, 2*sizeof(float));
}
}
void writeBurstSigned16LE(std::ofstream& os, const signalVector& v)
{
const complex *c = v.begin();
for (size_t i=0; i<v.size(); i++, c++) {
int16_t iq[2];
iq[0] = c->real()*SHRT_MAX;
iq[1] = c->imag()*SHRT_MAX;
iq[0] = htole16(iq[0]);
iq[1] = htole16(iq[1]);
os.write((char*)&iq, 2*sizeof(int16_t));
}
}
void writeBurstSigned16BE(std::ofstream& os, const signalVector& v)
{
const complex *c = v.begin();
for (size_t i=0; i<v.size(); i++, c++) {
int16_t iq[2];
iq[0] = c->real()*SHRT_MAX;
iq[1] = c->imag()*SHRT_MAX;
iq[0] = htobe16(iq[0]);
iq[1] = htobe16(iq[1]);
os.write((char*)&iq, 2*sizeof(int16_t));
}
}
void writeBurst(std::ofstream& os, const signalVector& v, FileType ftype)
{
switch(ftype)
{
case FLOAT_NORM_LE:
writeBurstFloatNorm(os, v);
break;
case FLOAT16_LE:
writeBurstFloat16LE(os, v);
break;
case SIGNED16_LE:
writeBurstSigned16LE(os, v);
break;
case SIGNED16_BE:
writeBurstSigned16BE(os, v);
break;
default:
assert(!"unknown file type");
}
}
// Setup configuration values
static void print_config(struct trx_config *config)
{
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Destination file name........ " << config->filename << std::endl;
ost << " Destination file type........ " << config->file_type << std::endl;
ost << " Log Level.................... " << config->log_level << std::endl;
ost << " Tx Samples-per-Symbol........ " << config->sps << std::endl;
ost << " EDGE support................. " << (config->edge ? "Enabled" : "Disabled") << std::endl;
ost << " Burst type................... " << config->type << std::endl;
ost << " Burst TSC.................... " << config->tsc << std::endl;
ost << " Signal full scale............ " << config->full_scale << std::endl;
std::cout << ost << std::endl;
}
static void print_help()
{
fprintf(stdout,
"This utility generates waveform files aka IQ binary files in a number of formats"
"to use them as input to osmo-trx-dec or load them into signal generators.\n"
"\n"
"Options:\n"
" -h This text\n"
" -l LEVEL Logging level (%s)\n"
" -e Enable EDGE receiver\n"
" -s SPS Samples-per-symbol (1 or 4, default: %d)\n"
" -t TSC Burst training sequence (0 to 7, default: 0)\n"
" -f FILE File to write generated bursts to\n"
" -F FILETYPE Format of the file - float, float16, signed16, signed16be (default: f16)\n"
" Note: Keysight waveform format is signed16be. osmo-trx-dec accepts float16.\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG",
DEFAULT_SPS);
}
FileType option_to_file_type(const std::string &optarg)
{
if (optarg == "float") {
return FLOAT_NORM_LE;
} else if (optarg == "float16") {
return FLOAT16_LE;
} else if (optarg == "signed16") {
return SIGNED16_LE;
} else if (optarg == "signed16be") {
return SIGNED16_BE;
} else {
return (FileType)-1;
}
}
static bool handle_options(int argc, char **argv, struct trx_config *config)
{
int option;
config->log_level = "NOTICE";
config->sps = DEFAULT_SPS;
config->tsc = 0;
config->full_scale = SHRT_MAX;
config->edge = false;
config->type = TSC;
config->file_type = FLOAT16_LE;
while ((option = getopt(argc, argv, "ls:et:f:F:h")) != -1) {
switch (option) {
case 'l':
config->log_level = optarg;
break;
case 's':
config->sps = atoi(optarg);
break;
case 'e':
config->edge = true;
break;
case 't':
config->tsc = atoi(optarg);
break;
case 'f':
config->filename = optarg;
break;
case 'F':
config->file_type = option_to_file_type(optarg);
break;
case 'h':
default:
print_help();
exit(0);
}
}
if ((config->sps != 1) && (config->sps != 4)) {
printf("ERROR: Unsupported samples-per-symbol %i\n\n", config->sps);
return false;
}
if (config->edge && (config->sps != 4)) {
printf("ERROR: EDGE only supported at 4 samples per symbol\n\n");
return false;
}
if (config->tsc > 7) {
printf("ERROR: Invalid training sequence %i\n\n", config->tsc);
return false;
}
if (config->filename.length() == 0) {
printf("ERROR: No output file name specified\n\n");
return false;
}
if (config->file_type < 0) {
printf("ERROR: Wrong output file format\n\n");
}
return true;
}
int main(int argc, char *argv[])
{
struct trx_config config;
#ifdef HAVE_SSE3
printf("Info: SSE3 support compiled in");
if (__builtin_cpu_supports("sse3"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#endif
#ifdef HAVE_SSE4_1
printf("Info: SSE4.1 support compiled in");
if (__builtin_cpu_supports("sse4.1"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#endif
convolve_init();
convert_init();
// Process command line options and print config to screen
if (!handle_options(argc, argv, &config)) {
print_help();
exit(0);
}
print_config(&config);
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
if (!sigProcLibSetup()) {
LOG(ALERT) << "Failed to initialize signal processing library";
return -1;
}
signalVector burst(2*BURST_LEN_FULL);
GSM::Time gsmTime;
PRBS9 prbs;
// Configure output stream
std::cout << std::fixed;
std::cout << std::setprecision(2);
std::ofstream file (config.filename.c_str(), std::ifstream::binary);
for (int i=0; i<511; i++) {
signalVector *signal = genRandNormalBurst(config.tsc, config.sps, gsmTime.TN(), prbs);
writeBurst(file, *signal, config.file_type);
gsmTime.incTN();
}
file.close();
std::cout << "Done!" << std::endl;
return 0;
}

View File

@@ -22,59 +22,131 @@
#include "Transceiver.h"
#include "radioDevice.h"
#include "Utils.h"
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <sched.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <GSMCommon.h>
#include <Logger.h>
#include <Configuration.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/stats.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/misc.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/ctrl/ports.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/command.h>
#include "convolve.h"
#include "convert.h"
#include "trx_vty.h"
#include "debug.h"
#include "osmo_signal.h"
}
#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
/* 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
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
/*
* 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
static char* config_file = (char*)DEFAULT_CONFIG_FILE;
/* Default configuration parameters */
#define DEFAULT_TRX_PORT 5700
#define DEFAULT_TRX_IP "127.0.0.1"
#define DEFAULT_CHANS 1
struct trx_config {
std::string log_level;
std::string addr;
std::string dev_args;
unsigned port;
unsigned tx_sps;
unsigned rx_sps;
unsigned chans;
unsigned rtsc;
unsigned rach_delay;
bool extref;
bool gpsref;
Transceiver::FillerType filler;
bool mcbts;
double offset;
double rssi_offset;
bool swap_channels;
bool edge;
};
ConfigurationTable gConfig;
volatile bool gshutdown = false;
static void *tall_trx_ctx;
static struct trx_ctx *g_trx_ctx;
static struct ctrl_handle *g_ctrlh;
/* Setup configuration values
* Don't query the existence of the Log.Level because it's a
* mandatory value. That is, if it doesn't exist, the configuration
* table will crash or will have already crashed. Everything else we
* can survive without and use default values if the database entries
* are empty.
*/
bool trx_setup_config(struct trx_config *config)
{
std::string refstr, fillstr, divstr, mcstr, edgestr;
static RadioDevice *usrp;
static RadioInterface *radio;
static Transceiver *transceiver;
if (config->mcbts && config->chans > 5) {
std::cout << "Unsupported number of channels" << std::endl;
return false;
}
edgestr = config->edge ? "Enabled" : "Disabled";
mcstr = config->mcbts ? "Enabled" : "Disabled";
if (config->extref)
refstr = "External";
else if (config->gpsref)
refstr = "GPS";
else
refstr = "Internal";
switch (config->filler) {
case Transceiver::FILLER_DUMMY:
fillstr = "Dummy bursts";
break;
case Transceiver::FILLER_ZERO:
fillstr = "Disabled";
break;
case Transceiver::FILLER_NORM_RAND:
fillstr = "Normal busrts with random payload";
break;
case Transceiver::FILLER_EDGE_RAND:
fillstr = "EDGE busrts with random payload";
break;
case Transceiver::FILLER_ACCESS_RAND:
fillstr = "Access busrts with random payload";
break;
}
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Log Level............... " << config->log_level << std::endl;
ost << " Device args............. " << config->dev_args << std::endl;
ost << " TRX Base Port........... " << config->port << std::endl;
ost << " TRX Address............. " << config->addr << std::endl;
ost << " Channels................ " << config->chans << std::endl;
ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
ost << " Rx Samples-per-Symbol... " << config->rx_sps << std::endl;
ost << " EDGE support............ " << edgestr << std::endl;
ost << " Reference............... " << refstr << std::endl;
ost << " C0 Filler Table......... " << fillstr << std::endl;
ost << " Multi-Carrier........... " << mcstr << std::endl;
ost << " Tuning offset........... " << config->offset << std::endl;
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
ost << " Swap channels........... " << config->swap_channels << std::endl;
std::cout << ost << std::endl;
return true;
}
/* Create radio interface
* The interface consists of sample rate changes, frequency shifts,
@@ -83,24 +155,24 @@ static Transceiver *transceiver;
* The radio interface connects the main transceiver with the device
* object, which may be operating some other rate.
*/
RadioInterface *makeRadioInterface(struct trx_ctx *trx,
RadioInterface *makeRadioInterface(struct trx_config *config,
RadioDevice *usrp, int type)
{
RadioInterface *radio = NULL;
switch (type) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
radio = new RadioInterface(usrp, config->tx_sps,
config->rx_sps, config->chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps);
radio = new RadioInterfaceResamp(usrp, config->tx_sps,
config->rx_sps);
break;
case RadioDevice::MULTI_ARFCN:
radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
radio = new RadioInterfaceMulti(usrp, config->tx_sps,
config->rx_sps, config->chans);
break;
default:
LOG(ALERT) << "Unsupported radio interface configuration";
@@ -115,243 +187,203 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
return radio;
}
/* Callback function to be called every time we receive a signal from TRANSC */
static int transc_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
switch (signal) {
case S_TRANSC_STOP_REQUIRED:
gshutdown = true;
break;
default:
break;
}
return 0;
}
/* Create transceiver core
* The multi-threaded modem core operates at multiples of the GSM rate of
* 270.8333 ksps and consists of GSM specific modulation, demodulation,
* and decoding schemes. Also included are the socket interfaces for
* connecting to the upper layer stack.
*/
int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
{
Transceiver *trx;
VectorFIFO *fifo;
transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr,
trx->cfg.remote_addr, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0),
radio, trx->cfg.rssi_offset);
if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc,
trx->cfg.rach_delay, trx->cfg.egprs)) {
trx = new Transceiver(config->port, config->addr.c_str(),
config->tx_sps, config->rx_sps, config->chans,
GSM::Time(3,0), radio, config->rssi_offset);
if (!trx->init(config->filler, config->rtsc,
config->rach_delay, config->edge)) {
LOG(ALERT) << "Failed to initialize transceiver";
return -1;
delete trx;
return NULL;
}
transceiver->setSignalHandler(transc_sig_cb);
for (size_t i = 0; i < trx->cfg.num_chans; i++) {
for (size_t i = 0; i < config->chans; i++) {
fifo = radio->receiveFIFO(i);
if (fifo && transceiver->receiveFIFO(fifo, i))
if (fifo && trx->receiveFIFO(fifo, i))
continue;
LOG(ALERT) << "Could not attach FIFO to channel " << i;
return -1;
delete trx;
return NULL;
}
return 0;
return trx;
}
static void sig_handler(int signo)
{
fprintf(stdout, "signal %d received\n", signo);
switch (signo) {
case SIGINT:
case SIGTERM:
fprintf(stdout, "shutting down\n");
gshutdown = true;
break;
case SIGABRT:
case SIGUSR1:
talloc_report(tall_trx_ctx, stderr);
talloc_report_full(tall_trx_ctx, stderr);
break;
case SIGUSR2:
talloc_report_full(tall_trx_ctx, stderr);
break;
default:
break;
}
fprintf(stdout, "Received shutdown signal");
gshutdown = true;
}
static void setup_signal_handlers()
{
/* Handle keyboard interrupt SIGINT */
signal(SIGINT, &sig_handler);
signal(SIGTERM, &sig_handler);
signal(SIGABRT, &sig_handler);
signal(SIGUSR1, &sig_handler);
signal(SIGUSR2, &sig_handler);
osmo_init_ignore_signals();
if (signal(SIGINT, sig_handler) == SIG_ERR) {
fprintf(stderr, "Failed to install SIGINT signal handler\n");
exit(EXIT_FAILURE);
}
if (signal(SIGTERM, sig_handler) == SIG_ERR) {
fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
exit( EXIT_FAILURE);
}
}
static void print_help()
{
fprintf(stdout, "Options:\n"
" -h, --help This text\n"
" -C, --config Filename The config file to use\n"
" -V, --version Print the version of OsmoTRX\n"
);
" -h This text\n"
" -a UHD device args\n"
" -l Logging level (%s)\n"
" -i IP address of GSM core\n"
" -p Base port number\n"
" -e Enable EDGE receiver\n"
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
" -x Enable external 10 MHz reference\n"
" -g Enable GPSDO reference\n"
" -s Tx samples-per-symbol (1 or 4)\n"
" -b Rx samples-per-symbol (1 or 4)\n"
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -o Set baseband frequency offset (default=auto)\n"
" -r Random GMSK Normal Burst test mode with given TSC\n"
" -E Random 8-PSK Normal Burst test mode with given TSC\n"
" -A Random Access Burst test mode with delay\n"
" -R RSSI to dBm offset in dB (default=0)\n"
" -S Swap channels (UmTRX only)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
}
static void print_deprecated(char opt)
{
LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
<< " Please use VTY cfg option instead."
<< " All cmd line options are already being overriden by VTY options if set.";
}
static void handle_options(int argc, char **argv, struct trx_ctx* trx)
static void handle_options(int argc, char **argv, struct trx_config *config)
{
int option;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
bool rx_paths_set = false, tx_paths_set = false;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config", 1, 0, 'C'},
{"version", 0, 0, 'V'},
{NULL, 0, 0, 0}
};
while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options,
NULL)) != -1) {
config->log_level = "NOTICE";
config->addr = DEFAULT_TRX_IP;
config->port = DEFAULT_TRX_PORT;
config->tx_sps = DEFAULT_TX_SPS;
config->rx_sps = DEFAULT_RX_SPS;
config->chans = DEFAULT_CHANS;
config->rtsc = 0;
config->rach_delay = 0;
config->extref = false;
config->gpsref = false;
config->filler = Transceiver::FILLER_ZERO;
config->mcbts = false;
config->offset = 0.0;
config->rssi_offset = 0.0;
config->swap_channels = false;
config->edge = false;
while ((option = getopt(argc, argv, "ha:l:i:p:c:dmxgfo:s:b:r:E:A:R:Se")) != -1) {
switch (option) {
case 'h':
print_help();
exit(0);
break;
case 'a':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
config->dev_args = optarg;
break;
case 'l':
print_deprecated(option);
log_set_log_level(osmo_stderr_target, atoi(optarg));
config->log_level = optarg;
break;
case 'i':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg);
break;
case 'j':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg);
config->addr = optarg;
break;
case 'p':
print_deprecated(option);
trx->cfg.base_port = atoi(optarg);
config->port = atoi(optarg);
break;
case 'c':
print_deprecated(option);
trx->cfg.num_chans = atoi(optarg);
config->chans = atoi(optarg);
break;
case 'm':
print_deprecated(option);
trx->cfg.multi_arfcn = true;
config->mcbts = true;
break;
case 'x':
print_deprecated(option);
trx->cfg.clock_ref = REF_EXTERNAL;
config->extref = true;
break;
case 'g':
print_deprecated(option);
trx->cfg.clock_ref = REF_GPS;
config->gpsref = true;
break;
case 'f':
print_deprecated(option);
trx->cfg.filler = FILLER_DUMMY;
config->filler = Transceiver::FILLER_DUMMY;
break;
case 'o':
print_deprecated(option);
trx->cfg.offset = atof(optarg);
config->offset = atof(optarg);
break;
case 's':
print_deprecated(option);
trx->cfg.tx_sps = atoi(optarg);
config->tx_sps = atoi(optarg);
break;
case 'b':
print_deprecated(option);
trx->cfg.rx_sps = atoi(optarg);
config->rx_sps = atoi(optarg);
break;
case 'r':
print_deprecated(option);
trx->cfg.rtsc_set = true;
trx->cfg.rtsc = atoi(optarg);
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
trx->cfg.filler = FILLER_NORM_RAND;
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_NORM_RAND;
break;
case 'E':
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_EDGE_RAND;
break;
case 'A':
print_deprecated(option);
trx->cfg.rach_delay_set = true;
trx->cfg.rach_delay = atoi(optarg);
trx->cfg.filler = FILLER_ACCESS_RAND;
config->rach_delay = atoi(optarg);
config->filler = Transceiver::FILLER_ACCESS_RAND;
break;
case 'R':
print_deprecated(option);
trx->cfg.rssi_offset = atof(optarg);
config->rssi_offset = atof(optarg);
break;
case 'S':
print_deprecated(option);
trx->cfg.swap_channels = true;
config->swap_channels = true;
break;
case 'e':
print_deprecated(option);
trx->cfg.egprs = true;
break;
case 't':
print_deprecated(option);
trx->cfg.sched_rr = atoi(optarg);
break;
case 'y':
print_deprecated(option);
tx_paths = comma_delimited_to_vector(optarg);
tx_paths_set = true;
break;
case 'z':
print_deprecated(option);
rx_paths = comma_delimited_to_vector(optarg);
rx_paths_set = true;
break;
case 'C':
config_file = optarg;
break;
case 'V':
print_version(1);
exit(0);
config->edge = true;
break;
default:
goto bad_config;
print_help();
exit(0);
}
}
/* Cmd line option specific validation & setup */
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
if ((config->edge) || (config->mcbts)) {
config->tx_sps = 4;
config->rx_sps = 4;
}
if (trx->cfg.num_chans > TRX_CHAN_MAX) {
LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX;
if (config->gpsref && config->extref) {
printf("External and GPSDO references unavailable at the same time\n\n");
goto bad_config;
}
if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) ||
(rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) {
LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match";
if (!config->edge && (config->filler == Transceiver::FILLER_EDGE_RAND)) {
printf("Can't enable EDGE filler when EDGE mode is disabled\n\n");
goto bad_config;
}
for (i = 0; i < trx->cfg.num_chans; i++) {
trx->cfg.chans[i].trx = trx;
trx->cfg.chans[i].idx = i;
if (tx_paths_set)
osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str());
if (rx_paths_set)
osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str());
if ((config->tx_sps != 1) && (config->tx_sps != 4) &&
(config->rx_sps != 1) && (config->rx_sps != 4)) {
printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
goto bad_config;
}
if (config->rtsc > 7) {
printf("Invalid training sequence %i\n\n", config->rtsc);
goto bad_config;
}
if (config->rach_delay > 68) {
printf("RACH delay is too big %i\n\n", config->rach_delay);
goto bad_config;
}
return;
@@ -361,231 +393,90 @@ bad_config:
exit(0);
}
int trx_validate_config(struct trx_ctx *trx)
int main(int argc, char *argv[])
{
if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) {
LOG(ERROR) << "Unsupported number of channels";
return -1;
}
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
if ((trx->cfg.egprs || trx->cfg.multi_arfcn) &&
(trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) {
LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
return -1;
}
return 0;
}
static int set_sched_rr(unsigned int prio)
{
struct sched_param param;
int rc;
memset(&param, 0, sizeof(param));
param.sched_priority = prio;
LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority;
rc = sched_setscheduler(getpid(), SCHED_RR, &param);
if (rc != 0) {
LOG(ERROR) << "Config: Setting SCHED_RR failed";
return -1;
}
return 0;
}
static void print_config(struct trx_ctx *trx)
{
unsigned int i;
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl;
ost << " Device args............. " << charp2str(trx->cfg.dev_args) << std::endl;
ost << " TRX Base Port........... " << trx->cfg.base_port << std::endl;
ost << " TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl;
ost << " GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl;
ost << " Channels................ " << trx->cfg.num_chans << std::endl;
ost << " Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl;
ost << " Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl;
ost << " EDGE support............ " << trx->cfg.egprs << std::endl;
ost << " Reference............... " << trx->cfg.clock_ref << std::endl;
ost << " C0 Filler Table......... " << trx->cfg.filler << std::endl;
ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
ost << " Tuning offset........... " << trx->cfg.offset << std::endl;
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl;
ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
ost << " Tx Antennas.............";
for (i = 0; i < trx->cfg.num_chans; i++) {
std::string p = charp2str(trx->cfg.chans[i].tx_path);
ost << " '" << ((p != "") ? p : "<default>") << "'";
}
ost << std::endl;
ost << " Rx Antennas.............";
for (i = 0; i < trx->cfg.num_chans; i++) {
std::string p = charp2str(trx->cfg.chans[i].rx_path);
ost << " '" << ((p != "") ? p : "<default>") << "'";
}
ost << std::endl;
LOG(INFO) << ost << std::endl;
}
static void trx_stop()
{
LOG(NOTICE) << "Shutting down transceiver..." << std::endl;
delete transceiver;
delete radio;
delete usrp;
}
static int trx_start(struct trx_ctx *trx)
{
int type, chans;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
int type, chans, ref;
RadioDevice *usrp;
RadioInterface *radio = NULL;
Transceiver *trx = NULL;
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
struct trx_config config;
#ifdef HAVE_SSE3
printf("Info: SSE3 support compiled in");
if (__builtin_cpu_supports("sse3"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#endif
#ifdef HAVE_SSE4_1
printf("Info: SSE4.1 support compiled in");
if (__builtin_cpu_supports("sse4.1"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#endif
convolve_init();
convert_init();
handle_options(argc, argv, &config);
setup_signal_handlers();
/* Check database sanity */
if (!trx_setup_config(&config)) {
std::cerr << "Config: Database failure - exiting" << std::endl;
return EXIT_FAILURE;
}
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
srandom(time(NULL));
/* Create the low level device object */
if (trx->cfg.multi_arfcn)
if (config.mcbts)
iface = RadioDevice::MULTI_ARFCN;
/* Generate vector of rx/tx_path: */
for (i = 0; i < trx->cfg.num_chans; i++) {
rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
}
if (config.extref)
ref = RadioDevice::REF_EXTERNAL;
else if (config.gpsref)
ref = RadioDevice::REF_GPS;
else
ref = RadioDevice::REF_INTERNAL;
usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
trx->cfg.num_chans, trx->cfg.offset,
tx_paths, rx_paths);
type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
usrp = RadioDevice::make(config.tx_sps, config.rx_sps, iface,
config.chans, config.offset);
type = usrp->open(config.dev_args, ref, config.swap_channels);
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;
}
/* Setup the appropriate device interface */
radio = makeRadioInterface(trx, usrp, type);
radio = makeRadioInterface(&config, usrp, type);
if (!radio)
goto shutdown;
/* Create the transceiver core */
if (makeTransceiver(trx, radio) < 0)
trx = makeTransceiver(&config, radio);
if (!trx)
goto shutdown;
chans = transceiver->numChans();
LOG(NOTICE) << "-- Transceiver active with "
chans = trx->numChans();
std::cout << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl;
return 0;
while (!gshutdown)
sleep(1);
shutdown:
trx_stop();
return -1;
}
std::cout << "Shutting down transceiver..." << std::endl;
int main(int argc, char *argv[])
{
int rc;
tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
msgb_talloc_ctx_init(tall_trx_ctx, 0);
g_vty_info.tall_ctx = tall_trx_ctx;
setup_signal_handlers();
g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
#ifdef HAVE_SSE3
printf("Info: SSE3 support compiled in");
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (__builtin_cpu_supports("sse3"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#else
printf(", but runtime SIMD detection disabled\n");
#endif
#endif
#ifdef HAVE_SSE4_1
printf("Info: SSE4.1 support compiled in");
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (__builtin_cpu_supports("sse4.1"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#else
printf(", but runtime SIMD detection disabled\n");
#endif
#endif
convolve_init();
convert_init();
osmo_init_logging2(tall_trx_ctx, &log_info);
osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info);
ctrl_vty_init(tall_trx_ctx);
trx_vty_init(g_trx_ctx);
logging_vty_add_cmds();
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();
handle_options(argc, argv, g_trx_ctx);
rate_ctr_init(tall_trx_ctx);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
exit(2);
}
rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
if (rc < 0)
exit(1);
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1);
}
/* Backward compatibility: Hack to have 1 channel allocated by default.
* Can be Dropped once we * get rid of "-c" cmdline param */
if (g_trx_ctx->cfg.num_chans == 0) {
g_trx_ctx->cfg.num_chans = 1;
g_trx_ctx->cfg.chans[0].trx = g_trx_ctx;
g_trx_ctx->cfg.chans[0].idx = 0;
LOG(ERROR) << "No explicit channel config found. Make sure you" \
" configure channels in VTY config. Using 1 channel as default," \
" but expect your config to break in the future.";
}
print_config(g_trx_ctx);
if (trx_validate_config(g_trx_ctx) < 0) {
LOG(ERROR) << "Config failure - exiting";
return EXIT_FAILURE;
}
if (g_trx_ctx->cfg.sched_rr) {
if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0)
return EXIT_FAILURE;
}
srandom(time(NULL));
if(trx_start(g_trx_ctx) < 0)
return EXIT_FAILURE;
while (!gshutdown)
osmo_select_main(0);
trx_stop();
delete trx;
delete radio;
delete usrp;
return 0;
}

View File

@@ -18,13 +18,6 @@
#include <string>
#include <vector>
#include "GSMCommon.h"
#include "Logger.h"
extern "C" {
#include "config_defs.h"
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -40,7 +33,7 @@ class RadioDevice {
public:
/* Available transport bus types */
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED, TX_WINDOW_LMS1 };
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
/* Radio interface types */
enum InterfaceType {
@@ -50,10 +43,14 @@ class RadioDevice {
MULTI_ARFCN,
};
enum ReferenceType {
REF_INTERNAL,
REF_EXTERNAL,
REF_GPS,
};
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chans = 1, double offset = 0.0,
const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
size_t chans = 1, double offset = 0.0);
/** Initialize the USRP */
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
@@ -139,24 +136,6 @@ class RadioDevice {
/** return minimum Tx Gain **/
virtual double minTxGain(void) = 0;
/** sets the RX path to use, returns true if successful and false otherwise */
virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0;
/** return the used RX path */
virtual std::string getRxAntenna(size_t chan = 0) = 0;
/** sets the RX path to use, returns true if successful and false otherwise */
virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0;
/** return the used RX path */
virtual std::string getTxAntenna(size_t chan = 0) = 0;
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual bool requiresRadioAlign() = 0;
/** Minimum latency that the device can achieve */
virtual GSM::Time minLatency() = 0;
/** Return internal status values */
virtual double getTxFreq(size_t chan = 0) = 0;
virtual double getRxFreq(size_t chan = 0) = 0;
@@ -164,47 +143,6 @@ class RadioDevice {
virtual double numberRead()=0;
virtual double numberWritten()=0;
protected:
size_t tx_sps, rx_sps;
InterfaceType iface;
size_t chans;
double lo_offset;
std::vector<std::string> tx_paths, rx_paths;
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chans), lo_offset(offset),
tx_paths(tx_paths), rx_paths(rx_paths)
{ }
bool set_antennas() {
unsigned int i;
for (i = 0; i < tx_paths.size(); i++) {
if (tx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
if (!setTxAntenna(tx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
return false;
}
}
for (i = 0; i < rx_paths.size(); i++) {
if (rx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
if (!setRxAntenna(rx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
return false;
}
}
LOG(INFO) << "Antennas configured successfully";
return true;
}
};
#endif

View File

@@ -22,6 +22,7 @@
#include "radioInterface.h"
#include "Resampler.h"
#include <Logger.h>
#include <PRBS.h>
extern "C" {
#include "convert.h"
@@ -75,14 +76,6 @@ bool RadioInterface::init(int type)
void RadioInterface::close()
{
for (std::vector<RadioBuffer*>::iterator it = sendBuffer.begin(); it != sendBuffer.end(); ++it)
delete *it;
for (std::vector<RadioBuffer*>::iterator it = recvBuffer.begin(); it != recvBuffer.end(); ++it)
delete *it;
for (std::vector<short*>::iterator it = convertSendBuffer.begin(); it != convertSendBuffer.end(); ++it)
delete[] *it;
for (std::vector<short*>::iterator it = convertRecvBuffer.begin(); it != convertRecvBuffer.end(); ++it)
delete[] *it;
sendBuffer.resize(0);
recvBuffer.resize(0);
convertSendBuffer.resize(0);
@@ -153,32 +146,16 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
return mRadio->setRxFreq(freq, chan);
}
/** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
set_selfthread_name("AlignRadio");
while (1) {
sleep(60);
radioInterface->alignRadio();
pthread_testcancel();
}
return NULL;
}
void RadioInterface::alignRadio() {
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
}
bool RadioInterface::start()
{
if (mOn)
return true;
LOG(INFO) << "Starting radio device";
if (mRadio->requiresRadioAlign())
mAlignRadioServiceLoopThread.start(
(void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#ifdef USRP1
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
if (!mRadio->start())
return false;
@@ -215,6 +192,22 @@ bool RadioInterface::stop()
return true;
}
#ifdef USRP1
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
while (1) {
radioInterface->alignRadio();
pthread_testcancel();
}
return NULL;
}
void RadioInterface::alignRadio() {
sleep(60);
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
}
#endif
void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros)
{
@@ -227,15 +220,14 @@ void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
while (pushBuffer());
}
int RadioInterface::driveReceiveRadio()
bool RadioInterface::driveReceiveRadio()
{
radioVector *burst = NULL;
if (!mOn)
return 0;
return false;
if (pullBuffer() < 0)
return -1;
pullBuffer();
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
@@ -249,11 +241,11 @@ int RadioInterface::driveReceiveRadio()
else
burstSize = symbolsPerSlot + (tN % 4 == 0);
/*
/*
* Pre-allocate head room for the largest correlation size
* so we can later avoid a re-allocation and copy
* */
size_t head = GSM::gRACHSynchSequenceTS0.size();
size_t head = GSM::gRACHSynchSequence.size();
/*
* Form receive bursts and pass up to transceiver. Use repeating
@@ -280,7 +272,7 @@ int RadioInterface::driveReceiveRadio()
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
}
return 1;
return true;
}
bool RadioInterface::isUnderrun()
@@ -310,14 +302,13 @@ double RadioInterface::getRxGain(size_t chan)
}
/* Receive a timestamped chunk from the device */
int RadioInterface::pullBuffer()
void RadioInterface::pullBuffer()
{
bool local_underrun;
int numRecv;
size_t segmentLen = recvBuffer[0]->getSegmentLen();
size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
if (recvBuffer[0]->getFreeSegments() <= 0)
return -1;
return;
/* Outer buffer access size is fixed */
numRecv = mRadio->readSamples(convertRecvBuffer,
@@ -326,9 +317,9 @@ int RadioInterface::pullBuffer()
readTimestamp,
&local_underrun);
if ((size_t) numRecv != segmentLen) {
if (numRecv != segmentLen) {
LOG(ALERT) << "Receive error " << numRecv;
return -1;
return;
}
for (size_t i = 0; i < mChans; i++) {
@@ -339,7 +330,6 @@ int RadioInterface::pullBuffer()
underrun |= local_underrun;
readTimestamp += numRecv;
return 0;
}
/* Send timestamped chunk to the device with arbitrary size */

View File

@@ -14,7 +14,7 @@
#include "sigProcLib.h"
#include "sigProcLib.h"
#include "GSMCommon.h"
#include "LinkedLists.h"
#include "radioDevice.h"
@@ -71,7 +71,7 @@ private:
virtual bool pushBuffer(void);
/** pull GSM bursts from the receive buffer */
virtual int pullBuffer(void);
virtual void pullBuffer(void);
public:
@@ -116,8 +116,8 @@ public:
void driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros);
/** drive reception of GSM bursts. -1: Error. 0: Radio off. 1: Received something. */
int driveReceiveRadio();
/** drive reception of GSM bursts */
bool driveReceiveRadio();
int setPowerAttenuation(int atten, size_t chan = 0);
@@ -130,26 +130,31 @@ public:
/** set thread priority on current thread */
void setPriority(float prio = 0.5) { mRadio->setPriority(prio); }
/** get transport window type of attached device */
/** get transport window type of attached device */
enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
/** Minimum latency that the device can achieve */
GSM::Time minLatency() { return mRadio->minLatency(); }
#if USRP1
protected:
/** drive synchronization of Tx/Rx of USRP */
void alignRadio();
friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
#endif
};
#if USRP1
/** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface*);
#endif
class RadioInterfaceResamp : public RadioInterface {
private:
signalVector *outerSendBuffer;
signalVector *outerRecvBuffer;
bool pushBuffer();
int pullBuffer();
void pullBuffer();
public:
RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
@@ -162,7 +167,7 @@ public:
class RadioInterfaceMulti : public RadioInterface {
private:
bool pushBuffer();
int pullBuffer();
void pullBuffer();
signalVector *outerSendBuffer;
signalVector *outerRecvBuffer;

View File

@@ -1,7 +1,7 @@
/*
* Multi-carrier radio interface
*
* Copyright (C) 2016 Ettus Research LLC
* Copyright (C) 2016 Ettus Research LLC
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
@@ -225,15 +225,14 @@ bool RadioInterfaceMulti::init(int type)
}
/* Receive a timestamped chunk from the device */
int RadioInterfaceMulti::pullBuffer()
void RadioInterfaceMulti::pullBuffer()
{
bool local_underrun;
size_t num;
float *buf;
unsigned int i;
if (recvBuffer[0]->getFreeSegments() <= 0)
return -1;
return;
/* Outer buffer access size is fixed */
num = mRadio->readSamples(convertRecvBuffer,
@@ -243,7 +242,7 @@ int RadioInterfaceMulti::pullBuffer()
&local_underrun);
if (num != channelizer->inputLen()) {
LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
return -1;
return;
}
convert_short_float((float *) outerRecvBuffer->begin(),
@@ -274,22 +273,10 @@ int RadioInterfaceMulti::pullBuffer()
buf = channelizer->outputBuffer(pchan);
size_t cLen = channelizer->outputLen();
size_t hLen = dnsampler->len();
size_t hSize = 2 * hLen * sizeof(float);
float *fdst = &buf[2 * -hLen];
complex *src = history[lchan]->begin();
for (i = 0; i < hLen; i++) {
fdst[0] = src->real();
fdst[1] = src->imag();
src++;
fdst += 2;
}
complex *dst = history[lchan]->begin();
float *fsrc = &buf[2 * (cLen - hLen)];
for (i = 0; i < hLen; i++) {
*dst = complex(fdst[0], fdst[1]);
fsrc += 2;
dst++;
}
memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
float *wr_segment = recvBuffer[lchan]->getWriteSegment();
@@ -301,7 +288,6 @@ int RadioInterfaceMulti::pullBuffer()
LOG(ALERT) << "Sample rate upsampling error";
}
}
return 0;
}
/* Send a timestamped chunk to the device */

View File

@@ -160,13 +160,13 @@ bool RadioInterfaceResamp::init(int type)
}
/* Receive a timestamped chunk from the device */
int RadioInterfaceResamp::pullBuffer()
void RadioInterfaceResamp::pullBuffer()
{
bool local_underrun;
int rc, num_recv;
if (recvBuffer[0]->getFreeSegments() <= 0)
return -1;
return;
/* Outer buffer access size is fixed */
num_recv = mRadio->readSamples(convertRecvBuffer,
@@ -176,7 +176,7 @@ int RadioInterfaceResamp::pullBuffer()
&local_underrun);
if (num_recv != (int) resamp_outchunk) {
LOG(ALERT) << "Receive error " << num_recv;
return -1;
return;
}
convert_short_float((float *) outerRecvBuffer->begin(),
@@ -196,7 +196,6 @@ int RadioInterfaceResamp::pullBuffer()
/* Set history for the next chunk */
outerRecvBuffer->updateHistory();
return 0;
}
/* Send a timestamped chunk to the device */

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@
#include "Vector.h"
#include "Complex.h"
#include "BitVector.h"
#include "PRBS.h"
#include "signalVector.h"
/* Burst lengths */
@@ -25,15 +26,24 @@
#define EDGE_BURST_NBITS 444
#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
/** Convolution type indicator */
enum ConvType {
START_ONLY,
NO_DELAY,
CUSTOM,
UNDEFINED,
};
/** Codes for burst types of received bursts*/
enum CorrType{
OFF, ///< timeslot is off
TSC, ///< timeslot should contain a normal burst
EXT_RACH, ///< timeslot should contain an extended access burst
RACH, ///< timeslot should contain an access burst
EDGE, ///< timeslot should contain an EDGE burst
IDLE ///< timeslot is an idle (or dummy) burst
};
std::string corrTypeToString(CorrType corr);
std::ostream& operator<<(std::ostream& os, CorrType corr);
enum SignalError {
SIGERR_NONE,
@@ -52,12 +62,66 @@ enum SignalError {
*/
#define BURST_THRESH 4.0
/** Convert a linear number to a dB value */
float dB(float x);
/** Convert a dB value into a linear value */
float dBinv(float x);
/** Compute the energy of a vector */
float vectorNorm2(const signalVector &x);
/** Compute the average power of a vector */
float vectorPower(const signalVector &x);
/** Setup the signal processing library */
bool sigProcLibSetup();
/** Destroy the signal processing library */
void sigProcLibDestroy(void);
/**
Convolve two vectors.
@param a,b The vectors to be convolved.
@param c, A preallocated vector to hold the convolution result.
@param spanType The type/span of the convolution.
@return The convolution result or NULL on error.
*/
signalVector *convolve(const signalVector *a, const signalVector *b,
signalVector *c, ConvType spanType,
size_t start = 0, size_t len = 0,
size_t step = 1, int offset = 0);
/**
Frequency shift a vector.
@param y The frequency shifted vector.
@param x The vector to-be-shifted.
@param freq The digital frequency shift
@param startPhase The starting phase of the oscillator
@param finalPhase The final phase of the oscillator
@return The frequency shifted vector.
*/
signalVector* frequencyShift(signalVector *y,
signalVector *x,
float freq = 0.0,
float startPhase = 0.0,
float *finalPhase=NULL);
/**
Correlate two vectors.
@param a,b The vectors to be correlated.
@param c, A preallocated vector to hold the correlation result.
@param spanType The type/span of the correlation.
@return The correlation result.
*/
signalVector* correlate(signalVector *a,
signalVector *b,
signalVector *c,
ConvType spanType,
bool bReversedConjugated = false,
unsigned startIx = 0,
unsigned len = 0);
/** Operate soft slicer on a soft-bit vector */
bool vectorSlicer(SoftVector *x);
@@ -77,7 +141,7 @@ signalVector *generateEdgeBurst(int tsc);
signalVector *generateEmptyBurst(int sps, int tn);
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandNormalBurst(int tsc, int sps, int tn);
signalVector *genRandNormalBurst(int tsc, int sps, int tn, PRBS &prbs);
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandAccessBurst(int delay, int sps, int tn);
@@ -85,6 +149,45 @@ signalVector *genRandAccessBurst(int delay, int sps, int tn);
/** Generate a dummy GSM burst - 4 or 1 SPS */
signalVector *generateDummyBurst(int sps, int tn);
/** Sinc function */
float sinc(float x);
/** Delay a vector */
signalVector *delayVector(const signalVector *in, signalVector *out, float delay);
/** Add two vectors in-place */
bool addVector(signalVector &x,
signalVector &y);
/** Multiply two vectors in-place*/
bool multVector(signalVector &x,
signalVector &y);
/** Generate a vector of gaussian noise */
signalVector *gaussianNoise(int length,
float variance = 1.0,
complex mean = complex(0.0));
/**
Given a non-integer index, interpolate a sample.
@param inSig The signal from which to interpolate.
@param ix The index.
@return The interpolated signal value.
*/
complex interpolatePoint(const signalVector &inSig,
float ix);
/**
Given a correlator output, locate the correlation peak.
@param rxBurst The correlator result.
@param peakIndex Pointer to value to receive interpolated peak index.
@param avgPower Power to value to receive mean power.
@return Peak value.
*/
complex peakDetect(const signalVector &rxBurst,
float *peakIndex,
float *avgPwr);
/**
Apply a scalar to a vector.
@param x The vector of interest.
@@ -101,6 +204,68 @@ void scaleVector(signalVector &x,
*/
float energyDetect(const signalVector &rxBurst,
unsigned windowLength);
/**
RACH aka Access Burst correlator/detector.
@param burst The received GSM burst of interest.
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received RACH burst.
@param toa The estimate time-of-arrival of received RACH burst.
@param max_toa The maximum expected time-of-arrival
@return 1 if threshold value is reached,
negative value (-SignalError) on error,
zero (SIGERR_NONE) if no burst is detected
*/
int detectRACHBurst(const signalVector &burst,
float threshold,
int sps,
complex &amplitude,
float &toa,
unsigned max_toa);
/**
GMSK Normal Burst correlator/detector.
@param rxBurst The received GSM burst of interest.
@param tsc Midamble type (0..7) also known as TSC
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received TSC burst.
@param toa The estimate time-of-arrival of received TSC burst.
@param max_toa The maximum expected time-of-arrival
@return 1 if threshold value is reached,
negative value (-SignalError) on error,
zero (SIGERR_NONE) if no burst is detected
*/
int analyzeTrafficBurst(const signalVector &burst,
unsigned tsc,
float threshold,
int sps,
complex &amplitude,
float &toa,
unsigned max_toa);
/**
EDGE/8-PSK Normal Burst correlator/detector
@param burst The received GSM burst of interest
@param tsc Midamble type (0..7) also known as TSC
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received TSC burst.
@param toa The estimate time-of-arrival of received TSC burst.
@param max_toa The maximum expected time-of-arrival
@return 1 if threshold value is reached,
negative value (-SignalError) on error,
zero (SIGERR_NONE) if no burst is detected
*/
int detectEdgeBurst(const signalVector &burst,
unsigned tsc,
float threshold,
int sps,
complex &amplitude,
float &toa,
unsigned max_toa);
/**
8-PSK/GMSK/RACH burst detector
@param burst The received GSM burst of interest
@@ -123,6 +288,44 @@ int detectAnyBurst(const signalVector &burst,
float &toa,
unsigned max_toa);
/**
Downsample 4 SPS to 1 SPS using a polyphase filterbank
@param burst Input burst of at least 624 symbols
@return Decimated signal vector of 156 symbols
*/
signalVector *downsampleBurst(const signalVector &burst);
/**
Decimate a vector.
@param wVector The vector of interest.
@param factor Decimation factor.
@return The decimated signal vector.
*/
signalVector *decimateVector(signalVector &wVector, size_t factor);
/**
Demodulates a GMSK burst using a soft-slicer.
@param rxBurst The burst to be demodulated.
@param gsmPulse The GSM pulse.
@param sps The number of samples per GSM symbol.
@param channel The amplitude estimate of the received burst.
@param TOA The time-of-arrival of the received burst.
@return The demodulated bit sequence.
*/
SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps,
complex channel, float TOA);
/**
Demodulate 8-PSK EDGE burst with soft symbol ooutput
@param rxBurst The burst to be demodulated.
@param sps The number of samples per GSM symbol.
@param channel The amplitude estimate of the received burst.
@param TOA The time-of-arrival of the received burst.
@return The demodulated bit sequence.
*/
SoftVector *demodEdgeBurst(const signalVector &rxBurst, int sps,
complex channel, float TOA);
/** Demodulate burst basde on type and output soft bits */
SoftVector *demodAnyBurst(const signalVector &burst, int sps,
complex amp, float toa, CorrType type);

View File

@@ -1,20 +1,20 @@
#include "signalVector.h"
signalVector::signalVector(size_t size, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)
: Vector<complex>(size, wAllocFunc, wFreeFunc),
signalVector::signalVector(size_t size)
: Vector<complex>(size),
real(false), aligned(false), symmetry(NONE)
{
}
signalVector::signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)
: Vector<complex>(size + start, wAllocFunc, wFreeFunc),
signalVector::signalVector(size_t size, size_t start)
: Vector<complex>(size + start),
real(false), aligned(false), symmetry(NONE)
{
mStart = mData + start;
}
signalVector::signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)
: Vector<complex>(data, data + start, data + start + span, wAllocFunc, wFreeFunc),
signalVector::signalVector(complex *data, size_t start, size_t span)
: Vector<complex>(NULL, data + start, data + start + span),
real(false), aligned(false), symmetry(NONE)
{
}
@@ -41,14 +41,7 @@ signalVector::signalVector(const signalVector &vector,
void signalVector::operator=(const signalVector& vector)
{
resize(vector.size() + vector.getStart());
unsigned int i;
complex *dst = mData;
complex *src = vector.mData;
for (i = 0; i < size(); i++, src++, dst++)
*dst = *src;
/* TODO: optimize for non non-trivially copyable types: */
/*memcpy(mData, vector.mData, bytes()); */
memcpy(mData, vector.mData, bytes());
mStart = mData + vector.getStart();
}
@@ -65,13 +58,8 @@ size_t signalVector::getStart() const
size_t signalVector::updateHistory()
{
size_t num = getStart();
unsigned int i;
complex *dst = mData;
complex *src = mStart + this->size() - num;
for (i = 0; i < num; i++, src++, dst++)
*dst = *src;
/* TODO: optimize for non non-trivially copyable types: */
/*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */
memmove(mData, mStart + this->size() - num, num * sizeof(complex));
return num;
}

View File

@@ -13,13 +13,13 @@ enum Symmetry {
class signalVector: public Vector<complex> {
public:
/** Default constructor */
signalVector(size_t size = 0, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
signalVector(size_t size = 0);
/** Construct with head room */
signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
signalVector(size_t size, size_t start);
/** Construct from existing buffer data (buffer not managed) */
signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
signalVector(complex *data, size_t start, size_t span);
/** Construct by from existing vector */
signalVector(const signalVector &vector);

View File

@@ -1,15 +1,11 @@
if !ARCH_ARM
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
libarch_la_LIBADD =
# SSE 3 specific code
if HAVE_SSE3
@@ -29,5 +25,8 @@ libarch_la_LIBADD += libarch_sse_4_1.la
endif
libarch_la_SOURCES = \
../common/convolve_base.c \
../common/convert_base.c \
convert.c \
convolve.c
endif

View File

@@ -46,7 +46,6 @@ void convert_init(void)
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;
@@ -61,7 +60,6 @@ void convert_init(void)
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)

View File

@@ -82,7 +82,7 @@ void convolve_init(void)
c.conv_real4n = (void *)_base_convolve_real;
c.conv_real = (void *)_base_convolve_real;
#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
#ifdef HAVE_SSE3
if (__builtin_cpu_supports("sse3")) {
c.conv_cmplx_4n = sse_conv_cmplx_4n;
c.conv_cmplx_8n = sse_conv_cmplx_8n;

View File

@@ -1,982 +0,0 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
# or '14' (for the C++14 standard).
#
# The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
# -std=c++11). If neither is specified, you get whatever works, with
# preference for an extended mode.
#
# The third argument, if specified 'mandatory' or if left unspecified,
# indicates that baseline support for the specified C++ standard is
# required and that the macro should error out if no mode with that
# support is found. If specified 'optional', then configuration proceeds
# regardless, after defining HAVE_CXX${VERSION} if and only if a
# supporting mode is found.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
# Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 7
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13).
AX_REQUIRE_DEFINED([AC_MSG_WARN])
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [],
[$2], [ext], [],
[$2], [noext], [],
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
AC_LANG_PUSH([C++])dnl
ac_success=no
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
ax_cv_cxx_compile_cxx$1,
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[ax_cv_cxx_compile_cxx$1=yes],
[ax_cv_cxx_compile_cxx$1=no])])
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
ac_success=yes
fi
m4_if([$2], [noext], [], [dnl
if test x$ac_success = xno; then
for alternative in ${ax_cxx_compile_alternatives}; do
switch="-std=gnu++${alternative}"
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
fi])
m4_if([$2], [ext], [], [dnl
if test x$ac_success = xno; then
dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11"
for alternative in ${ax_cxx_compile_alternatives}; do
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
if test x$ac_success = xyes; then
break
fi
done
fi])
AC_LANG_POP([C++])
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
if test x$ac_success = xno; then
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
fi
fi
if test x$ac_success = xno; then
HAVE_CXX$1=0
AC_MSG_NOTICE([No compiler with C++$1 support was found])
else
HAVE_CXX$1=1
AC_DEFINE(HAVE_CXX$1,1,
[define if the compiler supports basic C++$1 syntax])
fi
AC_SUBST(HAVE_CXX$1)
m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])])
])
dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
)
dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
)
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
)
dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
// If the compiler admits that it is not ready for C++11, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#else
namespace cxx11
{
namespace test_static_assert
{
template <typename T>
struct check
{
static_assert(sizeof(int) <= sizeof(T), "not big enough");
};
}
namespace test_final_override
{
struct Base
{
virtual void f() {}
};
struct Derived : public Base
{
virtual void f() override {}
};
}
namespace test_double_right_angle_brackets
{
template < typename T >
struct check {};
typedef check<void> single_type;
typedef check<check<void>> double_type;
typedef check<check<check<void>>> triple_type;
typedef check<check<check<check<void>>>> quadruple_type;
}
namespace test_decltype
{
int
f()
{
int a = 1;
decltype(a) b = 2;
return a + b;
}
}
namespace test_type_deduction
{
template < typename T1, typename T2 >
struct is_same
{
static const bool value = false;
};
template < typename T >
struct is_same<T, T>
{
static const bool value = true;
};
template < typename T1, typename T2 >
auto
add(T1 a1, T2 a2) -> decltype(a1 + a2)
{
return a1 + a2;
}
int
test(const int c, volatile int v)
{
static_assert(is_same<int, decltype(0)>::value == true, "");
static_assert(is_same<int, decltype(c)>::value == false, "");
static_assert(is_same<int, decltype(v)>::value == false, "");
auto ac = c;
auto av = v;
auto sumi = ac + av + 'x';
auto sumf = ac + av + 1.0;
static_assert(is_same<int, decltype(ac)>::value == true, "");
static_assert(is_same<int, decltype(av)>::value == true, "");
static_assert(is_same<int, decltype(sumi)>::value == true, "");
static_assert(is_same<int, decltype(sumf)>::value == false, "");
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
return (sumf > 0.0) ? sumi : add(c, v);
}
}
namespace test_noexcept
{
int f() { return 0; }
int g() noexcept { return 0; }
static_assert(noexcept(f()) == false, "");
static_assert(noexcept(g()) == true, "");
}
namespace test_constexpr
{
template < typename CharT >
unsigned long constexpr
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
{
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
}
template < typename CharT >
unsigned long constexpr
strlen_c(const CharT *const s) noexcept
{
return strlen_c_r(s, 0UL);
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("1") == 1UL, "");
static_assert(strlen_c("example") == 7UL, "");
static_assert(strlen_c("another\0example") == 7UL, "");
}
namespace test_rvalue_references
{
template < int N >
struct answer
{
static constexpr int value = N;
};
answer<1> f(int&) { return answer<1>(); }
answer<2> f(const int&) { return answer<2>(); }
answer<3> f(int&&) { return answer<3>(); }
void
test()
{
int i = 0;
const int c = 0;
static_assert(decltype(f(i))::value == 1, "");
static_assert(decltype(f(c))::value == 2, "");
static_assert(decltype(f(0))::value == 3, "");
}
}
namespace test_uniform_initialization
{
struct test
{
static const int zero {};
static const int one {1};
};
static_assert(test::zero == 0, "");
static_assert(test::one == 1, "");
}
namespace test_lambdas
{
void
test1()
{
auto lambda1 = [](){};
auto lambda2 = lambda1;
lambda1();
lambda2();
}
int
test2()
{
auto a = [](int i, int j){ return i + j; }(1, 2);
auto b = []() -> int { return '0'; }();
auto c = [=](){ return a + b; }();
auto d = [&](){ return c; }();
auto e = [a, &b](int x) mutable {
const auto identity = [](int y){ return y; };
for (auto i = 0; i < a; ++i)
a += b--;
return x + identity(a + b);
}(0);
return a + b + c + d + e;
}
int
test3()
{
const auto nullary = [](){ return 0; };
const auto unary = [](int x){ return x; };
using nullary_t = decltype(nullary);
using unary_t = decltype(unary);
const auto higher1st = [](nullary_t f){ return f(); };
const auto higher2nd = [unary](nullary_t f1){
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
};
return higher1st(nullary) + higher2nd(nullary)(unary);
}
}
namespace test_variadic_templates
{
template <int...>
struct sum;
template <int N0, int... N1toN>
struct sum<N0, N1toN...>
{
static constexpr auto value = N0 + sum<N1toN...>::value;
};
template <>
struct sum<>
{
static constexpr auto value = 0;
};
static_assert(sum<>::value == 0, "");
static_assert(sum<1>::value == 1, "");
static_assert(sum<23>::value == 23, "");
static_assert(sum<1, 2>::value == 3, "");
static_assert(sum<5, 5, 11>::value == 21, "");
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
}
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
// because of this.
namespace test_template_alias_sfinae
{
struct foo {};
template<typename T>
using member = typename T::member_type;
template<typename T>
void func(...) {}
template<typename T>
void func(member<T>*) {}
void test();
void test() { func<foo>(0); }
}
} // namespace cxx11
#endif // __cplusplus >= 201103L
]])
dnl Tests for new features in C++14
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
// If the compiler admits that it is not ready for C++14, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L
#error "This is not a C++14 compiler"
#else
namespace cxx14
{
namespace test_polymorphic_lambdas
{
int
test()
{
const auto lambda = [](auto&&... args){
const auto istiny = [](auto x){
return (sizeof(x) == 1UL) ? 1 : 0;
};
const int aretiny[] = { istiny(args)... };
return aretiny[0];
};
return lambda(1, 1L, 1.0f, '1');
}
}
namespace test_binary_literals
{
constexpr auto ivii = 0b0000000000101010;
static_assert(ivii == 42, "wrong value");
}
namespace test_generalized_constexpr
{
template < typename CharT >
constexpr unsigned long
strlen_c(const CharT *const s) noexcept
{
auto length = 0UL;
for (auto p = s; *p; ++p)
++length;
return length;
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("x") == 1UL, "");
static_assert(strlen_c("test") == 4UL, "");
static_assert(strlen_c("another\0test") == 7UL, "");
}
namespace test_lambda_init_capture
{
int
test()
{
auto x = 0;
const auto lambda1 = [a = x](int b){ return a + b; };
const auto lambda2 = [a = lambda1(x)](){ return a; };
return lambda2();
}
}
namespace test_digit_separators
{
constexpr auto ten_million = 100'000'000;
static_assert(ten_million == 100000000, "");
}
namespace test_return_type_deduction
{
auto f(int& x) { return x; }
decltype(auto) g(int& x) { return x; }
template < typename T1, typename T2 >
struct is_same
{
static constexpr auto value = false;
};
template < typename T >
struct is_same<T, T>
{
static constexpr auto value = true;
};
int
test()
{
auto x = 0;
static_assert(is_same<int, decltype(f(x))>::value, "");
static_assert(is_same<int&, decltype(g(x))>::value, "");
return x;
}
}
} // namespace cxx14
#endif // __cplusplus >= 201402L
]])
dnl Tests for new features in C++17
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
// If the compiler admits that it is not ready for C++17, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus <= 201402L
#error "This is not a C++17 compiler"
#else
#if defined(__clang__)
#define REALLY_CLANG
#else
#if defined(__GNUC__)
#define REALLY_GCC
#endif
#endif
#include <initializer_list>
#include <utility>
#include <type_traits>
namespace cxx17
{
#if !defined(REALLY_CLANG)
namespace test_constexpr_lambdas
{
// TODO: test it with clang++ from git
constexpr int foo = [](){return 42;}();
}
#endif // !defined(REALLY_CLANG)
namespace test::nested_namespace::definitions
{
}
namespace test_fold_expression
{
template<typename... Args>
int multiply(Args... args)
{
return (args * ... * 1);
}
template<typename... Args>
bool all(Args... args)
{
return (args && ...);
}
}
namespace test_extended_static_assert
{
static_assert (true);
}
namespace test_auto_brace_init_list
{
auto foo = {5};
auto bar {5};
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
static_assert(std::is_same<int, decltype(bar)>::value);
}
namespace test_typename_in_template_template_parameter
{
template<template<typename> typename X> struct D;
}
namespace test_fallthrough_nodiscard_maybe_unused_attributes
{
int f1()
{
return 42;
}
[[nodiscard]] int f2()
{
[[maybe_unused]] auto unused = f1();
switch (f1())
{
case 17:
f1();
[[fallthrough]];
case 42:
f1();
}
return f1();
}
}
namespace test_extended_aggregate_initialization
{
struct base1
{
int b1, b2 = 42;
};
struct base2
{
base2() {
b3 = 42;
}
int b3;
};
struct derived : base1, base2
{
int d;
};
derived d1 {{1, 2}, {}, 4}; // full initialization
derived d2 {{}, {}, 4}; // value-initialized bases
}
namespace test_general_range_based_for_loop
{
struct iter
{
int i;
int& operator* ()
{
return i;
}
const int& operator* () const
{
return i;
}
iter& operator++()
{
++i;
return *this;
}
};
struct sentinel
{
int i;
};
bool operator== (const iter& i, const sentinel& s)
{
return i.i == s.i;
}
bool operator!= (const iter& i, const sentinel& s)
{
return !(i == s);
}
struct range
{
iter begin() const
{
return {0};
}
sentinel end() const
{
return {5};
}
};
void f()
{
range r {};
for (auto i : r)
{
[[maybe_unused]] auto v = i;
}
}
}
namespace test_lambda_capture_asterisk_this_by_value
{
struct t
{
int i;
int foo()
{
return [*this]()
{
return i;
}();
}
};
}
namespace test_enum_class_construction
{
enum class byte : unsigned char
{};
byte foo {42};
}
namespace test_constexpr_if
{
template <bool cond>
int f ()
{
if constexpr(cond)
{
return 13;
}
else
{
return 42;
}
}
}
namespace test_selection_statement_with_initializer
{
int f()
{
return 13;
}
int f2()
{
if (auto i = f(); i > 0)
{
return 3;
}
switch (auto i = f(); i + 4)
{
case 17:
return 2;
default:
return 1;
}
}
}
#if !defined(REALLY_CLANG)
namespace test_template_argument_deduction_for_class_templates
{
// TODO: test it with clang++ from git
template <typename T1, typename T2>
struct pair
{
pair (T1 p1, T2 p2)
: m1 {p1},
m2 {p2}
{}
T1 m1;
T2 m2;
};
void f()
{
[[maybe_unused]] auto p = pair{13, 42u};
}
}
#endif // !defined(REALLY_CLANG)
namespace test_non_type_auto_template_parameters
{
template <auto n>
struct B
{};
B<5> b1;
B<'a'> b2;
}
#if !defined(REALLY_CLANG)
namespace test_structured_bindings
{
// TODO: test it with clang++ from git
int arr[2] = { 1, 2 };
std::pair<int, int> pr = { 1, 2 };
auto f1() -> int(&)[2]
{
return arr;
}
auto f2() -> std::pair<int, int>&
{
return pr;
}
struct S
{
int x1 : 2;
volatile double y1;
};
S f3()
{
return {};
}
auto [ x1, y1 ] = f1();
auto& [ xr1, yr1 ] = f1();
auto [ x2, y2 ] = f2();
auto& [ xr2, yr2 ] = f2();
const auto [ x3, y3 ] = f3();
}
#endif // !defined(REALLY_CLANG)
#if !defined(REALLY_CLANG)
namespace test_exception_spec_type_system
{
// TODO: test it with clang++ from git
struct Good {};
struct Bad {};
void g1() noexcept;
void g2();
template<typename T>
Bad
f(T*, T*);
template<typename T1, typename T2>
Good
f(T1*, T2*);
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
}
#endif // !defined(REALLY_CLANG)
namespace test_inline_variables
{
template<class T> void f(T)
{}
template<class T> inline T g(T)
{
return T{};
}
template<> inline void f<>(int)
{}
template<> int g<>(int)
{
return 5;
}
}
} // namespace cxx17
#endif // __cplusplus <= 201402L
]])

View File

@@ -1,39 +0,0 @@
# =============================================================================
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
# =============================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the C++11
# standard; if necessary, add switches to CXX and CXXCPP to enable
# support.
#
# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
# macro with the version set to C++11. The two optional arguments are
# forwarded literally as the second and third argument respectively.
# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
# more information. If you want to use this macro, you also need to
# download the ax_cxx_compile_stdcxx.m4 file.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 18
AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])

View File

@@ -0,0 +1,79 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_avx_xgetbv.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_GCC_X86_AVX_XGETBV
#
# DESCRIPTION
#
# On later x86 processors with AVX SIMD support, with gcc or a compiler
# that has a compatible syntax for inline assembly instructions, run a
# small program that executes the xgetbv instruction with input OP. This
# can be used to detect if the OS supports AVX instruction usage.
#
# On output, the values of the eax and edx registers are stored as
# hexadecimal strings as "eax:edx" in the cache variable
# ax_cv_gcc_x86_avx_xgetbv.
#
# If the xgetbv instruction fails (because you are running a
# cross-compiler, or because you are not using gcc, or because you are on
# a processor that doesn't have this instruction),
# ax_cv_gcc_x86_avx_xgetbv_OP is set to the string "unknown".
#
# This macro mainly exists to be used in AX_EXT.
#
# LICENSE
#
# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 1
AC_DEFUN([AX_GCC_X86_AVX_XGETBV],
[AC_REQUIRE([AC_PROG_CC])
AC_LANG_PUSH([C])
AC_CACHE_CHECK(for x86-AVX xgetbv $1 output, ax_cv_gcc_x86_avx_xgetbv_$1,
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
int op = $1, eax, edx;
FILE *f;
/* Opcodes for xgetbv */
__asm__(".byte 0x0f, 0x01, 0xd0"
: "=a" (eax), "=d" (edx)
: "c" (op));
f = fopen("conftest_xgetbv", "w"); if (!f) return 1;
fprintf(f, "%x:%x\n", eax, edx);
fclose(f);
return 0;
])],
[ax_cv_gcc_x86_avx_xgetbv_$1=`cat conftest_xgetbv`; rm -f conftest_xgetbv],
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown; rm -f conftest_xgetbv],
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown])])
AC_LANG_POP([C])
])

View File

@@ -0,0 +1,79 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_GCC_X86_CPUID(OP)
#
# DESCRIPTION
#
# On Pentium and later x86 processors, with gcc or a compiler that has a
# compatible syntax for inline assembly instructions, run a small program
# that executes the cpuid instruction with input OP. This can be used to
# detect the CPU type.
#
# On output, the values of the eax, ebx, ecx, and edx registers are stored
# as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable
# ax_cv_gcc_x86_cpuid_OP.
#
# If the cpuid instruction fails (because you are running a
# cross-compiler, or because you are not using gcc, or because you are on
# a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP
# is set to the string "unknown".
#
# This macro mainly exists to be used in AX_GCC_ARCHFLAG.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2008 Matteo Frigo
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 7
AC_DEFUN([AX_GCC_X86_CPUID],
[AC_REQUIRE([AC_PROG_CC])
AC_LANG_PUSH([C])
AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1,
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
int op = $1, eax, ebx, ecx, edx;
FILE *f;
__asm__("cpuid"
: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
: "a" (op));
f = fopen("conftest_cpuid", "w"); if (!f) return 1;
fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx);
fclose(f);
return 0;
])],
[ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid],
[ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid],
[ax_cv_gcc_x86_cpuid_$1=unknown])])
AC_LANG_POP([C])
])

Some files were not shown because too many files have changed in this diff Show More