mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-10-30 03:33:35 +00:00
Compare commits
262 Commits
fairwaves/
...
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d20b7fa579 | ||
|
|
77e18352fb | ||
|
|
f97d75b355 | ||
|
|
a944001873 | ||
|
|
42c165605a | ||
|
|
affd351787 | ||
|
|
03b11620d9 | ||
|
|
25185886f0 | ||
|
|
47031405f5 | ||
|
|
0646b3ce75 | ||
|
|
46cf9efc8e | ||
|
|
871713bf6a | ||
|
|
8d9a05ce5b | ||
|
|
fe865f45d7 | ||
|
|
e5b6664419 | ||
|
|
1595ddaa5f | ||
|
|
441d82add9 | ||
|
|
867cea575b | ||
|
|
2f53ea4cf2 | ||
|
|
138caaf09d | ||
|
|
3fadcad33e | ||
|
|
eaa0144dcb | ||
|
|
f7331764ac | ||
|
|
800c029c70 | ||
|
|
522cbe91d3 | ||
|
|
ed361f9912 | ||
|
|
2d085e290b | ||
|
|
5cea18e5cb | ||
|
|
4adc4eb8d0 | ||
|
|
41c68aa41c | ||
|
|
39e0bbacca | ||
|
|
b4ea7b5211 | ||
|
|
69869bd58f | ||
|
|
ebb37693a5 | ||
|
|
55928f23cb | ||
|
|
0277b58f6d | ||
|
|
be8a83f681 | ||
|
|
d9a460dce9 | ||
|
|
f570b4af62 | ||
|
|
46560ea254 | ||
|
|
038ab45137 | ||
|
|
6b50b62b12 | ||
|
|
3984256273 | ||
|
|
d06172ce32 | ||
|
|
762c951faa | ||
|
|
e47d7e964a | ||
|
|
3cee25ac68 | ||
|
|
0bd0782a39 | ||
|
|
8072650406 | ||
|
|
c6541e6c26 | ||
|
|
1e2c0105e2 | ||
|
|
32b3c2e4b2 | ||
|
|
1c4bbadda6 | ||
|
|
08dfe237c8 | ||
|
|
444ff34396 | ||
|
|
a79bc70737 | ||
|
|
a439fed166 | ||
|
|
8fb0c3dce4 | ||
|
|
f5e1cf8790 | ||
|
|
e7d267f178 | ||
|
|
06f3b622b8 | ||
|
|
74bcc562a9 | ||
|
|
55dd2aa5ab | ||
|
|
5b60c98769 | ||
|
|
207d8a2624 | ||
|
|
92fc186450 | ||
|
|
4d179abfd0 | ||
|
|
6fdfb68b9e | ||
|
|
5ac2cb3662 | ||
|
|
f4ee021b8c | ||
|
|
e62555370e | ||
|
|
1f15152968 | ||
|
|
21ce05c54f | ||
|
|
5e68cde779 | ||
|
|
1f4a009c67 | ||
|
|
544ce0d6b7 | ||
|
|
b148d05542 | ||
|
|
970096932e | ||
|
|
db936b9b55 | ||
|
|
49ad759072 | ||
|
|
8e498bfd35 | ||
|
|
46444637c6 | ||
|
|
86be40b4eb | ||
|
|
288d8af070 | ||
|
|
aae403f0c9 | ||
|
|
5cc8858d8f | ||
|
|
70d0344b31 | ||
|
|
62002fb8ac | ||
|
|
03b3c30533 | ||
|
|
a4b569d936 | ||
|
|
eb0945d85d | ||
|
|
f968507912 | ||
|
|
58c89fb8d6 | ||
|
|
16e7e20f85 | ||
|
|
c7756e73b7 | ||
|
|
7ed686b223 | ||
|
|
2ab6ddb0de | ||
|
|
b229439b31 | ||
|
|
ffb3301bd8 | ||
|
|
62b7900fd7 | ||
|
|
61707e8b23 | ||
|
|
ce70ba529c | ||
|
|
cfb9dac70d | ||
|
|
105a61e689 | ||
|
|
2407314f2e | ||
|
|
ff4418539c | ||
|
|
0494d05916 | ||
|
|
fd268b6f5a | ||
|
|
43f3678b40 | ||
|
|
6493dc838b | ||
|
|
f7905ac548 | ||
|
|
0c27938d44 | ||
|
|
587916e810 | ||
|
|
79024867de | ||
|
|
4aec1f1cf5 | ||
|
|
68f054169b | ||
|
|
ea8be22e50 | ||
|
|
a5054b398b | ||
|
|
a438114173 | ||
|
|
9cb4f27112 | ||
|
|
c38e45e9dc | ||
|
|
1f50fedb5f | ||
|
|
c7a0bf1ffc | ||
|
|
940738e86a | ||
|
|
8c1e2bddff | ||
|
|
01eea0aa42 | ||
|
|
55df1e43e3 | ||
|
|
e9424e241f | ||
|
|
d0ac926b56 | ||
|
|
00d5114717 | ||
|
|
3a496f3b8a | ||
|
|
fad2e09840 | ||
|
|
e09e80f5ee | ||
|
|
2e276e7edd | ||
|
|
dffc21725c | ||
|
|
96f0f2cf7e | ||
|
|
225b16d48e | ||
|
|
0ebbb2ed2e | ||
|
|
2fea950644 | ||
|
|
d0a97a5f73 | ||
|
|
295b938d51 | ||
|
|
e8605202ab | ||
|
|
2a8183bdf0 | ||
|
|
478f82f47e | ||
|
|
f37b0ad652 | ||
|
|
3b78cbfdc1 | ||
|
|
f3d7f443a0 | ||
|
|
e564f0fd84 | ||
|
|
0fc20d14b3 | ||
|
|
a4316ee4c5 | ||
|
|
2128a308eb | ||
|
|
43fedb656b | ||
|
|
53bdb7f82a | ||
|
|
6462dd3963 | ||
|
|
e1977fcd22 | ||
|
|
f97296e0ce | ||
|
|
20259cb307 | ||
|
|
ffa4e5938c | ||
|
|
c0c6d70fe9 | ||
|
|
8c6c5d2bcd | ||
|
|
a62fcf786a | ||
|
|
4d9b59c3ef | ||
|
|
bd0efb0bea | ||
|
|
8fbbd656c7 | ||
|
|
b35cba613a | ||
|
|
8dffadb8da | ||
|
|
408f25081e | ||
|
|
2001550f7d | ||
|
|
a3ab8c263d | ||
|
|
efac20b6bb | ||
|
|
0bbd8922ea | ||
|
|
28b8cc6283 | ||
|
|
3f52f0e6c5 | ||
|
|
3da1f8352e | ||
|
|
5ea1817dc2 | ||
|
|
49d42e979e | ||
|
|
3a3b220751 | ||
|
|
ab22f4c421 | ||
|
|
8b843e5bed | ||
|
|
c92dad32dd | ||
|
|
61837c0420 | ||
|
|
f83e11fefd | ||
|
|
01aff88ce9 | ||
|
|
11d50d950c | ||
|
|
8bd111c942 | ||
|
|
3808e479aa | ||
|
|
bd45a979f8 | ||
|
|
b7095c7bc5 | ||
|
|
77ce99ac67 | ||
|
|
f58cd8ac83 | ||
|
|
99eb07e232 | ||
|
|
89be118a3b | ||
|
|
6fafd33b13 | ||
|
|
6e55d51747 | ||
|
|
6cae1d7b4b | ||
|
|
28ce315a32 | ||
|
|
10d76b6863 | ||
|
|
708b8b44ae | ||
|
|
cb0fc9b21a | ||
|
|
8639fee504 | ||
|
|
ca46896cfe | ||
|
|
4a25d6b8f6 | ||
|
|
79baee3a8f | ||
|
|
c2ba427b52 | ||
|
|
611212676b | ||
|
|
4ebb289c90 | ||
|
|
2f376a3edf | ||
|
|
2edbe4d366 | ||
|
|
a3694bd303 | ||
|
|
2652f2bc39 | ||
|
|
93d9b114b7 | ||
|
|
2ac788b2c3 | ||
|
|
d36ef2f57b | ||
|
|
caf2abc58f | ||
|
|
de1685f6d7 | ||
|
|
f3837d26f9 | ||
|
|
ddf4743306 | ||
|
|
82f83ced73 | ||
|
|
cff4ed9b4c | ||
|
|
6ec26bb788 | ||
|
|
a1ff991402 | ||
|
|
d09843c692 | ||
|
|
e5448ff972 | ||
|
|
e48c1367dc | ||
|
|
aa60dda99a | ||
|
|
1468a5c3dc | ||
|
|
b0e1bd8c22 | ||
|
|
78e1cd20e2 | ||
|
|
db9c1b54cb | ||
|
|
099a44abfb | ||
|
|
8c80095017 | ||
|
|
d49a6aa136 | ||
|
|
81486e053c | ||
|
|
28d8081e25 | ||
|
|
87ed77b937 | ||
|
|
f9d996813d | ||
|
|
aa5acc953c | ||
|
|
934da48618 | ||
|
|
7c405a0c1f | ||
|
|
4cafb0fa15 | ||
|
|
f611569018 | ||
|
|
354741326c | ||
|
|
d2e5c5694e | ||
|
|
a3dce85ffc | ||
|
|
bb0c68ae61 | ||
|
|
87d158cc2d | ||
|
|
7278a87767 | ||
|
|
63eef9faf2 | ||
|
|
d67bd603e9 | ||
|
|
988a464d5d | ||
|
|
1b6ab7d7ee | ||
|
|
980525c8a9 | ||
|
|
70134a01eb | ||
|
|
1fb0ce67d8 | ||
|
|
8ca237b5c2 | ||
|
|
082bbbf8fe | ||
|
|
3bd763d2a1 | ||
|
|
ee57357682 | ||
|
|
8537b90dbe | ||
|
|
038fd7fd70 | ||
|
|
0cd246c27a | ||
|
|
61fbf2ec95 |
45
.gitignore
vendored
45
.gitignore
vendored
@@ -2,22 +2,23 @@
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
Transceiver52M/osmo-trx
|
||||
Transceiver52M/osmo-trx-gen
|
||||
Transceiver52M/osmo-trx-dec
|
||||
Transceiver52M/osmo-trx-uhd
|
||||
Transceiver52M/osmo-trx-usrp1
|
||||
Transceiver52M/osmo-trx-lms
|
||||
|
||||
# tests
|
||||
CommonLibs/BitVectorTest
|
||||
CommonLibs/ConfigurationTest
|
||||
CommonLibs/F16Test
|
||||
CommonLibs/InterthreadTest
|
||||
CommonLibs/LogTest
|
||||
CommonLibs/RegexpTest
|
||||
CommonLibs/SocketsTest
|
||||
CommonLibs/TimevalTest
|
||||
CommonLibs/URLEncodeTest
|
||||
CommonLibs/VectorTest
|
||||
CommonLibs/PRBSTest
|
||||
tests/CommonLibs/BitVectorTest
|
||||
tests/CommonLibs/F16Test
|
||||
tests/CommonLibs/InterthreadTest
|
||||
tests/CommonLibs/LogTest
|
||||
tests/CommonLibs/RegexpTest
|
||||
tests/CommonLibs/SocketsTest
|
||||
tests/CommonLibs/TimevalTest
|
||||
tests/CommonLibs/URLEncodeTest
|
||||
tests/CommonLibs/VectorTest
|
||||
tests/CommonLibs/PRBSTest
|
||||
tests/Transceiver52M/convolve_test
|
||||
tests/Transceiver52M/LMSDeviceTest
|
||||
|
||||
# automake/autoconf
|
||||
*.in
|
||||
@@ -43,6 +44,22 @@ 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
83
AUTHORS
@@ -1,18 +1,18 @@
|
||||
#
|
||||
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
#
|
||||
#
|
||||
# This file is part of GNU Radio
|
||||
#
|
||||
#
|
||||
# GNU Radio is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
#
|
||||
# GNU Radio is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
@@ -23,34 +23,17 @@ David A. Burgess, dburgess@kestrelsp.com:
|
||||
CLI/CLI.h
|
||||
CommonLibs/Assert.h
|
||||
CommonLibs/BitVector.cpp
|
||||
CommonLibs/BitVectorTest.cpp
|
||||
CommonLibs/Configuration.cpp
|
||||
CommonLibs/Configuration.h
|
||||
CommonLibs/ConfigurationTest.cpp
|
||||
CommonLibs/Interthread.h
|
||||
CommonLibs/InterthreadTest.cpp
|
||||
CommonLibs/LinkedLists.cpp
|
||||
CommonLibs/LinkedLists.h
|
||||
CommonLibs/Regexp.h
|
||||
CommonLibs/RegexpTest.cpp
|
||||
CommonLibs/Sockets.cpp
|
||||
CommonLibs/Sockets.h
|
||||
CommonLibs/SocketsTest.cpp
|
||||
CommonLibs/Threads.cpp
|
||||
CommonLibs/Threads.h
|
||||
CommonLibs/Timeval.cpp
|
||||
CommonLibs/Timeval.h
|
||||
CommonLibs/TimevalTest.cpp
|
||||
CommonLibs/Vector.h
|
||||
CommonLibs/VectorTest.cpp
|
||||
Control/CallControl.cpp
|
||||
Control/ControlCommon.cpp
|
||||
Control/ControlCommon.h
|
||||
Control/FACCHDispatch.cpp
|
||||
Control/MobilityManagement.cpp
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
Control/SDCCHDispatch.cpp
|
||||
GSM/GSM610Tables.cpp
|
||||
GSM/GSM610Tables.h
|
||||
GSM/GSMCommon.cpp
|
||||
@@ -82,29 +65,15 @@ David A. Burgess, dburgess@kestrelsp.com:
|
||||
GSM/GSMTransfer.cpp
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
SIP/SIPEngine.h
|
||||
SIP/SIPInterface.h
|
||||
SMS/SMSMessages.cpp
|
||||
SMS/SMSMessages.h
|
||||
SMS/SMSTransfer.cpp
|
||||
SMS/SMSTransfer.h
|
||||
TRXManager/TRXManager.cpp
|
||||
Transceiver/Complex.h
|
||||
apps/OpenBTS900.cpp
|
||||
apps/OpenBTS850.cpp
|
||||
apps/OpenBTS25c3.cpp
|
||||
tests/AGCHTest.cpp
|
||||
tests/BeaconTest.cpp
|
||||
tests/CallTest.cpp
|
||||
tests/CallTest2.cpp
|
||||
tests/LAPDmTest.cpp
|
||||
tests/LoopbackTest.cpp
|
||||
tests/RegistrationTest.cpp
|
||||
tests/TRXSimulator.cpp
|
||||
tests/CommonLibs/BitVectorTest.cpp
|
||||
tests/CommonLibs/InterthreadTest.cpp
|
||||
tests/CommonLibs/SocketsTest.cpp
|
||||
tests/CommonLibs/TimevalTest.cpp
|
||||
tests/CommonLibs/VectorTest.cpp
|
||||
|
||||
Harvind S. Samra, hssamra@kestrelsp.com:
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
@@ -126,13 +95,6 @@ Harvind S. Samra, hssamra@kestrelsp.com:
|
||||
Transceiver/testRadio.cpp
|
||||
|
||||
Raffi Sevlian, raffisev@gmail.com:
|
||||
Control/CallControl.cpp
|
||||
Control/ControlCommon.cpp
|
||||
Control/ControlCommon.h
|
||||
Control/FACCHDispatch.cpp
|
||||
Control/MobilityManagement.cpp
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
GSM/GSMCommon.h
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSML1FEC.h
|
||||
@@ -157,36 +119,9 @@ Raffi Sevlian, raffisev@gmail.com:
|
||||
GSM/GSMSAPMux.h
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
SIP/SIPEngine.cpp
|
||||
SIP/SIPInterface.cpp
|
||||
SIP/SIPInterface.h
|
||||
SIP/SIPMessage.cpp
|
||||
SIP/SIPMessage.h
|
||||
SIP/SIPUtility.cpp
|
||||
SIP/SIPUtility.h
|
||||
SMS/CMMessage.cpp
|
||||
SMS/CMMessage.h
|
||||
SMS/CMProcessor.cpp
|
||||
SMS/CMProcessor.h
|
||||
SMS/CMTest.cpp
|
||||
SMS/RLMessage.cpp
|
||||
SMS/RLMessage.h
|
||||
SMS/RLProcessor.cpp
|
||||
SMS/RLProcessor.h
|
||||
SMS/SMSMessages.cpp
|
||||
SMS/SMSMessages.h
|
||||
SMS/SMSProcessors.cpp
|
||||
SMS/SMSProcessors.h
|
||||
SMS/SMSTransfer.cpp
|
||||
SMS/SMSTransfer.h
|
||||
SMS/TLMessage.cpp
|
||||
SMS/TLMessage.h
|
||||
SMS/TLProcessor.cpp
|
||||
SMS/TLProcessor.h
|
||||
TRXManager/TRXManager.h
|
||||
|
||||
Alon Levy, alonlevy1@gmail.com
|
||||
RRLPMessages.cpp
|
||||
RRLPMessages.h
|
||||
RRLPTest.cpp
|
||||
|
||||
|
||||
28
COPYING
28
COPYING
@@ -673,16 +673,16 @@ on the AGPLv3 text.
|
||||
=========================================================================
|
||||
|
||||
|
||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS
|
||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX
|
||||
|
||||
|
||||
Permissive Terms Supplementing the License
|
||||
|
||||
1. Remote Interaction Through IP Networks.
|
||||
|
||||
OpenBTS includes an implementation of the GSM network cellular air interface,
|
||||
OsmoTRX is an implementation of the GSM network cellular air interface,
|
||||
as well as other interfaces to IP networks. The interaction of cellular
|
||||
handsets with the OpenBTS software is considered "remote network interaction"
|
||||
handsets with the OsmoTRX software is considered "remote network interaction"
|
||||
for the purposes of the Affero General Public License and cellular users are
|
||||
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
|
||||
Network Interaction; Use with the GNU General Public License").
|
||||
@@ -694,17 +694,6 @@ interfaces other than the GSM air interface from the requirements of Section 13
|
||||
is an additional permission granted to you.
|
||||
|
||||
|
||||
Non-Permissive Terms Supplementing The License
|
||||
|
||||
1. Trademarks.
|
||||
|
||||
"OpenBTS" is a trademark of Range Networks, Inc., registered with
|
||||
the US Patent and Trademark Office. Your use of OpenBTS software under a GPL
|
||||
license does not include the right to use the OpenBTS trademark in commerce.
|
||||
This additional non-permissive term is consistent with Section 7 of the AGPLv3
|
||||
license.
|
||||
|
||||
|
||||
END OF ADDITIONAL TERMS
|
||||
|
||||
|
||||
@@ -712,13 +701,8 @@ END OF ADDITIONAL TERMS
|
||||
How to comply with Section 13 of the AGPLv3 license.
|
||||
|
||||
The recommended method for compliance with Section 13 of the AGPLv3 license is
|
||||
to deliver a text message to each handset that attaches to the OpenBTS cellular
|
||||
network. At a minimum, that text message should include the string "OpenBTS
|
||||
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
|
||||
to deliver a text message to each handset that attaches to the cellular
|
||||
network which uses OsmoTRX. At a minimum, that text message should include the string
|
||||
"OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This
|
||||
message need not be delivered to handsets that are denied registration with the
|
||||
network, since those handsets have been denied service.
|
||||
|
||||
In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message"
|
||||
feature. See the OpenBTS.config.example file for more information on the use of
|
||||
this feature for AGPLv3 compliance.
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011, 2012 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONFIGURATION_H
|
||||
#define CONFIGURATION_H
|
||||
|
||||
|
||||
#include "sqlite3util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <Threads.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/** A class for configuration file errors. */
|
||||
class ConfigurationTableError {};
|
||||
extern char gCmdName[]; // Gotta be global, gotta be char*, gotta love it.
|
||||
|
||||
/** An exception thrown when a given config key isn't found. */
|
||||
class ConfigurationTableKeyNotFound : public ConfigurationTableError {
|
||||
|
||||
private:
|
||||
|
||||
std::string mKey;
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationTableKeyNotFound(const std::string& wKey)
|
||||
:mKey(wKey)
|
||||
{ }
|
||||
|
||||
const std::string& key() const { return mKey; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ConfigurationRecord {
|
||||
|
||||
private:
|
||||
|
||||
std::string mValue;
|
||||
long mNumber;
|
||||
bool mDefined;
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationRecord(bool wDefined=true):
|
||||
mDefined(wDefined)
|
||||
{ }
|
||||
|
||||
ConfigurationRecord(const std::string& wValue):
|
||||
mValue(wValue),
|
||||
mNumber(strtol(wValue.c_str(),NULL,0)),
|
||||
mDefined(true)
|
||||
{ }
|
||||
|
||||
ConfigurationRecord(const char* wValue):
|
||||
mValue(std::string(wValue)),
|
||||
mNumber(strtol(wValue,NULL,0)),
|
||||
mDefined(true)
|
||||
{ }
|
||||
|
||||
|
||||
const std::string& value() const { return mValue; }
|
||||
long number() const { return mNumber; }
|
||||
bool defined() const { return mDefined; }
|
||||
|
||||
float floatNumber() const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** A string class that uses a hash function for comparison. */
|
||||
class HashString : public std::string {
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
uint64_t mHash;
|
||||
|
||||
void computeHash();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
HashString(const char* src)
|
||||
:std::string(src)
|
||||
{
|
||||
computeHash();
|
||||
}
|
||||
|
||||
HashString(const std::string& src)
|
||||
:std::string(src)
|
||||
{
|
||||
computeHash();
|
||||
}
|
||||
|
||||
HashString()
|
||||
{
|
||||
mHash=0;
|
||||
}
|
||||
|
||||
HashString& operator=(std::string& src)
|
||||
{
|
||||
std::string::operator=(src);
|
||||
computeHash();
|
||||
return *this;
|
||||
}
|
||||
|
||||
HashString& operator=(const char* src)
|
||||
{
|
||||
std::string::operator=(src);
|
||||
computeHash();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const HashString& other)
|
||||
{
|
||||
return mHash==other.mHash;
|
||||
}
|
||||
|
||||
bool operator<(const HashString& other)
|
||||
{
|
||||
return mHash<other.mHash;
|
||||
}
|
||||
|
||||
bool operator>(const HashString& other)
|
||||
{
|
||||
return mHash<other.mHash;
|
||||
}
|
||||
|
||||
uint64_t hash() const { return mHash; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap;
|
||||
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
|
||||
class ConfigurationKey;
|
||||
typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap;
|
||||
|
||||
/**
|
||||
A class for maintaining a configuration key-value table,
|
||||
based on sqlite3 and a local map-based cache.
|
||||
Thread-safe, too.
|
||||
*/
|
||||
class ConfigurationTable {
|
||||
|
||||
private:
|
||||
|
||||
sqlite3* mDB; ///< database connection
|
||||
ConfigurationMap mCache; ///< cache of recently access configuration values
|
||||
mutable Mutex mLock; ///< control for multithreaded access to the cache
|
||||
std::vector<std::string> (*mCrossCheck)(const std::string&); ///< cross check callback pointer
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic
|
||||
|
||||
ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap());
|
||||
|
||||
/** Generate an up-to-date example sql file for new installs. */
|
||||
std::string getDefaultSQL(const std::string& program, const std::string& version);
|
||||
|
||||
/** Generate an up-to-date TeX snippet. */
|
||||
std::string getTeX(const std::string& program, const std::string& version);
|
||||
|
||||
/** Return true if the key is used in the table. */
|
||||
bool defines(const std::string& key);
|
||||
|
||||
/** Return true if the application's schema knows about this key. */
|
||||
bool keyDefinedInSchema(const std::string& name);
|
||||
|
||||
/** Return true if the provided value validates correctly against the defined schema. */
|
||||
bool isValidValue(const std::string& name, const std::string& val);
|
||||
|
||||
/** Return true if the provided value validates correctly against the defined schema. */
|
||||
bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); }
|
||||
|
||||
/** Return a map of all similar keys in the defined schema. */
|
||||
ConfigurationKeyMap getSimilarKeys(const std::string& snippet);
|
||||
|
||||
/** Return true if this key is identified as static. */
|
||||
bool isStatic(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a string parameter from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
std::string getStr(const std::string& key);
|
||||
|
||||
|
||||
/**
|
||||
Get a boolean from the table.
|
||||
Return false if NULL or 0, true otherwise.
|
||||
*/
|
||||
bool getBool(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a numeric parameter from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
long getNum(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a vector of strings from the table.
|
||||
*/
|
||||
std::vector<std::string> getVectorOfStrings(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a float from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
float getFloat(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a numeric vector from the table.
|
||||
*/
|
||||
std::vector<unsigned> getVector(const std::string& key);
|
||||
|
||||
/** Get length of a vector */
|
||||
unsigned getVectorLength(const std::string &key)
|
||||
{ return getVector(key).size(); }
|
||||
|
||||
/** Set or change a value in the table. */
|
||||
bool set(const std::string& key, const std::string& value);
|
||||
|
||||
/** Set or change a value in the table. */
|
||||
bool set(const std::string& key, long value);
|
||||
|
||||
/** Create an entry in the table, no value though. */
|
||||
bool set(const std::string& key);
|
||||
|
||||
/**
|
||||
Remove an entry from the table.
|
||||
Will not alter required values.
|
||||
@param key The key of the item to be removed.
|
||||
@return true if anything was actually removed.
|
||||
*/
|
||||
bool remove(const std::string& key);
|
||||
|
||||
/** Search the table, dumping to a stream. */
|
||||
void find(const std::string& pattern, std::ostream&) const;
|
||||
|
||||
/** Return all key/value pairs stored in the ConfigurationTable */
|
||||
ConfigurationRecordMap getAllPairs() const;
|
||||
|
||||
/** Define the callback to purge the cache whenever the database changes. */
|
||||
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
|
||||
|
||||
/** Define the callback for cross checking. */
|
||||
void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&));
|
||||
|
||||
/** Execute the application specific value cross checking logic. */
|
||||
std::vector<std::string> crossCheck(const std::string& key);
|
||||
|
||||
/** purege cache if it exceeds a certain age */
|
||||
void checkCacheAge();
|
||||
|
||||
/** Delete all records from the cache. */
|
||||
void purge();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
Attempt to lookup a record, cache if needed.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
Caller should hold mLock because the returned reference points into the cache.
|
||||
*/
|
||||
const ConfigurationRecord& lookup(const std::string& key);
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<HashString, std::string> HashStringMap;
|
||||
|
||||
class SimpleKeyValue {
|
||||
|
||||
protected:
|
||||
|
||||
HashStringMap mMap;
|
||||
|
||||
public:
|
||||
|
||||
/** Take a C string "A=B" and set map["A"]="B". */
|
||||
void addItem(const char*);
|
||||
|
||||
/** Take a C string "A=B C=D E=F ..." and add all of the pairs to the map. */
|
||||
void addItems(const char*s);
|
||||
|
||||
/** Return a reference to the string at map["key"]. */
|
||||
const char* get(const char*) const;
|
||||
};
|
||||
|
||||
|
||||
class ConfigurationKey {
|
||||
|
||||
public:
|
||||
|
||||
enum VisibilityLevel
|
||||
{
|
||||
CUSTOMER,
|
||||
CUSTOMERSITE,
|
||||
CUSTOMERTUNE,
|
||||
CUSTOMERWARN,
|
||||
DEVELOPER,
|
||||
FACTORY
|
||||
};
|
||||
|
||||
enum Type
|
||||
{
|
||||
BOOLEAN,
|
||||
CHOICE_OPT,
|
||||
CHOICE,
|
||||
CIDR_OPT,
|
||||
CIDR,
|
||||
FILEPATH_OPT,
|
||||
FILEPATH,
|
||||
IPADDRESS_OPT,
|
||||
IPADDRESS,
|
||||
IPANDPORT,
|
||||
MIPADDRESS_OPT,
|
||||
MIPADDRESS,
|
||||
PORT_OPT,
|
||||
PORT,
|
||||
REGEX_OPT,
|
||||
REGEX,
|
||||
STRING_OPT,
|
||||
STRING,
|
||||
VALRANGE
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
std::string mName;
|
||||
std::string mDefaultValue;
|
||||
std::string mUnits;
|
||||
VisibilityLevel mVisibility;
|
||||
Type mType;
|
||||
std::string mValidValues;
|
||||
bool mIsStatic;
|
||||
std::string mDescription;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription):
|
||||
mName(wName),
|
||||
mDefaultValue(wDefaultValue),
|
||||
mUnits(wUnits),
|
||||
mVisibility(wVisibility),
|
||||
mType(wType),
|
||||
mValidValues(wValidValues),
|
||||
mIsStatic(wIsStatic),
|
||||
mDescription(wDescription)
|
||||
{ }
|
||||
|
||||
ConfigurationKey()
|
||||
{ }
|
||||
|
||||
const std::string& getName() const { return mName; }
|
||||
const std::string& getDefaultValue() const { return mDefaultValue; }
|
||||
void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; }
|
||||
void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); }
|
||||
const std::string& getUnits() const { return mUnits; }
|
||||
const VisibilityLevel& getVisibility() const { return mVisibility; }
|
||||
const Type& getType() const { return mType; }
|
||||
const std::string& getValidValues() const { return mValidValues; }
|
||||
bool isStatic() const { return mIsStatic; }
|
||||
const std::string& getDescription() const { return mDescription; }
|
||||
|
||||
static bool isValidIP(const std::string& ip);
|
||||
static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping);
|
||||
template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger);
|
||||
static const std::string visibilityLevelToString(const VisibilityLevel& visibility);
|
||||
static const std::string typeToString(const ConfigurationKey::Type& type);
|
||||
static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os);
|
||||
static void printDescription(const ConfigurationKey &key, std::ostream& os);
|
||||
static const std::string getARFCNsString();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Configuration.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ConfigurationKeyMap getConfigurationKeys();
|
||||
ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys());
|
||||
|
||||
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
|
||||
{
|
||||
//cout << "update hook" << endl;
|
||||
gConfig.purge();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
gConfig.setUpdateHook(purgeConfig);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -29,6 +29,25 @@
|
||||
#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.
|
||||
|
||||
@@ -70,6 +70,7 @@ 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.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011, 2012 Range Networks, Inc.
|
||||
* Copyright (C) 2018 sysmocom - s.f.m.c. GmbH
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
@@ -32,300 +30,35 @@
|
||||
#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()
|
||||
{
|
||||
if (mDummyInit) return;
|
||||
// Anything at or above LOG_CRIT is an "alarm".
|
||||
// Save alarms in the local list and echo them to stderr.
|
||||
if (mPriority <= LOG_ERR) {
|
||||
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||
cerr << mStream.str() << endl;
|
||||
}
|
||||
// Current logging level was already checked by the macro. So just log.
|
||||
// Log to syslog
|
||||
if (gLogToSyslog) {
|
||||
syslog(mPriority, "%s", mStream.str().c_str());
|
||||
}
|
||||
// Log to file and console
|
||||
if (gLogToConsole||gLogToFile) {
|
||||
int mlen = mStream.str().size();
|
||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||
ScopedLock lock(gLogToLock);
|
||||
if (gLogToConsole) {
|
||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||
// so just use std::cout.
|
||||
std::cout << mStream.str();
|
||||
if (neednl) std::cout<<"\n";
|
||||
}
|
||||
if (gLogToFile) {
|
||||
fputs(mStream.str().c_str(),gLogToFile);
|
||||
if (neednl) {fputc('\n',gLogToFile);}
|
||||
fflush(gLogToFile);
|
||||
}
|
||||
}
|
||||
int 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);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -23,71 +23,41 @@
|
||||
|
||||
*/
|
||||
|
||||
// (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>
|
||||
|
||||
#define _LOG(level) \
|
||||
Log(LOG_##level).get() << pthread_self() \
|
||||
<< timestr() << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
|
||||
extern "C" {
|
||||
#include <osmocom/core/logging.h>
|
||||
#include "debug.h"
|
||||
}
|
||||
|
||||
#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)
|
||||
/* 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
|
||||
#endif
|
||||
|
||||
// pat: And for your edification here are the 'levels' as defined in syslog.h:
|
||||
// LOG_EMERG 0 system is unusable
|
||||
// LOG_ALERT 1 action must be taken immediately
|
||||
// LOG_CRIT 2 critical conditions
|
||||
// LOG_ERR 3 error conditions
|
||||
// LOG_WARNING 4 warning conditions
|
||||
// LOG_NOTICE 5 normal, but significant, condition
|
||||
// LOG_INFO 6 informational message
|
||||
// LOG_DEBUG 7 debug-level message
|
||||
#define LOG(level) \
|
||||
Log(DMAIN, 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 LOGC(category, level) \
|
||||
Log(category, LOGL_##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()
|
||||
#define LOGLV(category, level) \
|
||||
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
@@ -97,45 +67,28 @@ class Log {
|
||||
|
||||
protected:
|
||||
|
||||
std::ostringstream mStream; ///< This is where we buffer up the log entry.
|
||||
int mPriority; ///< Priority of current report.
|
||||
bool mDummyInit;
|
||||
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.
|
||||
|
||||
public:
|
||||
|
||||
Log(int wPriority)
|
||||
:mPriority(wPriority), mDummyInit(false)
|
||||
Log(int wCategory, int wPriority, const char* filename, int line)
|
||||
: mCategory(wCategory), mPriority(wPriority),
|
||||
filename(filename), line(line)
|
||||
{ }
|
||||
|
||||
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
|
||||
|
||||
@@ -22,11 +22,8 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
|
||||
|
||||
EXTRA_DIST = \
|
||||
example.config \
|
||||
README.common
|
||||
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
|
||||
noinst_LTLIBRARIES = libcommon.la
|
||||
|
||||
@@ -37,20 +34,10 @@ libcommon_la_SOURCES = \
|
||||
Threads.cpp \
|
||||
Timeval.cpp \
|
||||
Logger.cpp \
|
||||
Configuration.cpp \
|
||||
sqlite3util.cpp
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
BitVectorTest \
|
||||
PRBSTest \
|
||||
InterthreadTest \
|
||||
SocketsTest \
|
||||
TimevalTest \
|
||||
VectorTest \
|
||||
ConfigurationTest \
|
||||
LogTest
|
||||
|
||||
# ReportingTest
|
||||
Utils.cpp \
|
||||
trx_vty.c \
|
||||
debug.c
|
||||
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
|
||||
|
||||
noinst_HEADERS = \
|
||||
BitVector.h \
|
||||
@@ -61,38 +48,9 @@ noinst_HEADERS = \
|
||||
Threads.h \
|
||||
Timeval.h \
|
||||
Vector.h \
|
||||
Configuration.h \
|
||||
Logger.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
|
||||
|
||||
|
||||
Utils.h \
|
||||
trx_vty.h \
|
||||
debug.h \
|
||||
osmo_signal.h \
|
||||
config_defs.h
|
||||
|
||||
@@ -223,18 +223,18 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
||||
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort)
|
||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort);
|
||||
open(wSrcPort, wSrcIP);
|
||||
}
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort,
|
||||
const char * wDestIP, unsigned short wDestPort )
|
||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
|
||||
const char *wDestIP, unsigned short wDestPort)
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort);
|
||||
open(wSrcPort, wSrcIP);
|
||||
destination(wDestPort, wDestIP);
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
|
||||
}
|
||||
|
||||
|
||||
void UDPSocket::open(unsigned short localPort)
|
||||
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
||||
@@ -265,7 +265,7 @@ void UDPSocket::open(unsigned short localPort)
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_addr.s_addr = inet_addr(wlocalIP);
|
||||
address.sin_port = htons(localPort);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
@@ -284,50 +284,4 @@ unsigned short UDPSocket::port() const
|
||||
return ntohs(name.sin_port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UDDSocket::UDDSocket(const char* localPath, const char* remotePath)
|
||||
:DatagramSocket()
|
||||
{
|
||||
if (localPath!=NULL) open(localPath);
|
||||
if (remotePath!=NULL) destination(remotePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDDSocket::open(const char* localPath)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0);
|
||||
if (mSocketFD<0) {
|
||||
perror("socket() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
|
||||
// bind
|
||||
struct sockaddr_un address;
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sun_family = AF_UNIX;
|
||||
strcpy(address.sun_path,localPath);
|
||||
unlink(localPath);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDDSocket::destination(const char* remotePath)
|
||||
{
|
||||
struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination;
|
||||
strcpy(unAddr->sun_path,remotePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim:ts=4:sw=4
|
||||
|
||||
@@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket {
|
||||
public:
|
||||
|
||||
/** Open a USP socket with an OS-assigned port and no default destination. */
|
||||
UDPSocket( unsigned short localPort=0);
|
||||
UDPSocket(const char *localIP, unsigned short localPort);
|
||||
|
||||
/** Given a full specification, open the socket and set the dest address. */
|
||||
UDPSocket( unsigned short localPort,
|
||||
const char * remoteIP, unsigned short remotePort);
|
||||
UDPSocket(const char *localIP, unsigned short localPort,
|
||||
const char *remoteIP, unsigned short remotePort);
|
||||
|
||||
/** Set the destination port. */
|
||||
void destination( unsigned short wDestPort, const char * wDestIP );
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
unsigned short port() const;
|
||||
|
||||
/** Open and bind the UDP socket to a local port. */
|
||||
void open(unsigned short localPort=0);
|
||||
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
||||
@@ -166,26 +166,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** Unix Domain Datagram Socket */
|
||||
class UDDSocket : public DatagramSocket {
|
||||
|
||||
public:
|
||||
|
||||
UDDSocket(const char* localPath=NULL, const char* remotePath=NULL);
|
||||
|
||||
void destination(const char* remotePath);
|
||||
|
||||
void open(const char* localPath);
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; }
|
||||
|
||||
size_t addressSize() const { return sizeof(struct sockaddr_un); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -24,11 +24,17 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#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;
|
||||
@@ -102,6 +108,19 @@ 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)
|
||||
{
|
||||
|
||||
@@ -141,6 +141,8 @@ 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 {
|
||||
|
||||
|
||||
@@ -27,43 +27,49 @@
|
||||
|
||||
#include "Timeval.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/timer.h>
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Timeval::now()
|
||||
{
|
||||
osmo_clock_gettime(CLOCK_REALTIME, &mTimespec);
|
||||
}
|
||||
|
||||
void Timeval::future(unsigned offset)
|
||||
{
|
||||
now();
|
||||
unsigned sec = offset/1000;
|
||||
unsigned msec = offset%1000;
|
||||
mTimeval.tv_usec += msec*1000;
|
||||
mTimeval.tv_sec += sec;
|
||||
if (mTimeval.tv_usec>1000000) {
|
||||
mTimeval.tv_usec -= 1000000;
|
||||
mTimeval.tv_sec += 1;
|
||||
mTimespec.tv_nsec += msec*1000*1000;
|
||||
mTimespec.tv_sec += sec;
|
||||
if (mTimespec.tv_nsec > 1000*1000*1000) {
|
||||
mTimespec.tv_nsec -= 1000*1000*1000;
|
||||
mTimespec.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct timespec Timeval::timespec() const
|
||||
{
|
||||
struct timespec retVal;
|
||||
retVal.tv_sec = mTimeval.tv_sec;
|
||||
retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
|
||||
return retVal;
|
||||
return mTimespec;
|
||||
}
|
||||
|
||||
|
||||
bool Timeval::passed() const
|
||||
{
|
||||
Timeval nowTime;
|
||||
if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
|
||||
if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
|
||||
if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
|
||||
if (nowTime.mTimespec.tv_sec < mTimespec.tv_sec) return false;
|
||||
if (nowTime.mTimespec.tv_sec > mTimespec.tv_sec) return true;
|
||||
if (nowTime.mTimespec.tv_nsec >= mTimespec.tv_nsec) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
double Timeval::seconds() const
|
||||
{
|
||||
return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
|
||||
return ((double)mTimespec.tv_sec) + 1e-9*((double)mTimespec.tv_nsec);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +78,8 @@ long Timeval::delta(const Timeval& other) const
|
||||
{
|
||||
// 2^31 milliseconds is just over 4 years.
|
||||
int32_t deltaS = other.sec() - sec();
|
||||
int32_t deltaUs = other.usec() - usec();
|
||||
return 1000*deltaS + deltaUs/1000;
|
||||
int32_t deltaNs = other.nsec() - nsec();
|
||||
return 1000*deltaS + deltaNs/1000000;
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +95,7 @@ ostream& operator<<(ostream& os, const Timeval& tv)
|
||||
|
||||
ostream& operator<<(ostream& os, const struct timespec& ts)
|
||||
{
|
||||
os << ts.tv_sec << "," << ts.tv_nsec;
|
||||
os << ts.tv_sec << "," << ts.tv_nsec/1000;
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,12 +42,12 @@ class Timeval {
|
||||
|
||||
private:
|
||||
|
||||
struct timeval mTimeval;
|
||||
struct timespec mTimespec;
|
||||
|
||||
public:
|
||||
|
||||
/** Set the value to gettimeofday. */
|
||||
void now() { gettimeofday(&mTimeval,NULL); }
|
||||
/** Set the value to current time. */
|
||||
void now();
|
||||
|
||||
/** Set the value to gettimeofday plus an offset. */
|
||||
void future(unsigned ms);
|
||||
@@ -55,16 +55,18 @@ class Timeval {
|
||||
//@{
|
||||
Timeval(unsigned sec, unsigned usec)
|
||||
{
|
||||
mTimeval.tv_sec = sec;
|
||||
mTimeval.tv_usec = usec;
|
||||
mTimespec.tv_sec = sec;
|
||||
mTimespec.tv_nsec = usec*1000;
|
||||
}
|
||||
|
||||
Timeval(const struct timeval& wTimeval)
|
||||
:mTimeval(wTimeval)
|
||||
{}
|
||||
{
|
||||
mTimespec.tv_sec = wTimeval.tv_sec;
|
||||
mTimespec.tv_nsec = wTimeval.tv_sec*1000;
|
||||
}
|
||||
|
||||
/**
|
||||
Create a Timeval offset into the future.
|
||||
Create a Timespec offset into the future.
|
||||
@param offset milliseconds
|
||||
*/
|
||||
Timeval(unsigned offset=0) { future(offset); }
|
||||
@@ -76,8 +78,9 @@ class Timeval {
|
||||
/** Return total seconds. */
|
||||
double seconds() const;
|
||||
|
||||
uint32_t sec() const { return mTimeval.tv_sec; }
|
||||
uint32_t usec() const { return mTimeval.tv_usec; }
|
||||
uint32_t sec() const { return mTimespec.tv_sec; }
|
||||
uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
|
||||
uint32_t nsec() const { return mTimespec.tv_nsec; }
|
||||
|
||||
/** Return differnce from other (other-self), in ms. */
|
||||
long delta(const Timeval& other) const;
|
||||
@@ -88,11 +91,11 @@ class Timeval {
|
||||
/** Remaining time in ms. */
|
||||
long remaining() const { return -elapsed(); }
|
||||
|
||||
/** Return true if the time has passed, as per gettimeofday. */
|
||||
/** Return true if the time has passed, as per clock_gettime(CLOCK_REALTIME). */
|
||||
bool passed() const;
|
||||
|
||||
/** Add a given number of minutes to the time. */
|
||||
void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; }
|
||||
void addMinutes(unsigned minutes) { mTimespec.tv_sec += minutes*60; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "Timeval.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
Timeval then(10000);
|
||||
cout << then.elapsed() << endl;
|
||||
|
||||
while (!then.passed()) {
|
||||
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
|
||||
usleep(500000);
|
||||
}
|
||||
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
|
||||
}
|
||||
36
CommonLibs/Utils.cpp
Normal file
36
CommonLibs/Utils.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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(const 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;
|
||||
}
|
||||
24
CommonLibs/Utils.h
Normal file
24
CommonLibs/Utils.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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(const char* opt);
|
||||
@@ -32,11 +32,14 @@
|
||||
#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.
|
||||
@@ -60,6 +63,8 @@ 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:
|
||||
|
||||
@@ -85,9 +90,19 @@ template <class T> class Vector {
|
||||
/** Change the size of the Vector, discarding content. */
|
||||
void resize(size_t newSize)
|
||||
{
|
||||
if (mData!=NULL) delete[] mData;
|
||||
if (mData!=NULL) {
|
||||
if (mFreeFunc)
|
||||
mFreeFunc(mData);
|
||||
else
|
||||
delete[] mData;
|
||||
}
|
||||
if (newSize==0) mData=NULL;
|
||||
else mData = new T[newSize];
|
||||
else {
|
||||
if (mAllocFunc)
|
||||
mData = (T*) mAllocFunc(newSize);
|
||||
else
|
||||
mData = new T[newSize];
|
||||
}
|
||||
mStart = mData;
|
||||
mEnd = mStart + newSize;
|
||||
}
|
||||
@@ -107,7 +122,7 @@ template <class T> class Vector {
|
||||
void clone(const Vector<T>& other)
|
||||
{
|
||||
resize(other.size());
|
||||
memcpy(mData,other.mStart,other.bytes());
|
||||
other.copyTo(*this);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,29 +131,31 @@ template <class T> class Vector {
|
||||
//@{
|
||||
|
||||
/** Build an empty Vector of a given size. */
|
||||
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
||||
Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
||||
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
||||
{ resize(wSize); }
|
||||
|
||||
/** Build a Vector by shifting the data block. */
|
||||
Vector(Vector<T>& other)
|
||||
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
||||
/** 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)
|
||||
{ other.mData=NULL; }
|
||||
|
||||
/** Build a Vector by copying another. */
|
||||
Vector(const Vector<T>& other):mData(NULL) { clone(other); }
|
||||
Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); }
|
||||
|
||||
/** Build a Vector with explicit values. */
|
||||
Vector(T* wData, T* wStart, T* wEnd)
|
||||
:mData(wData),mStart(wStart),mEnd(wEnd)
|
||||
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)
|
||||
{ }
|
||||
|
||||
/** Build a vector from an existing block, NOT to be deleted upon destruction. */
|
||||
Vector(T* wStart, size_t span)
|
||||
:mData(NULL),mStart(wStart),mEnd(wStart+span)
|
||||
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)
|
||||
{ }
|
||||
|
||||
/** Build a Vector by concatenation. */
|
||||
Vector(const Vector<T>& other1, const Vector<T>& other2)
|
||||
:mData(NULL)
|
||||
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)
|
||||
{
|
||||
resize(other1.size()+other2.size());
|
||||
memcpy(mStart, other1.mStart, other1.bytes());
|
||||
@@ -162,6 +179,8 @@ template <class T> class Vector {
|
||||
mData=other.mData;
|
||||
mStart=other.mStart;
|
||||
mEnd=other.mEnd;
|
||||
mAllocFunc=other.mAllocFunc;
|
||||
mFreeFunc=other.mFreeFunc;
|
||||
other.mData=NULL;
|
||||
}
|
||||
|
||||
@@ -204,10 +223,15 @@ template <class T> class Vector {
|
||||
*/
|
||||
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
|
||||
{
|
||||
T* base = other.mStart + start;
|
||||
assert(base+span<=other.mEnd);
|
||||
unsigned int i;
|
||||
T* dst = other.mStart + start;
|
||||
T* src = mStart;
|
||||
assert(dst+span<=other.mEnd);
|
||||
assert(mStart+span<=mEnd);
|
||||
memcpy(base,mStart,span*sizeof(T));
|
||||
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)); */
|
||||
}
|
||||
|
||||
/** Copy all of this Vector to a segment of another Vector. */
|
||||
@@ -282,7 +306,7 @@ template <class T> class Vector {
|
||||
T* end() { return mEnd; }
|
||||
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
20
CommonLibs/config_defs.h
Normal file
20
CommonLibs/config_defs.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#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,
|
||||
};
|
||||
36
CommonLibs/debug.c
Normal file
36
CommonLibs/debug.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#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),
|
||||
};
|
||||
11
CommonLibs/debug.h
Normal file
11
CommonLibs/debug.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
extern const struct log_info log_info;
|
||||
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DMAIN,
|
||||
DTRXCTRL,
|
||||
DDEV,
|
||||
DLMS,
|
||||
};
|
||||
35
CommonLibs/osmo_signal.h
Normal file
35
CommonLibs/osmo_signal.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* 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 */
|
||||
};
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include "sqlite3util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
// Wrappers to sqlite operations.
|
||||
// These will eventually get moved to commonlibs.
|
||||
|
||||
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query)
|
||||
{
|
||||
int src = SQLITE_BUSY;
|
||||
while (src==SQLITE_BUSY) {
|
||||
src = sqlite3_prepare_v2(DB,query,strlen(query),stmt,NULL);
|
||||
if (src==SQLITE_BUSY) {
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
if (src) {
|
||||
fprintf(stderr,"sqlite3_prepare_v2 failed for \"%s\": %s\n",query,sqlite3_errmsg(DB));
|
||||
sqlite3_finalize(*stmt);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt)
|
||||
{
|
||||
int src = SQLITE_BUSY;
|
||||
while (src==SQLITE_BUSY) {
|
||||
src = sqlite3_step(stmt);
|
||||
if (src==SQLITE_BUSY) {
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
if ((src!=SQLITE_DONE) && (src!=SQLITE_ROW)) {
|
||||
fprintf(stderr,"sqlite3_run_query failed: %s: %s\n", sqlite3_sql(stmt), sqlite3_errmsg(DB));
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
bool sqlite3_exists(sqlite3* DB, const char *tableName,
|
||||
const char* keyName, const char* keyData)
|
||||
{
|
||||
size_t stringSize = 100 + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
||||
char query[stringSize];
|
||||
sprintf(query,"SELECT * FROM %s WHERE %s == \"%s\"",tableName,keyName,keyData);
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
// Anything there?
|
||||
return (src == SQLITE_ROW);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
|
||||
const char* keyName, const char* keyData,
|
||||
const char* valueName, unsigned &valueData)
|
||||
{
|
||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
||||
char query[stringSize];
|
||||
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
bool retVal = false;
|
||||
if (src == SQLITE_ROW) {
|
||||
valueData = (unsigned)sqlite3_column_int64(stmt,0);
|
||||
retVal = true;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
// This function returns an allocated string that must be free'd by the caller.
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, const char* keyData,
|
||||
const char* valueName, char* &valueData)
|
||||
{
|
||||
valueData=NULL;
|
||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
||||
char query[stringSize];
|
||||
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
bool retVal = false;
|
||||
if (src == SQLITE_ROW) {
|
||||
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
|
||||
if (ptr) valueData = strdup(ptr);
|
||||
retVal = true;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
// This function returns an allocated string that must be free'd by tha caller.
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, unsigned keyData,
|
||||
const char* valueName, char* &valueData)
|
||||
{
|
||||
valueData=NULL;
|
||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + 20;
|
||||
char query[stringSize];
|
||||
sprintf(query,"SELECT %s FROM %s WHERE %s == %u",valueName,tableName,keyName,keyData);
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
bool retVal = false;
|
||||
if (src == SQLITE_ROW) {
|
||||
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
|
||||
if (ptr) valueData = strdup(ptr);
|
||||
retVal = true;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool sqlite3_command(sqlite3* DB, const char* query)
|
||||
{
|
||||
// Prepare the statement.
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
||||
// Run the query.
|
||||
int src = sqlite3_run_query(DB,stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return src==SQLITE_DONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef SQLITE3UTIL_H
|
||||
#define SQLITE3UTIL_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query);
|
||||
|
||||
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt);
|
||||
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
|
||||
const char* keyName, const char* keyData,
|
||||
const char* valueName, unsigned &valueData);
|
||||
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, const char* keyData,
|
||||
const char* valueName, char* &valueData);
|
||||
|
||||
// This function returns an allocated string that must be free'd by the caller.
|
||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, unsigned keyData,
|
||||
const char* valueName, char* &valueData);
|
||||
|
||||
bool sqlite3_exists(sqlite3* DB, const char* tableName,
|
||||
const char* keyName, const char* keyData);
|
||||
|
||||
/** Run a query, ignoring the result; return true on success. */
|
||||
bool sqlite3_command(sqlite3* DB, const char* query);
|
||||
|
||||
#endif
|
||||
576
CommonLibs/trx_vty.c
Normal file
576
CommonLibs/trx_vty.c
Normal file
@@ -0,0 +1,576 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
68
CommonLibs/trx_vty.h
Normal file
68
CommonLibs/trx_vty.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#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);
|
||||
@@ -54,7 +54,10 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
|
||||
|
||||
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
||||
|
||||
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
|
||||
/* 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 */
|
||||
|
||||
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
||||
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
||||
|
||||
@@ -52,7 +52,9 @@ extern const BitVector gEdgeTrainingSequence[];
|
||||
extern const BitVector gDummyBurst;
|
||||
|
||||
/** Random access burst synch. sequence */
|
||||
extern const BitVector gRACHSynchSequence;
|
||||
extern const BitVector gRACHSynchSequenceTS0;
|
||||
extern const BitVector gRACHSynchSequenceTS1;
|
||||
extern const BitVector gRACHSynchSequenceTS2;
|
||||
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
||||
extern const BitVector gRACHBurst;
|
||||
|
||||
|
||||
22
INSTALLATION
22
INSTALLATION
@@ -2,32 +2,18 @@ Installation Requirements
|
||||
|
||||
|
||||
|
||||
OpenBTS compiles to a simple Unix binary and does not require special
|
||||
osmo-trx compiles to a simple Unix binary and does not require special
|
||||
installation.
|
||||
|
||||
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
|
||||
running configure.
|
||||
|
||||
To run OpenBTS, the following should be installed:
|
||||
|
||||
Asterisk (http://www.asterisk.org), running SIP on port 5060.
|
||||
|
||||
libosip2 (http://www.gnu.org/software/osip/)
|
||||
|
||||
libortp (http://freshmeat.net/projects/ortp/)
|
||||
|
||||
libusrp (http://gnuradio.org).
|
||||
To run osmo-trx, the following should be installed:
|
||||
libuhd (https://gnuradio.org).
|
||||
This is part of the GNURadio installation.
|
||||
It is the only part used by OpenBTS.
|
||||
|
||||
|
||||
OpenBTS logs to syslogd as facility LOG_LOCAL7. Please set your /etc/syslog.conf
|
||||
accordingly.
|
||||
|
||||
|
||||
For information on specific executables, see tests/README.tests and
|
||||
apps/README.apps.
|
||||
|
||||
See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more
|
||||
See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
|
||||
information.
|
||||
|
||||
|
||||
20
LEGAL
20
LEGAL
@@ -1,5 +1,8 @@
|
||||
OpenBTS
|
||||
|
||||
The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details.
|
||||
|
||||
The related copyrights:
|
||||
Most parts copyright 2008-2011 Free Software Foundation.
|
||||
Some parts copyright 2010 Kestrel Signal Processing, Inc.
|
||||
Some parts copyright 2011 Range Networks, Inc.
|
||||
@@ -12,17 +15,9 @@ patented technologies. The user of this software is required to take whatever
|
||||
actions are necessary to avoid patent infringement.
|
||||
|
||||
|
||||
Trademark
|
||||
|
||||
"OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a
|
||||
California corporation. Range reserves the right to control the use of this
|
||||
trademark. Do not use this trademark in commerce without permission and do not
|
||||
rebrand OpenBTS under a different trademark.
|
||||
|
||||
|
||||
Telecom and Radio Spectrum Laws
|
||||
|
||||
The primary function of OpenBTS is the provision of telecommunications service
|
||||
The primary function of OsmoTRX is the provision of telecommunications service
|
||||
over a radio link. This activity is heavily regulated nearly everywhere in
|
||||
the world. Users of this software are expected to comply with local and national
|
||||
regulations in the jurisdictions where this sortware is used with radio equipment.
|
||||
@@ -39,7 +34,7 @@ The legal restrictions listed here are not necessarily exhaustive.
|
||||
|
||||
Note to US Government Users
|
||||
|
||||
The OpenBTS software applications and associated documentation are "Commercial
|
||||
The OsmoTRX software applications and associated documentation are "Commercial
|
||||
Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of
|
||||
"Commercial Computer Software" and "Commercial Computer Software Documentation,"
|
||||
as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
|
||||
@@ -54,13 +49,12 @@ and AGPLv3.
|
||||
Note to US Government Contractors
|
||||
|
||||
GPL is not compatible with "government purpose rights" (GPR). If you receive
|
||||
OpenBTS software under a GPL and deliver it under GPR, you will be in violation
|
||||
OsmoTRX software under a GPL and deliver it under GPR, you will be in violation
|
||||
of GPL and possibly subject to enforcement actions by the original authors and
|
||||
copyright holders, including the Free Software Foundation, Inc.
|
||||
|
||||
|
||||
Software Licensing and Distribution
|
||||
|
||||
A subset of OpenBTS is distributed publicly under AGPLv3. Range reserves the right to
|
||||
distribute most of this source code other licenses as well. See the COPYING file
|
||||
The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file
|
||||
for more information on the license for this distribution.
|
||||
|
||||
19
Makefile.am
19
Makefile.am
@@ -21,16 +21,19 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
ACLOCAL_AMFLAGS = -I config
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES) $(SQLITE3_CFLAGS)
|
||||
AM_CXXFLAGS = -Wall -pthread -ldl
|
||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
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
|
||||
|
||||
# Order must be preserved
|
||||
SUBDIRS = \
|
||||
doc \
|
||||
CommonLibs \
|
||||
GSM \
|
||||
Transceiver52M
|
||||
Transceiver52M \
|
||||
contrib \
|
||||
tests
|
||||
|
||||
EXTRA_DIST = \
|
||||
autogen.sh \
|
||||
@@ -39,6 +42,12 @@ EXTRA_DIST = \
|
||||
COPYING \
|
||||
README
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
.PHONY: release
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
dox: FORCE
|
||||
doxygen doxconfig
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
# 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
|
||||
|
||||
@@ -32,4 +29,10 @@ 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 = *~
|
||||
|
||||
@@ -25,12 +25,11 @@
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Channelizer.h"
|
||||
|
||||
extern "C" {
|
||||
#include "common/fft.h"
|
||||
#include "common/convolve.h"
|
||||
#include "fft.h"
|
||||
#include "convolve.h"
|
||||
}
|
||||
|
||||
static void deinterleave(const float *in, size_t ilen,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "ChannelizerBase.h"
|
||||
|
||||
extern "C" {
|
||||
#include "common/fft.h"
|
||||
#include "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);
|
||||
|
||||
@@ -21,22 +21,10 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common
|
||||
AM_CXXFLAGS = -ldl -lpthread
|
||||
SUBDIRS = arch device
|
||||
|
||||
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
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
|
||||
rev2dir = $(datadir)/usrp/rev2
|
||||
rev4dir = $(datadir)/usrp/rev4
|
||||
@@ -44,11 +32,9 @@ rev4dir = $(datadir)/usrp/rev4
|
||||
dist_rev2_DATA = std_inband.rbf
|
||||
dist_rev4_DATA = std_inband.rbf
|
||||
|
||||
EXTRA_DIST = \
|
||||
README \
|
||||
README.Talgorithm
|
||||
EXTRA_DIST = README
|
||||
|
||||
noinst_LTLIBRARIES = libtransceiver.la
|
||||
noinst_LTLIBRARIES = libtransceiver_common.la
|
||||
|
||||
COMMON_SOURCES = \
|
||||
radioInterface.cpp \
|
||||
@@ -60,66 +46,66 @@ COMMON_SOURCES = \
|
||||
Transceiver.cpp \
|
||||
ChannelizerBase.cpp \
|
||||
Channelizer.cpp \
|
||||
Synthesis.cpp \
|
||||
common/fft.c
|
||||
Synthesis.cpp
|
||||
|
||||
libtransceiver_la_SOURCES = \
|
||||
libtransceiver_common_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 \
|
||||
common/convolve.h \
|
||||
common/convert.h \
|
||||
common/scale.h \
|
||||
common/mult.h \
|
||||
common/fft.h
|
||||
Synthesis.h
|
||||
|
||||
osmo_trx_SOURCES = osmo-trx.cpp
|
||||
osmo_trx_LDADD = \
|
||||
libtransceiver.la \
|
||||
COMMON_LDADD = \
|
||||
libtransceiver_common.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE3_LIBS)
|
||||
$(COMMON_LA) \
|
||||
$(FFTWF_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS)
|
||||
|
||||
osmo_trx_gen_SOURCES = osmo-trx-gen.cpp
|
||||
osmo_trx_gen_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
bin_PROGRAMS =
|
||||
|
||||
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)
|
||||
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)
|
||||
endif
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Resampler.h"
|
||||
|
||||
@@ -35,6 +36,8 @@ extern "C" {
|
||||
|
||||
#define MAX_OUTPUT_LEN 4096
|
||||
|
||||
using namespace std;
|
||||
|
||||
static float sinc(float x)
|
||||
{
|
||||
if (x == 0.0)
|
||||
@@ -43,32 +46,19 @@ static float sinc(float x)
|
||||
return sin(M_PI * x) / (M_PI * x);
|
||||
}
|
||||
|
||||
bool Resampler::initFilters(float bw)
|
||||
void Resampler::initFilters(float bw)
|
||||
{
|
||||
size_t proto_len = p * filt_len;
|
||||
float *proto, val, cutoff;
|
||||
float cutoff;
|
||||
float sum = 0.0f, scale = 0.0f;
|
||||
float midpt = (float) (proto_len - 1.0) / 2.0;
|
||||
|
||||
/*
|
||||
* Allocate partition filters and the temporary prototype filter
|
||||
* according to numerator of the rational rate. Coefficients are
|
||||
* real only and must be 16-byte memory aligned for SSE usage.
|
||||
*/
|
||||
proto = new float[proto_len];
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
partitions = (float **) malloc(sizeof(float *) * p);
|
||||
if (!partitions) {
|
||||
delete[] proto;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < p; i++) {
|
||||
partitions[i] = (float *)
|
||||
memalign(16, filt_len * 2 * sizeof(float));
|
||||
}
|
||||
auto proto = vector<float>(p * filt_len);
|
||||
for (auto &part : partitions)
|
||||
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
|
||||
|
||||
/*
|
||||
* Generate the prototype filter with a Blackman-harris window.
|
||||
@@ -85,47 +75,26 @@ bool Resampler::initFilters(float bw)
|
||||
else
|
||||
cutoff = (float) q;
|
||||
|
||||
for (size_t i = 0; i < proto_len; i++) {
|
||||
float midpt = (proto.size() - 1) / 2.0;
|
||||
for (size_t i = 0; i < proto.size(); i++) {
|
||||
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
||||
proto[i] *= a0 -
|
||||
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
|
||||
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
|
||||
a3 * cos(6 * M_PI * i / (proto_len - 1));
|
||||
a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
|
||||
a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
|
||||
a3 * cos(6 * M_PI * i / (proto.size() - 1));
|
||||
sum += proto[i];
|
||||
}
|
||||
scale = p / sum;
|
||||
|
||||
/* Populate filter partitions from the prototype filter */
|
||||
for (size_t i = 0; i < filt_len; i++) {
|
||||
for (size_t n = 0; n < p; n++) {
|
||||
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
|
||||
partitions[n][2 * i + 1] = 0.0f;
|
||||
}
|
||||
for (size_t n = 0; n < p; n++)
|
||||
partitions[n][i] = complex<float>(proto[i * p + n] * scale);
|
||||
}
|
||||
|
||||
/* For convolution, we store the filter taps in reverse */
|
||||
for (size_t n = 0; n < p; n++) {
|
||||
for (size_t i = 0; i < filt_len / 2; i++) {
|
||||
val = partitions[n][2 * i];
|
||||
partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
|
||||
partitions[n][2 * (filt_len - 1 - i)] = val;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] proto;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resampler::releaseFilters()
|
||||
{
|
||||
if (partitions) {
|
||||
for (size_t i = 0; i < p; i++)
|
||||
free(partitions[i]);
|
||||
}
|
||||
|
||||
free(partitions);
|
||||
partitions = NULL;
|
||||
/* Store filter taps in reverse */
|
||||
for (auto &part : partitions)
|
||||
reverse(&part[0], &part[filt_len]);
|
||||
}
|
||||
|
||||
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||
@@ -159,14 +128,6 @@ 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;
|
||||
@@ -180,8 +141,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,
|
||||
partitions[path], filt_len,
|
||||
&out[2 * i], out_len - i,
|
||||
reinterpret_cast<float *>(partitions[path]),
|
||||
filt_len, &out[2 * i], out_len - i,
|
||||
n, 1, 1, 0);
|
||||
}
|
||||
|
||||
@@ -190,14 +151,18 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
|
||||
|
||||
bool Resampler::init(float bw)
|
||||
{
|
||||
if (p == 0 || q == 0 || filt_len == 0) return false;
|
||||
|
||||
/* Filterbank filter internals */
|
||||
if (!initFilters(bw))
|
||||
return false;
|
||||
initFilters(bw);
|
||||
|
||||
/* Precompute filterbank paths */
|
||||
in_index = new size_t[MAX_OUTPUT_LEN];
|
||||
out_path = new size_t[MAX_OUTPUT_LEN];
|
||||
computePath();
|
||||
int i = 0;
|
||||
for (auto &index : in_index)
|
||||
index = (q * i++) / p;
|
||||
i = 0;
|
||||
for (auto &path : out_path)
|
||||
path = (q * i++) % p;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -208,7 +173,7 @@ size_t Resampler::len()
|
||||
}
|
||||
|
||||
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||
: in_index(NULL), out_path(NULL), partitions(NULL)
|
||||
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
|
||||
{
|
||||
this->p = p;
|
||||
this->q = q;
|
||||
@@ -217,8 +182,6 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||
|
||||
Resampler::~Resampler()
|
||||
{
|
||||
releaseFilters();
|
||||
|
||||
delete in_index;
|
||||
delete out_path;
|
||||
for (auto &part : partitions)
|
||||
free(part);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#ifndef _RESAMPLER_H_
|
||||
#define _RESAMPLER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
|
||||
class Resampler {
|
||||
public:
|
||||
/* Constructor for rational sample rate conversion
|
||||
@@ -63,14 +66,11 @@ private:
|
||||
size_t p;
|
||||
size_t q;
|
||||
size_t filt_len;
|
||||
size_t *in_index;
|
||||
size_t *out_path;
|
||||
std::vector<size_t> in_index;
|
||||
std::vector<size_t> out_path;
|
||||
std::vector<std::complex<float> *> partitions;
|
||||
|
||||
float **partitions;
|
||||
|
||||
bool initFilters(float bw);
|
||||
void releaseFilters();
|
||||
void computePath();
|
||||
void initFilters(float bw);
|
||||
};
|
||||
|
||||
#endif /* _RESAMPLER_H_ */
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Synthesis.h"
|
||||
|
||||
extern "C" {
|
||||
#include "common/fft.h"
|
||||
#include "common/convolve.h"
|
||||
#include "fft.h"
|
||||
#include "convolve.h"
|
||||
}
|
||||
|
||||
static void interleave(float **in, size_t ilen,
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#include "Transceiver.h"
|
||||
#include <Logger.h>
|
||||
|
||||
extern "C" {
|
||||
#include "osmo_signal.h"
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
@@ -35,12 +39,6 @@ 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
|
||||
|
||||
@@ -71,7 +69,7 @@ TransceiverState::~TransceiverState()
|
||||
}
|
||||
}
|
||||
|
||||
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
|
||||
bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
|
||||
{
|
||||
signalVector *burst;
|
||||
|
||||
@@ -81,19 +79,19 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un
|
||||
for (size_t n = 0; n < 8; n++) {
|
||||
for (size_t i = 0; i < 102; i++) {
|
||||
switch (filler) {
|
||||
case Transceiver::FILLER_DUMMY:
|
||||
case FILLER_DUMMY:
|
||||
burst = generateDummyBurst(sps, n);
|
||||
break;
|
||||
case Transceiver::FILLER_NORM_RAND:
|
||||
burst = genRandNormalBurst(rtsc, sps, n, mPrbs);
|
||||
case FILLER_NORM_RAND:
|
||||
burst = genRandNormalBurst(rtsc, sps, n);
|
||||
break;
|
||||
case Transceiver::FILLER_EDGE_RAND:
|
||||
case FILLER_EDGE_RAND:
|
||||
burst = generateEdgeBurst(rtsc);
|
||||
break;
|
||||
case Transceiver::FILLER_ACCESS_RAND:
|
||||
case FILLER_ACCESS_RAND:
|
||||
burst = genRandAccessBurst(rach_delay, sps, n);
|
||||
break;
|
||||
case Transceiver::FILLER_ZERO:
|
||||
case FILLER_ZERO:
|
||||
default:
|
||||
burst = generateEmptyBurst(sps, n);
|
||||
}
|
||||
@@ -102,8 +100,8 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un
|
||||
fillerTable[i][n] = burst;
|
||||
}
|
||||
|
||||
if ((filler == Transceiver::FILLER_NORM_RAND) ||
|
||||
(filler == Transceiver::FILLER_EDGE_RAND)) {
|
||||
if ((filler == FILLER_NORM_RAND) ||
|
||||
(filler == FILLER_EDGE_RAND)) {
|
||||
chanType[n] = TSC;
|
||||
}
|
||||
}
|
||||
@@ -112,16 +110,17 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un
|
||||
}
|
||||
|
||||
Transceiver::Transceiver(int wBasePort,
|
||||
const char *wTRXAddress,
|
||||
const char *TRXAddress,
|
||||
const char *GSMcoreAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
double wRssiOffset)
|
||||
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
||||
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
||||
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
|
||||
mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
|
||||
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||
rssiOffset(wRssiOffset),
|
||||
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false),
|
||||
rssiOffset(wRssiOffset), sig_cbfn(NULL),
|
||||
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
|
||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
||||
mWriteBurstToDiskMask(0)
|
||||
{
|
||||
@@ -160,7 +159,7 @@ Transceiver::~Transceiver()
|
||||
* are still expected to report clock indications through control channel
|
||||
* activity.
|
||||
*/
|
||||
bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
|
||||
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge)
|
||||
{
|
||||
int d_srcport, d_dstport, c_srcport, c_dstport;
|
||||
|
||||
@@ -197,8 +196,8 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
|
||||
d_srcport = mBasePort + 2 * i + 2;
|
||||
d_dstport = mBasePort + 2 * i + 102;
|
||||
|
||||
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
|
||||
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
||||
mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
|
||||
mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
|
||||
}
|
||||
|
||||
/* Randomize the central clock */
|
||||
@@ -224,6 +223,17 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
|
||||
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
|
||||
*
|
||||
@@ -273,7 +283,7 @@ bool Transceiver::start()
|
||||
TxUpperLoopAdapter, (void*) chan);
|
||||
}
|
||||
|
||||
writeClockInterface();
|
||||
mForceClockInterface = true;
|
||||
mOn = true;
|
||||
return true;
|
||||
}
|
||||
@@ -297,6 +307,10 @@ void Transceiver::stop()
|
||||
LOG(NOTICE) << "Stopping the transceiver";
|
||||
mTxLowerLoopThread->cancel();
|
||||
mRxLowerLoopThread->cancel();
|
||||
mTxLowerLoopThread->join();
|
||||
mRxLowerLoopThread->join();
|
||||
delete mTxLowerLoopThread;
|
||||
delete mRxLowerLoopThread;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
mRxServiceLoopThreads[i]->cancel();
|
||||
@@ -315,11 +329,6 @@ void Transceiver::stop()
|
||||
mTxPriorityQueues[i].clear();
|
||||
}
|
||||
|
||||
mTxLowerLoopThread->join();
|
||||
mRxLowerLoopThread->join();
|
||||
delete mTxLowerLoopThread;
|
||||
delete mRxLowerLoopThread;
|
||||
|
||||
mOn = false;
|
||||
LOG(NOTICE) << "Transceiver stopped";
|
||||
}
|
||||
@@ -379,7 +388,8 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
||||
state = &mStates[i];
|
||||
|
||||
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
|
||||
LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
|
||||
LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->SDR interface ("
|
||||
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
|
||||
if (state->mRetrans)
|
||||
updateFillerTable(i, burst);
|
||||
delete burst;
|
||||
@@ -430,7 +440,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;
|
||||
@@ -545,7 +555,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,
|
||||
@@ -607,7 +617,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
||||
avg = sqrt(avg / radio_burst->chans());
|
||||
|
||||
wTime = time;
|
||||
RSSI = 20.0 * log10(avg / rxFullScale);
|
||||
RSSI = 20.0 * log10(rxFullScale / avg);
|
||||
|
||||
/* RSSI estimation are valid */
|
||||
isRssiValid = true;
|
||||
@@ -656,42 +666,67 @@ 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)
|
||||
{
|
||||
int MAX_PACKET_LENGTH = 100;
|
||||
char buffer[MAX_PACKET_LENGTH + 1];
|
||||
char response[MAX_PACKET_LENGTH + 1];
|
||||
char *command, *params;
|
||||
int msgLen;
|
||||
|
||||
// check control socket
|
||||
char buffer[MAX_PACKET_LENGTH];
|
||||
int msgLen = -1;
|
||||
buffer[0] = '\0';
|
||||
/* Attempt to read from control socket */
|
||||
msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
|
||||
if (msgLen < 1)
|
||||
return;
|
||||
|
||||
msgLen = mCtrlSockets[chan]->read(buffer, sizeof(buffer));
|
||||
/* Zero-terminate received string */
|
||||
buffer[msgLen] = '\0';
|
||||
|
||||
if (msgLen < 1) {
|
||||
/* Verify a command signature */
|
||||
if (strncmp(buffer, "CMD ", 4)) {
|
||||
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
|
||||
return;
|
||||
}
|
||||
|
||||
char cmdcheck[4];
|
||||
char command[MAX_PACKET_LENGTH];
|
||||
char response[MAX_PACKET_LENGTH];
|
||||
/* Set command pointer */
|
||||
command = buffer + 4;
|
||||
LOGC(DTRXCTRL, INFO) << "chan " << chan << ": command is '" << command << "'";
|
||||
|
||||
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) {
|
||||
if (match_cmd(command, "POWEROFF", NULL)) {
|
||||
stop();
|
||||
sprintf(response,"RSP POWEROFF 0");
|
||||
}
|
||||
else if (strcmp(command,"POWERON")==0) {
|
||||
} else if (match_cmd(command, "POWERON", NULL)) {
|
||||
if (!start()) {
|
||||
sprintf(response,"RSP POWERON 1");
|
||||
} else {
|
||||
@@ -701,41 +736,43 @@ void Transceiver::driveControl(size_t chan)
|
||||
mHandover[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(command,"HANDOVER")==0){
|
||||
int ts=0,ss=0;
|
||||
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
|
||||
mHandover[ts][ss] = true;
|
||||
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) {
|
||||
} else if (match_cmd(command, "HANDOVER", ¶ms)) {
|
||||
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", ¶ms)) {
|
||||
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", ¶ms)) {
|
||||
//set expected maximum time-of-arrival
|
||||
int maxDelay;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
|
||||
sscanf(params, "%d", &maxDelay);
|
||||
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
|
||||
}
|
||||
else if (strcmp(command,"SETMAXDLYNB")==0) {
|
||||
} else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) {
|
||||
//set expected maximum time-of-arrival
|
||||
int maxDelay;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
|
||||
sscanf(params, "%d", &maxDelay);
|
||||
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
|
||||
}
|
||||
else if (strcmp(command,"SETRXGAIN")==0) {
|
||||
} else if (match_cmd(command, "SETRXGAIN", ¶ms)) {
|
||||
//set expected maximum time-of-arrival
|
||||
int newGain;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
|
||||
sscanf(params, "%d", &newGain);
|
||||
newGain = mRadioInterface->setRxGain(newGain, chan);
|
||||
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
|
||||
}
|
||||
else if (strcmp(command,"NOISELEV")==0) {
|
||||
} else if (match_cmd(command, "NOISELEV", NULL)) {
|
||||
if (mOn) {
|
||||
float lev = mStates[chan].mNoiseLev;
|
||||
sprintf(response,"RSP NOISELEV 0 %d",
|
||||
@@ -744,86 +781,78 @@ void Transceiver::driveControl(size_t chan)
|
||||
else {
|
||||
sprintf(response,"RSP NOISELEV 1 0");
|
||||
}
|
||||
}
|
||||
else if (!strcmp(command, "SETPOWER")) {
|
||||
} else if (match_cmd(command, "SETPOWER", ¶ms)) {
|
||||
int power;
|
||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
|
||||
sscanf(params, "%d", &power);
|
||||
power = mRadioInterface->setPowerAttenuation(power, chan);
|
||||
mStates[chan].mPower = power;
|
||||
sprintf(response, "RSP SETPOWER 0 %d", power);
|
||||
}
|
||||
else if (!strcmp(command,"ADJPOWER")) {
|
||||
} else if (match_cmd(command, "ADJPOWER", ¶ms)) {
|
||||
int power, step;
|
||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
|
||||
sscanf(params, "%d", &step);
|
||||
power = mStates[chan].mPower + step;
|
||||
power = mRadioInterface->setPowerAttenuation(power, chan);
|
||||
mStates[chan].mPower = power;
|
||||
sprintf(response, "RSP ADJPOWER 0 %d", power);
|
||||
}
|
||||
else if (strcmp(command,"RXTUNE")==0) {
|
||||
} else if (match_cmd(command, "RXTUNE", ¶ms)) {
|
||||
// tune receiver
|
||||
int freqKhz;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
||||
sscanf(params, "%d", &freqKhz);
|
||||
mRxFreq = freqKhz * 1e3;
|
||||
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
|
||||
LOG(ALERT) << "RX failed to tune";
|
||||
LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
|
||||
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
|
||||
}
|
||||
else
|
||||
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
|
||||
}
|
||||
else if (strcmp(command,"TXTUNE")==0) {
|
||||
} else if (match_cmd(command, "TXTUNE", ¶ms)) {
|
||||
// tune txmtr
|
||||
int freqKhz;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
||||
sscanf(params, "%d", &freqKhz);
|
||||
mTxFreq = freqKhz * 1e3;
|
||||
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
|
||||
LOG(ALERT) << "TX failed to tune";
|
||||
LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
|
||||
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
||||
}
|
||||
else
|
||||
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
|
||||
}
|
||||
else if (!strcmp(command,"SETTSC")) {
|
||||
} else if (match_cmd(command, "SETTSC", ¶ms)) {
|
||||
// set TSC
|
||||
unsigned TSC;
|
||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
|
||||
sscanf(params, "%u", &TSC);
|
||||
if (TSC > 7) {
|
||||
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
||||
} else {
|
||||
LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
|
||||
LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
|
||||
mTSC = TSC;
|
||||
sprintf(response,"RSP SETTSC 0 %d", TSC);
|
||||
}
|
||||
}
|
||||
else if (strcmp(command,"SETSLOT")==0) {
|
||||
} else if (match_cmd(command, "SETSLOT", ¶ms)) {
|
||||
// set slot type
|
||||
int corrCode;
|
||||
int timeslot;
|
||||
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode);
|
||||
sscanf(params, "%d %d", ×lot, &corrCode);
|
||||
if ((timeslot < 0) || (timeslot > 7)) {
|
||||
LOG(WARNING) << "bogus message on control interface";
|
||||
LOGC(DTRXCTRL, 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 (strcmp(command,"_SETBURSTTODISKMASK")==0) {
|
||||
} else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) {
|
||||
// debug command! may change or disapear without notice
|
||||
// set a mask which bursts to dump to disk
|
||||
int mask;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&mask);
|
||||
sscanf(params, "%d", &mask);
|
||||
mWriteBurstToDiskMask = mask;
|
||||
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
|
||||
}
|
||||
else {
|
||||
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
||||
} else {
|
||||
LOGC(DTRXCTRL, 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);
|
||||
}
|
||||
|
||||
@@ -853,14 +882,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);
|
||||
@@ -872,11 +901,15 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
|
||||
void Transceiver::driveReceiveRadio()
|
||||
{
|
||||
if (!mRadioInterface->driveReceiveRadio()) {
|
||||
int rc = mRadioInterface->driveReceiveRadio();
|
||||
if (rc == 0) {
|
||||
usleep(100000);
|
||||
} else {
|
||||
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
||||
writeClockInterface();
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -946,7 +979,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.
|
||||
|
||||
@@ -957,7 +990,7 @@ void Transceiver::driveTxFIFO()
|
||||
|
||||
|
||||
RadioClock *radioClock = (mRadioInterface->getClock());
|
||||
|
||||
|
||||
if (mOn) {
|
||||
//radioClock->wait(); // wait until clock updates
|
||||
LOG(DEBUG) << "radio clock " << radioClock->get();
|
||||
@@ -969,14 +1002,15 @@ 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;
|
||||
LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
|
||||
<< radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
|
||||
mLatencyUpdateTime = radioClock->get();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if underrun hasn't occurred in the last sec (216 frames) drop
|
||||
// transmit latency by a timeslot
|
||||
if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
|
||||
if (mTransmitLatency > mRadioInterface->minLatency()) {
|
||||
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
|
||||
mTransmitLatency.decTN();
|
||||
LOG(INFO) << "reduced latency: " << mTransmitLatency;
|
||||
@@ -1012,11 +1046,15 @@ 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) {
|
||||
@@ -1028,6 +1066,8 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan)
|
||||
|
||||
void *RxLowerLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
set_selfthread_name("RxLower");
|
||||
|
||||
transceiver->setPriority(0.45);
|
||||
|
||||
while (1) {
|
||||
@@ -1039,6 +1079,8 @@ void *RxLowerLoopAdapter(Transceiver *transceiver)
|
||||
|
||||
void *TxLowerLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
set_selfthread_name("TxLower");
|
||||
|
||||
transceiver->setPriority(0.44);
|
||||
|
||||
while (1) {
|
||||
@@ -1050,11 +1092,15 @@ 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();
|
||||
@@ -1064,11 +1110,15 @@ 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) {
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
#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 */
|
||||
@@ -54,7 +59,7 @@ struct TransceiverState {
|
||||
~TransceiverState();
|
||||
|
||||
/* Initialize a multiframe slot in the filler table */
|
||||
bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
|
||||
bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
|
||||
|
||||
int chanType[8];
|
||||
|
||||
@@ -84,23 +89,22 @@ 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 manager, as a string
|
||||
@param TRXAddress IP address of the TRX, as a string
|
||||
@param GSMcoreAddress IP address of the GSM core, as a string
|
||||
@param wSPS number of samples per GSM symbol
|
||||
@param wTransmitLatency initial setting of transmit latency
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
const char *GSMcoreAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
@@ -110,7 +114,7 @@ public:
|
||||
~Transceiver();
|
||||
|
||||
/** Start the control loop */
|
||||
bool init(int filler, size_t rtsc, unsigned rach_delay, bool edge);
|
||||
bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge);
|
||||
|
||||
/** attach the radioInterface receive FIFO */
|
||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||
@@ -125,6 +129,8 @@ 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
|
||||
@@ -145,17 +151,10 @@ 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 mAddr;
|
||||
std::string mLocalAddr;
|
||||
std::string mRemoteAddr;
|
||||
|
||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||
@@ -172,7 +171,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
|
||||
@@ -181,6 +180,8 @@ 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);
|
||||
@@ -211,6 +212,7 @@ 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
|
||||
@@ -278,4 +280,3 @@ void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** transmit queueing thread loop */
|
||||
void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
|
||||
8
Transceiver52M/arch/Makefile.am
Normal file
8
Transceiver52M/arch/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
SUBDIRS = common
|
||||
if ARCH_ARM
|
||||
SUBDIRS += arm
|
||||
else
|
||||
SUBDIRS += x86
|
||||
endif
|
||||
@@ -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../common
|
||||
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../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,4 +20,3 @@ libarch_la_SOURCES = \
|
||||
scale_neon.S \
|
||||
mult.c \
|
||||
mult_neon.S
|
||||
endif
|
||||
@@ -28,6 +28,9 @@
|
||||
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,
|
||||
@@ -54,7 +57,6 @@ 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)
|
||||
{
|
||||
15
Transceiver52M/arch/common/Makefile.am
Normal file
15
Transceiver52M/arch/common/Makefile.am
Normal file
@@ -0,0 +1,15 @@
|
||||
AM_CFLAGS = -Wall -std=gnu99
|
||||
|
||||
noinst_LTLIBRARIES = libarch_common.la
|
||||
|
||||
noinst_HEADERS = \
|
||||
convolve.h \
|
||||
convert.h \
|
||||
scale.h \
|
||||
mult.h \
|
||||
fft.h
|
||||
|
||||
libarch_common_la_SOURCES = \
|
||||
convolve_base.c \
|
||||
convert_base.c \
|
||||
fft.c
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _CONVOLVE_H_
|
||||
#define _CONVOLVE_H_
|
||||
|
||||
void *convolve_h_alloc(int num);
|
||||
void *convolve_h_alloc(size_t num);
|
||||
|
||||
int convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
@@ -146,7 +146,7 @@ int base_convolve_complex(const float *x, int x_len,
|
||||
}
|
||||
|
||||
/* Aligned filter tap allocation */
|
||||
void *convolve_h_alloc(int len)
|
||||
void *convolve_h_alloc(size_t len)
|
||||
{
|
||||
#ifdef HAVE_SSE3
|
||||
return memalign(16, len * 2 * sizeof(float));
|
||||
@@ -1,11 +1,15 @@
|
||||
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
|
||||
|
||||
libarch_la_LIBADD =
|
||||
noinst_HEADERS = \
|
||||
convert_sse_3.h \
|
||||
convert_sse_4_1.h \
|
||||
convolve_sse_3.h
|
||||
|
||||
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
|
||||
|
||||
# SSE 3 specific code
|
||||
if HAVE_SSE3
|
||||
@@ -25,8 +29,5 @@ 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
|
||||
@@ -46,6 +46,7 @@ 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;
|
||||
@@ -60,6 +61,7 @@ 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)
|
||||
@@ -82,7 +82,7 @@ void convolve_init(void)
|
||||
c.conv_real4n = (void *)_base_convolve_real;
|
||||
c.conv_real = (void *)_base_convolve_real;
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
|
||||
if (__builtin_cpu_supports("sse3")) {
|
||||
c.conv_cmplx_4n = sse_conv_cmplx_4n;
|
||||
c.conv_cmplx_8n = sse_conv_cmplx_8n;
|
||||
17
Transceiver52M/device/Makefile.am
Normal file
17
Transceiver52M/device/Makefile.am
Normal file
@@ -0,0 +1,17 @@
|
||||
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
|
||||
700
Transceiver52M/device/lms/LMSDevice.cpp
Normal file
700
Transceiver52M/device/lms/LMSDevice.cpp
Normal file
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
* 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 "Utils.h"
|
||||
|
||||
#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)
|
||||
|
||||
static int compat_LMS_VCTCXORead(lms_device_t *dev, uint16_t *val, bool memory)
|
||||
{
|
||||
#if HAVE_LMS_VCTCXO_EEPROM_SAVING
|
||||
return LMS_VCTCXORead(dev, val, memory);
|
||||
#else
|
||||
return LMS_VCTCXORead(dev, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int compat_LMS_VCTCXOWrite(lms_device_t *dev, uint16_t val, bool memory)
|
||||
{
|
||||
#if HAVE_LMS_VCTCXO_EEPROM_SAVING
|
||||
return LMS_VCTCXOWrite(dev, val, memory);
|
||||
#else
|
||||
return LMS_VCTCXOWrite(dev, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
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 device 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;
|
||||
|
||||
filters = comma_delimited_to_vector(args.c_str());
|
||||
|
||||
/* 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[" << 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 (compat_LMS_VCTCXORead(m_lms_dev, &dac_val, false) < 0)
|
||||
goto out_close;
|
||||
LOGC(DDEV, INFO) << "Setting VCTCXO to " << dac_val;
|
||||
if (compat_LMS_VCTCXOWrite(m_lms_dev, dac_val, false) < 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);
|
||||
}
|
||||
211
Transceiver52M/device/lms/LMSDevice.h
Normal file
211
Transceiver52M/device/lms/LMSDevice.h
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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_
|
||||
10
Transceiver52M/device/lms/Makefile.am
Normal file
10
Transceiver52M/device/lms/Makefile.am
Normal file
@@ -0,0 +1,10 @@
|
||||
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
|
||||
@@ -18,6 +18,13 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GSMCommon.h"
|
||||
#include "Logger.h"
|
||||
|
||||
extern "C" {
|
||||
#include "config_defs.h"
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
@@ -33,7 +40,7 @@ class RadioDevice {
|
||||
|
||||
public:
|
||||
/* Available transport bus types */
|
||||
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
|
||||
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED, TX_WINDOW_LMS1 };
|
||||
|
||||
/* Radio interface types */
|
||||
enum InterfaceType {
|
||||
@@ -43,14 +50,10 @@ 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);
|
||||
size_t chans = 1, double offset = 0.0,
|
||||
const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
|
||||
const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
|
||||
|
||||
/** Initialize the USRP */
|
||||
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
|
||||
@@ -136,6 +139,24 @@ 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;
|
||||
@@ -143,6 +164,47 @@ 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
|
||||
8
Transceiver52M/device/uhd/Makefile.am
Normal file
8
Transceiver52M/device/uhd/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
10
Transceiver52M/device/usrp1/Makefile.am
Normal file
10
Transceiver52M/device/usrp1/Makefile.am
Normal file
@@ -0,0 +1,10 @@
|
||||
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
|
||||
@@ -27,17 +27,16 @@
|
||||
Compilation Flags
|
||||
|
||||
SWLOOPBACK compile for software loopback testing
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "Logger.h"
|
||||
#include "Threads.h"
|
||||
#include "USRPDevice.h"
|
||||
|
||||
#include <Logger.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
@@ -59,12 +58,15 @@ const dboardConfigType dboardConfig = TXA_RXB;
|
||||
|
||||
const double USRPDevice::masterClockRate = 52.0e6;
|
||||
|
||||
USRPDevice::USRPDevice(size_t sps)
|
||||
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)
|
||||
{
|
||||
LOG(INFO) << "creating USRP device...";
|
||||
LOGC(DDEV, INFO) << "creating USRP device...";
|
||||
|
||||
this->sps = sps;
|
||||
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
|
||||
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
|
||||
actualSampleRate = masterClockRate/decimRate;
|
||||
rxGain = 0;
|
||||
|
||||
@@ -74,14 +76,14 @@ USRPDevice::USRPDevice(size_t sps)
|
||||
* split sample rate Tx/Rx - 4/1 sps we need to need to
|
||||
* compensate for advance rather than delay.
|
||||
*/
|
||||
if (sps == 1)
|
||||
if (tx_sps == 1)
|
||||
pingOffset = 272;
|
||||
else if (sps == 4)
|
||||
else if (tx_sps == 4)
|
||||
pingOffset = 269 - 7500;
|
||||
else
|
||||
pingOffset = 0;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
#ifdef SWLOOPBACK
|
||||
samplePeriod = 1.0e6/actualSampleRate;
|
||||
loopbackBufferSize = 0;
|
||||
gettimeofday(&lastReadTime,NULL);
|
||||
@@ -93,34 +95,32 @@ int USRPDevice::open(const std::string &, int, bool)
|
||||
{
|
||||
writeLock.unlock();
|
||||
|
||||
LOG(INFO) << "opening USRP device..";
|
||||
#ifndef SWLOOPBACK
|
||||
LOGC(DDEV, 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 * sps, 1, -1,
|
||||
0, decimRate * tx_sps, 1, -1,
|
||||
usrp_standard_rx::FPGA_MODE_NORMAL,
|
||||
1024, 16 * 8, rbf));
|
||||
m_uRx->set_fpga_master_clock_freq(masterClockRate);
|
||||
}
|
||||
|
||||
catch(...) {
|
||||
LOG(ALERT) << "make failed on Rx";
|
||||
LOGC(DDEV, ALERT) << "make failed on Rx";
|
||||
m_uRx.reset();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (m_uRx->fpga_master_clock_freq() != masterClockRate)
|
||||
{
|
||||
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
|
||||
LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
|
||||
<< ", desired clock freq = " << masterClockRate;
|
||||
m_uRx.reset();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
|
||||
@@ -130,22 +130,22 @@ int USRPDevice::open(const std::string &, int, bool)
|
||||
}
|
||||
|
||||
catch(...) {
|
||||
LOG(ALERT) << "make failed on Tx";
|
||||
LOGC(DDEV, ALERT) << "make failed on Tx";
|
||||
m_uTx.reset();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (m_uTx->fpga_master_clock_freq() != masterClockRate)
|
||||
{
|
||||
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
|
||||
LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
|
||||
<< ", desired clock freq = " << masterClockRate;
|
||||
m_uTx.reset();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!skipRx) m_uRx->stop();
|
||||
m_uRx->stop();
|
||||
m_uTx->stop();
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
switch (dboardConfig) {
|
||||
@@ -176,20 +176,20 @@ int USRPDevice::open(const std::string &, int, bool)
|
||||
samplesRead = 0;
|
||||
samplesWritten = 0;
|
||||
started = false;
|
||||
|
||||
|
||||
return NORMAL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool USRPDevice::start()
|
||||
bool USRPDevice::start()
|
||||
{
|
||||
LOG(INFO) << "starting USRP...";
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx && !skipRx) return false;
|
||||
LOGC(DDEV, INFO) << "starting USRP...";
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx) return false;
|
||||
if (!m_uTx) return false;
|
||||
|
||||
if (!skipRx) m_uRx->stop();
|
||||
|
||||
m_uRx->stop();
|
||||
m_uTx->stop();
|
||||
|
||||
writeLock.lock();
|
||||
@@ -218,11 +218,8 @@ bool USRPDevice::start()
|
||||
hi32Timestamp = 0;
|
||||
isAligned = false;
|
||||
|
||||
|
||||
if (!skipRx)
|
||||
|
||||
started = (m_uRx->start() && m_uTx->start());
|
||||
else
|
||||
started = m_uTx->start();
|
||||
return started;
|
||||
#else
|
||||
gettimeofday(&lastReadTime,NULL);
|
||||
@@ -230,14 +227,14 @@ bool USRPDevice::start()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool USRPDevice::stop()
|
||||
bool USRPDevice::stop()
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx) return false;
|
||||
if (!m_uTx) return false;
|
||||
|
||||
|
||||
delete[] currData;
|
||||
|
||||
|
||||
started = !(m_uRx->stop() && m_uTx->stop());
|
||||
return !started;
|
||||
#else
|
||||
@@ -258,7 +255,7 @@ double USRPDevice::minTxGain()
|
||||
double USRPDevice::maxRxGain()
|
||||
{
|
||||
return m_dbRx->gain_max();
|
||||
}
|
||||
}
|
||||
|
||||
double USRPDevice::minRxGain()
|
||||
{
|
||||
@@ -268,7 +265,7 @@ double USRPDevice::minRxGain()
|
||||
double USRPDevice::setTxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@@ -278,10 +275,10 @@ double USRPDevice::setTxGain(double dB, size_t chan)
|
||||
if (dB < minTxGain())
|
||||
dB = minTxGain();
|
||||
|
||||
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||
LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||
|
||||
if (!m_dbTx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting TX gain";
|
||||
LOGC(DDEV, ERR) << "Error setting TX gain";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
@@ -292,7 +289,7 @@ double USRPDevice::setTxGain(double dB, size_t chan)
|
||||
double USRPDevice::setRxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@@ -304,38 +301,86 @@ double USRPDevice::setRxGain(double dB, size_t chan)
|
||||
if (dB < minRxGain())
|
||||
dB = minRxGain();
|
||||
|
||||
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
|
||||
LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB.";
|
||||
|
||||
if (!m_dbRx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting RX gain";
|
||||
LOGC(DDEV, 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;
|
||||
@@ -345,21 +390,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;
|
||||
LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
|
||||
LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
|
||||
|
||||
bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
|
||||
if (incrementHi32 && (timeStart!=0)) {
|
||||
LOG(DEBUG) << "high 32 increment!!!";
|
||||
LOGC(DDEV, DEBUG) << "high 32 increment!!!";
|
||||
hi32Timestamp++;
|
||||
}
|
||||
pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
|
||||
@@ -371,24 +416,24 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
|
||||
timestamp -= timestampOffset;
|
||||
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
|
||||
LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
|
||||
LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset;
|
||||
timestamp += timestampOffset;
|
||||
isAligned = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (chan != 0) {
|
||||
LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
|
||||
LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
|
||||
continue;
|
||||
}
|
||||
if ((word0 >> 28) & 0x04) {
|
||||
if (underrun) *underrun = true;
|
||||
LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
|
||||
if (underrun) *underrun = true;
|
||||
LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
|
||||
}
|
||||
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
|
||||
|
||||
|
||||
if (!isAligned) continue;
|
||||
|
||||
|
||||
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
|
||||
while (cursorStart*2 > currDataSize) {
|
||||
cursorStart -= currDataSize/2;
|
||||
@@ -401,25 +446,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);
|
||||
|
||||
LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
|
||||
LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// copy desired data to buf
|
||||
unsigned bufStart = dataStart+(timestamp-timeStart);
|
||||
if (bufStart + len < currDataSize/2) {
|
||||
LOG(DEBUG) << "bufStart: " << bufStart;
|
||||
if (bufStart + len < currDataSize/2) {
|
||||
LOGC(DDEV, DEBUG) << "bufStart: " << bufStart;
|
||||
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
|
||||
memset(data+bufStart*2,0,len*2*sizeof(short));
|
||||
}
|
||||
else {
|
||||
LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
|
||||
LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
|
||||
unsigned firstLength = (currDataSize/2-bufStart);
|
||||
LOG(DEBUG) << "firstLength: " << firstLength;
|
||||
LOGC(DDEV, 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));
|
||||
@@ -429,21 +474,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
timeStart = timestamp + len;
|
||||
|
||||
return len;
|
||||
|
||||
|
||||
#else
|
||||
if (loopbackBufferSize < 2) return 0;
|
||||
int numSamples = 0;
|
||||
struct timeval currTime;
|
||||
gettimeofday(&currTime,NULL);
|
||||
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
|
||||
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
|
||||
(currTime.tv_usec - lastReadTime.tv_usec);
|
||||
if (timeElapsed < samplePeriod) {return 0;}
|
||||
int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
|
||||
if (numSamplesToRead < len) return 0;
|
||||
|
||||
|
||||
if (numSamplesToRead > len) numSamplesToRead = len;
|
||||
if (numSamplesToRead > loopbackBufferSize/2) {
|
||||
firstRead =false;
|
||||
firstRead =false;
|
||||
numSamplesToRead = loopbackBufferSize/2;
|
||||
}
|
||||
memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
|
||||
@@ -461,7 +506,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
firstRead = true;
|
||||
}
|
||||
samplesRead += numSamples;
|
||||
|
||||
|
||||
return numSamples;
|
||||
#endif
|
||||
}
|
||||
@@ -472,7 +517,7 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
{
|
||||
writeLock.lock();
|
||||
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uTx)
|
||||
return 0;
|
||||
|
||||
@@ -519,14 +564,14 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
|
||||
samplesWritten += retVal;
|
||||
loopbackBufferSize += retVal*2;
|
||||
|
||||
|
||||
return retVal;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
short data[] = {0x00,0x02,0x00,0x00};
|
||||
uint32_t *wordPtr = (uint32_t *) data;
|
||||
*wordPtr = host_to_usrp_u32(*wordPtr);
|
||||
@@ -543,25 +588,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) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
|
||||
LOG(INFO) << "set TX: " << wFreq << std::endl
|
||||
LOGC(DDEV, 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 {
|
||||
LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl
|
||||
LOGC(DDEV, 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;
|
||||
@@ -574,19 +619,19 @@ bool USRPDevice::setRxFreq(double wFreq, size_t chan)
|
||||
usrp_tune_result result;
|
||||
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
|
||||
LOG(INFO) << "set RX: " << wFreq << std::endl
|
||||
LOGC(DDEV, 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 {
|
||||
LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl
|
||||
LOGC(DDEV, 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;
|
||||
@@ -601,7 +646,21 @@ bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
||||
#endif
|
||||
|
||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||
size_t chans, double)
|
||||
InterfaceType iface, size_t chans, double lo_offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths)
|
||||
{
|
||||
return new USRPDevice(tx_sps);
|
||||
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);
|
||||
}
|
||||
@@ -48,7 +48,6 @@ 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
|
||||
|
||||
@@ -56,7 +55,6 @@ private:
|
||||
unsigned long long samplesWritten; ///< number of samples sent to USRP
|
||||
|
||||
bool started; ///< flag indicates USRP has started
|
||||
bool skipRx; ///< set if USRP is transmit-only.
|
||||
|
||||
static const unsigned int currDataSize_log2 = 21;
|
||||
static const unsigned long currDataSize = (1 << currDataSize_log2);
|
||||
@@ -83,10 +81,10 @@ private:
|
||||
|
||||
double rxGain;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
#ifdef SWLOOPBACK
|
||||
short loopbackBuffer[1000000];
|
||||
int loopbackBufferSize;
|
||||
double samplePeriod;
|
||||
double samplePeriod;
|
||||
|
||||
struct timeval startTime;
|
||||
struct timeval lastReadTime;
|
||||
@@ -96,7 +94,9 @@ private:
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
USRPDevice(size_t sps);
|
||||
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);
|
||||
|
||||
/** Instantiate the USRP */
|
||||
int open(const std::string &, int, bool);
|
||||
@@ -179,14 +179,30 @@ 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_
|
||||
|
||||
@@ -1,520 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "sigProcLib.h"
|
||||
#include "signalVector.h"
|
||||
#include "Transceiver.h"
|
||||
#include "Configuration.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convolve.h"
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
#define DEFAULT_RX_SPS 1
|
||||
#define DEFAULT_SEARCH_WINDOW 30
|
||||
|
||||
// Tail + data + stealing + midamble + guard (without the last 0.25)
|
||||
#define BURST_LEN_FULL 156
|
||||
// Tail + data + stealing + midamble
|
||||
#define BURST_LEN_ACTIVE 148
|
||||
// Tail + data + stealing + midamble - 2*0.5
|
||||
#define BURST_LEN_USEFUL 147
|
||||
|
||||
// Size of a sample in bytes as stores in a file
|
||||
#define SAMPLE_SIZE_BYTES (2 * sizeof(float))
|
||||
// Burst length in bytes as stored in a file
|
||||
#define BURST_LEN_BYTES (BURST_LEN_FULL * SAMPLE_SIZE_BYTES)
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
struct trx_config {
|
||||
std::string log_level;
|
||||
unsigned sps;
|
||||
unsigned tsc;
|
||||
unsigned max_expected_delay_nb;
|
||||
unsigned max_expected_delay_ab;
|
||||
double full_scale;
|
||||
bool edge;
|
||||
CorrType type;
|
||||
std::string filename;
|
||||
unsigned ber_burst_avg; ///< Average BER over this many bursts.
|
||||
///< Set to 0 to average for the whole duration.
|
||||
};
|
||||
|
||||
class NormalBurstSoftbitMask {
|
||||
public:
|
||||
NormalBurstSoftbitMask(SoftVector &softBits)
|
||||
: mSoftBits(softBits)
|
||||
{
|
||||
}
|
||||
|
||||
SoftVector &bits() { return mSoftBits; }
|
||||
SoftVector tailBitsL() { return mSoftBits.segment(0,3); }
|
||||
SoftVector dataBitsL() { return mSoftBits.segment(3,57); }
|
||||
SoftVector stealingBitsL() { return mSoftBits.segment(60, 1); }
|
||||
SoftVector midambleBits() { return mSoftBits.segment(61, 26); }
|
||||
SoftVector stealingBitsR() { return mSoftBits.segment(87, 1); }
|
||||
SoftVector dataBitsR() { return mSoftBits.segment(88,57); }
|
||||
SoftVector tailBitsR() { return mSoftBits.segment(145,3); }
|
||||
SoftVector guardBits() { return mSoftBits.segment(148,8); }
|
||||
|
||||
protected:
|
||||
SoftVector &mSoftBits;
|
||||
};
|
||||
|
||||
class SoftBurst {
|
||||
public:
|
||||
SoftBurst(SoftVector *softBits, double toa=0)
|
||||
: mSoftBits(softBits), mTOA(toa)
|
||||
{
|
||||
assert(mSoftBits != NULL);
|
||||
}
|
||||
|
||||
~SoftBurst()
|
||||
{
|
||||
delete mSoftBits;
|
||||
}
|
||||
|
||||
void TOA(double TOA) { mTOA = TOA; }
|
||||
double TOA() { return mTOA; }
|
||||
|
||||
NormalBurstSoftbitMask normalBurstMask() { return NormalBurstSoftbitMask(*mSoftBits); }
|
||||
|
||||
protected:
|
||||
SoftVector *mSoftBits;
|
||||
double mTOA;
|
||||
};
|
||||
|
||||
class BEREstimator {
|
||||
public:
|
||||
BEREstimator(const PRBS& prbs)
|
||||
: mPRBS(prbs), mTotalBits(0), mErrorBits(0), mSynchronized(false)
|
||||
{}
|
||||
|
||||
unsigned synchronize(const BitVector &bits)
|
||||
{
|
||||
for (unsigned i=0; i<mPRBS.size(); i++) {
|
||||
mPRBS.processBit(bits[i]);
|
||||
}
|
||||
mSynchronized = true;
|
||||
return mPRBS.size();
|
||||
}
|
||||
|
||||
void process(const BitVector &bits, size_t start_from = 0)
|
||||
{
|
||||
for (size_t i=start_from; i<bits.size(); i++) {
|
||||
mTotalBits++;
|
||||
if (mPRBS.generateBit() != bits.bit(i)) {
|
||||
mErrorBits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sync_and_process(const BitVector &bits)
|
||||
{
|
||||
unsigned skip = 0;
|
||||
if (!mSynchronized) {
|
||||
skip = synchronize(bits);
|
||||
}
|
||||
|
||||
process(bits, skip);
|
||||
}
|
||||
|
||||
void skip(size_t num)
|
||||
{
|
||||
for (size_t i=0; i<num; i++) {
|
||||
mTotalBits++;
|
||||
mErrorBits++;
|
||||
mPRBS.generateBit();
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
mTotalBits = 0;
|
||||
mErrorBits = 0;
|
||||
}
|
||||
|
||||
unsigned totalBits() const { return mTotalBits; }
|
||||
unsigned errorBits() const { return mErrorBits; }
|
||||
double BER() const { return mErrorBits/(double)mTotalBits; }
|
||||
|
||||
bool isSynchronized() const {return mSynchronized; }
|
||||
|
||||
protected:
|
||||
PRBS mPRBS;
|
||||
unsigned mTotalBits;
|
||||
unsigned mErrorBits;
|
||||
bool mSynchronized;
|
||||
};
|
||||
|
||||
double getBurstRSSI(const signalVector &burst, unsigned sps, double full_scale)
|
||||
{
|
||||
/* Calculate average power of the burst */
|
||||
float avg = energyDetect(burst, 20 * sps);
|
||||
return 20.0 * log10(sqrt(avg) / full_scale);
|
||||
}
|
||||
|
||||
void printDetectionResult(int rc)
|
||||
{
|
||||
if (rc > 0) {
|
||||
std::cout << "Detected correlation type: " << (CorrType)rc << std::endl;
|
||||
} else {
|
||||
if (rc == -SIGERR_CLIP) {
|
||||
std::cout << "Clipping detected on received RACH or Normal Burst" << std::endl;
|
||||
} else if (rc != SIGERR_NONE) {
|
||||
std::cout << "Unhandled RACH or Normal Burst detection error" << std::endl;
|
||||
} else {
|
||||
// std::cout << "No burst detected" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoftVector *demodulateBurst(const signalVector &burst,
|
||||
CorrType expected_type,
|
||||
unsigned sps, unsigned tsc,
|
||||
unsigned max_expected_delay,
|
||||
double &timingOffset)
|
||||
{
|
||||
complex amp;
|
||||
float toa;
|
||||
int rc;
|
||||
CorrType detected_type;
|
||||
|
||||
/* Detect normal or RACH bursts */
|
||||
rc = detectAnyBurst(burst, tsc, BURST_THRESH, sps, expected_type, amp, toa,
|
||||
max_expected_delay);
|
||||
printDetectionResult(rc);
|
||||
if (rc <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Convert samples to symbols
|
||||
timingOffset = toa / sps;
|
||||
// rc > 0 means it's a detected CorrType
|
||||
detected_type = (CorrType)rc;
|
||||
|
||||
return demodAnyBurst(burst, sps, amp, toa, detected_type);
|
||||
}
|
||||
|
||||
static bool processBurst(const trx_config &config, signalVector &burst,
|
||||
unsigned max_expected_delay,
|
||||
double &RSSI,
|
||||
double &timingOffset,
|
||||
BEREstimator &berEstimator)
|
||||
{
|
||||
RSSI = getBurstRSSI(burst, config.sps, config.full_scale);
|
||||
SoftVector *softBits = demodulateBurst(burst, config.type, config.sps,config.tsc,
|
||||
max_expected_delay, timingOffset);
|
||||
|
||||
/* Print burst information and content */
|
||||
if (softBits == NULL) {
|
||||
std::cout << "Skipped frame" << std::endl;
|
||||
// TODO: This is different for EDGE
|
||||
berEstimator.skip(57*2);
|
||||
return false;
|
||||
}
|
||||
|
||||
SoftBurst softBurst(softBits, timingOffset);
|
||||
NormalBurstSoftbitMask nb = softBurst.normalBurstMask();
|
||||
|
||||
berEstimator.sync_and_process(nb.dataBitsL().sliced());
|
||||
berEstimator.sync_and_process(nb.dataBitsR().sliced());
|
||||
|
||||
std::cout << "TOA: " << softBurst.TOA() << " symbols" << std::endl;
|
||||
// Exclude tail and guard bits from the energy calculation
|
||||
std::cout << "Energy: " << softBits->segment(3,142).getEnergy() << std::endl;
|
||||
//std::cout << "Demodulated burst: " << *softBits << std::endl;
|
||||
std::cout << " tail|--------------------------data---------------------------|f|--------midamble----------|f|--------------------------data---------------------------|tai|-guard--" << std::endl;
|
||||
// " 000 010001011011110011101001100100000001010001011000100100010 0 11101111000100101110111100 0 011010111011101010011010111000101100001110101011011001011 000 1''..---"
|
||||
std::cout << "Demodulated burst:"
|
||||
<< " " << nb.tailBitsL()
|
||||
<< " " << nb.dataBitsL()
|
||||
<< " " << nb.stealingBitsL()
|
||||
<< " " << nb.midambleBits()
|
||||
<< " " << nb.stealingBitsR()
|
||||
<< " " << nb.dataBitsR()
|
||||
<< " " << nb.tailBitsR()
|
||||
<< " " << nb.guardBits()
|
||||
<< std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Setup configuration values
|
||||
static void print_config(struct trx_config *config)
|
||||
{
|
||||
std::ostringstream ost("");
|
||||
ost << "Config Settings" << std::endl;
|
||||
ost << " Source file name............. " << config->filename << std::endl;
|
||||
ost << " Log Level.................... " << config->log_level << std::endl;
|
||||
ost << " Rx Samples-per-Symbol........ " << config->sps << std::endl;
|
||||
ost << " EDGE support................. " << (config->edge ? "Enabled" : "Disabled") << std::endl;
|
||||
ost << " Burst type................... " << config->type << std::endl;
|
||||
ost << " Burst TSC.................... " << config->tsc << std::endl;
|
||||
ost << " Normal Burst search window... " << config->max_expected_delay_nb << std::endl;
|
||||
ost << " Access Burst search window... " << config->max_expected_delay_ab << std::endl;
|
||||
ost << " Signal full scale............ " << config->full_scale << std::endl;
|
||||
ost << " BER average window (bursts).. " << config->ber_burst_avg << std::endl;
|
||||
std::cout << ost << std::endl;
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
fprintf(stdout, "Options:\n"
|
||||
" -h This text\n"
|
||||
" -l LEVEL Logging level (%s)\n"
|
||||
" -e Enable EDGE receiver\n"
|
||||
" -s SPS Samples-per-symbol (1 or 4, default: %d)\n"
|
||||
" -t TSC Burst training sequence (0 to 7, default: 0)\n"
|
||||
" -f FILE File to read\n"
|
||||
" -w SYMBOLS Normal Burst search window (0 to 156, default: %d)\n"
|
||||
" -W SYMBOLS Access Burst search window (0 to 156, default: %d)\n"
|
||||
" -b BURSTS BER average window. Set to 0 to average over the whole file (default: 1)\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG",
|
||||
DEFAULT_RX_SPS,
|
||||
DEFAULT_SEARCH_WINDOW, DEFAULT_SEARCH_WINDOW);
|
||||
}
|
||||
|
||||
static bool handle_options(int argc, char **argv, struct trx_config *config)
|
||||
{
|
||||
int option;
|
||||
|
||||
config->log_level = "NOTICE";
|
||||
config->sps = DEFAULT_RX_SPS;
|
||||
config->tsc = 0;
|
||||
config->max_expected_delay_nb = DEFAULT_SEARCH_WINDOW;
|
||||
config->max_expected_delay_ab = DEFAULT_SEARCH_WINDOW;
|
||||
config->full_scale = SHRT_MAX;
|
||||
config->edge = false;
|
||||
config->type = TSC;
|
||||
config->ber_burst_avg = 1;
|
||||
|
||||
while ((option = getopt(argc, argv, "ls:et:f:w:W:b:h")) != -1) {
|
||||
switch (option) {
|
||||
case 'l':
|
||||
config->log_level = optarg;
|
||||
break;
|
||||
case 's':
|
||||
config->sps = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
config->edge = true;
|
||||
break;
|
||||
case 't':
|
||||
config->tsc = atoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
config->filename = optarg;
|
||||
break;
|
||||
case 'w':
|
||||
config->max_expected_delay_nb = atoi(optarg);
|
||||
break;
|
||||
case 'W':
|
||||
config->max_expected_delay_ab = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
config->ber_burst_avg = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((config->sps != 1) && (config->sps != 4)) {
|
||||
printf("ERROR: Unsupported samples-per-symbol %i\n\n", config->sps);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->edge && (config->sps != 4)) {
|
||||
printf("ERROR: EDGE only supported at 4 samples per symbol\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->tsc > 7) {
|
||||
printf("ERROR: Invalid training sequence %i\n\n", config->tsc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->filename.length() == 0) {
|
||||
printf("ERROR: No input file specified\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->max_expected_delay_nb > 156 || config->max_expected_delay_nb < 0 ||
|
||||
config->max_expected_delay_ab > 156 || config->max_expected_delay_ab < 0) {
|
||||
printf("ERROR: Invalid search window size, must be withit [1..156] range\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct trx_config config;
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
printf("Info: SSE3 support compiled in");
|
||||
if (__builtin_cpu_supports("sse3"))
|
||||
printf(" and supported by CPU\n");
|
||||
else
|
||||
printf(", but not supported by CPU\n");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE4_1
|
||||
printf("Info: SSE4.1 support compiled in");
|
||||
if (__builtin_cpu_supports("sse4.1"))
|
||||
printf(" and supported by CPU\n");
|
||||
else
|
||||
printf(", but not supported by CPU\n");
|
||||
#endif
|
||||
|
||||
convolve_init();
|
||||
convert_init();
|
||||
|
||||
// Process command line options and print config to screen
|
||||
if (!handle_options(argc, argv, &config)) {
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
print_config(&config);
|
||||
|
||||
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
|
||||
|
||||
if (!sigProcLibSetup()) {
|
||||
LOG(ALERT) << "Failed to initialize signal processing library";
|
||||
return -1;
|
||||
}
|
||||
|
||||
double RSSI;
|
||||
double timingOffset, timingOffsetPrev = 0.0;
|
||||
signalVector burst(2*BURST_LEN_FULL);
|
||||
GSM::Time gsmTime;
|
||||
bool syncedTo157bits = false; // We should syncronize to 156-157 frame structure only once
|
||||
bool burst156_157 = false; // Set to true to enable 156-156-156-157 frame
|
||||
int bitsReadExtra = 0; // set to 1 every 4 bursts and when TOA>1.0
|
||||
int bitsToSkip = 0; // set to 1 when TOA<0.0
|
||||
unsigned berBurstsAveraged = 0;
|
||||
PRBS9 prbs;
|
||||
BEREstimator berEstimator(prbs);
|
||||
|
||||
// Configure output stream
|
||||
std::cout << std::fixed;
|
||||
std::cout << std::setprecision(2);
|
||||
|
||||
std::ifstream file (config.filename.c_str(), std::ifstream::binary);
|
||||
|
||||
// Read the first burst, but do not process it, because we need at least two bursts
|
||||
// worth of data for reliable initial detection.
|
||||
file.read((char*)burst.begin(), config.sps * BURST_LEN_BYTES);
|
||||
{signalVector t = burst.segment(0, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
|
||||
|
||||
#if 0
|
||||
/* Distort signal */
|
||||
{
|
||||
signalVector burst_read = burst.segment(85,156);
|
||||
std::ifstream file (config.filename.c_str(), std::ifstream::binary);
|
||||
file.read((char*)burst_read.begin(), burst_read.size() * 2 * sizeof(float));
|
||||
file.close();
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
// Read more data and try burst detection until successful
|
||||
while(file.read((char*)(burst.begin()+config.sps*BURST_LEN_FULL), config.sps*BURST_LEN_BYTES))
|
||||
{
|
||||
{signalVector t = burst.segment(BURST_LEN_FULL, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
|
||||
bool found = processBurst(config, burst, BURST_LEN_FULL, RSSI, timingOffset, berEstimator);
|
||||
std::cout << "RSSI: " << RSSI << " dBFS" << std::endl;
|
||||
if (found) {
|
||||
gsmTime.incTN();
|
||||
berBurstsAveraged++;
|
||||
break;
|
||||
}
|
||||
burst.segmentMove(config.sps*BURST_LEN_FULL, 0, config.sps*BURST_LEN_FULL);
|
||||
}
|
||||
|
||||
// Align stream to burst
|
||||
int offsetInt = (int)timingOffset;
|
||||
burst.segmentMove(config.sps*(BURST_LEN_FULL+offsetInt), 0, config.sps*(BURST_LEN_FULL-offsetInt));
|
||||
{signalVector t = burst.segment(0, BURST_LEN_FULL-offsetInt); scaleVector(t, complex(1.0/SHRT_MAX)); }
|
||||
file.read((char*)(burst.begin()+config.sps*(BURST_LEN_FULL-offsetInt)), config.sps*offsetInt*SAMPLE_SIZE_BYTES);
|
||||
#endif
|
||||
|
||||
// Resize burst vector to hold only one burst, because demodulation code
|
||||
// always decode the full vector size.
|
||||
burst.shrink(BURST_LEN_FULL+1);
|
||||
|
||||
// Process the rest of the stream
|
||||
do {
|
||||
{signalVector t = burst.segment(0, BURST_LEN_FULL); scaleVector(t, complex(SHRT_MAX)); }
|
||||
processBurst(config, burst, (config.type==RACH)?config.max_expected_delay_ab:config.max_expected_delay_ab,
|
||||
RSSI, timingOffset, berEstimator);
|
||||
if (burst156_157 && !syncedTo157bits && timingOffset - timingOffsetPrev > .75) {
|
||||
std::cout << "TOA adjust: Found a 157-bit burst, reset TN to mark it" << std::endl;
|
||||
gsmTime.TN(2);
|
||||
timingOffset -= 1.0;
|
||||
// Make sure we do this adjustment only once.
|
||||
syncedTo157bits = true;
|
||||
} else {
|
||||
gsmTime.incTN();
|
||||
}
|
||||
bitsToSkip = 0;
|
||||
bitsReadExtra = 0;
|
||||
if (timingOffset < 0.0) {
|
||||
std::cout << "TOA adjust: skip a bit" << std::endl;
|
||||
burst[0] = 0;
|
||||
bitsToSkip = 1;
|
||||
bitsReadExtra--;
|
||||
}
|
||||
bitsReadExtra += (gsmTime.TN()%4 == 0);
|
||||
if (timingOffset > 1.1) {
|
||||
std::cout << "TOA adjust: add extra bit" << std::endl;
|
||||
bitsReadExtra++;
|
||||
}
|
||||
std::cout << "Clock: " << gsmTime;
|
||||
std::cout << " RSSI: " << RSSI << " dBFS";
|
||||
std::cout << " Error bits: " << berEstimator.errorBits() << " Total bits: " << berEstimator.totalBits()
|
||||
<< " BER: " << 100.0*berEstimator.errorBits() / berEstimator.totalBits() << "%" << std::endl;
|
||||
berBurstsAveraged++;
|
||||
// Never reset if config.ber_burst_avg is 0
|
||||
if (config.ber_burst_avg > 0 && berBurstsAveraged >= config.ber_burst_avg) {
|
||||
berBurstsAveraged = 0;
|
||||
berEstimator.reset();
|
||||
}
|
||||
std::cout << "bitsReadExtra: " << bitsReadExtra << " bitsToSkip: " << bitsToSkip << std::endl;
|
||||
timingOffsetPrev = timingOffset;
|
||||
} while(file.read((char*)(burst.begin()+bitsToSkip), config.sps*(BURST_LEN_BYTES+SAMPLE_SIZE_BYTES*bitsReadExtra)));
|
||||
|
||||
std::cout << "End of file reached" << std::endl;
|
||||
file.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <endian.h> // for byte order manipulation
|
||||
|
||||
#include "Logger.h"
|
||||
#include "sigProcLib.h"
|
||||
#include "GSMCommon.h"
|
||||
#include "BitVector.h"
|
||||
#include "Configuration.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convolve.h"
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
#define DEFAULT_SPS 4
|
||||
#define DEFAULT_SEARCH_WINDOW 30
|
||||
|
||||
// Tail + data + stealing + midamble + guard (without the last 0.25)
|
||||
#define BURST_LEN_FULL 156
|
||||
// Tail + data + stealing + midamble
|
||||
#define BURST_LEN_ACTIVE 148
|
||||
// Tail + data + stealing + midamble - 2*0.5
|
||||
#define BURST_LEN_USEFUL 147
|
||||
|
||||
// Size of a sample in bytes as stores in a file
|
||||
#define SAMPLE_SIZE_BYTES (2 * sizeof(float))
|
||||
// Burst length in bytes as stored in a file
|
||||
#define BURST_LEN_BYTES (BURST_LEN_FULL * SAMPLE_SIZE_BYTES)
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
enum FileType {
|
||||
FLOAT_NORM_LE, ///< Float -1..+1 Little Endian
|
||||
FLOAT16_LE, ///< Float -32767..+32767 Little Endian
|
||||
SIGNED16_LE, ///< Integer -32767..+32767 Little Endian
|
||||
SIGNED16_BE, ///< Integer -32767..+32767 Big Endian (Keysight waveform format)
|
||||
};
|
||||
|
||||
struct trx_config {
|
||||
std::string log_level;
|
||||
unsigned sps;
|
||||
unsigned tsc;
|
||||
double full_scale;
|
||||
bool edge;
|
||||
CorrType type;
|
||||
std::string filename;
|
||||
FileType file_type;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, FileType ftype)
|
||||
{
|
||||
switch(ftype)
|
||||
{
|
||||
case FLOAT_NORM_LE:
|
||||
os << "float";
|
||||
break;
|
||||
case FLOAT16_LE:
|
||||
os << "float16";
|
||||
break;
|
||||
case SIGNED16_LE:
|
||||
os << "signed16";
|
||||
break;
|
||||
case SIGNED16_BE:
|
||||
os << "signed16be";
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown file type");
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
void writeBurstFloatNorm(std::ofstream& os, const signalVector& v)
|
||||
{
|
||||
os.write((char*)v.begin(), v.size() * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
void writeBurstFloat16LE(std::ofstream& os, const signalVector& v)
|
||||
{
|
||||
const complex *c = v.begin();
|
||||
for (size_t i=0; i<v.size(); i++, c++) {
|
||||
float iq[2];
|
||||
iq[0] = c->real()*SHRT_MAX;
|
||||
iq[1] = c->imag()*SHRT_MAX;
|
||||
os.write((char*)&iq, 2*sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
void writeBurstSigned16LE(std::ofstream& os, const signalVector& v)
|
||||
{
|
||||
const complex *c = v.begin();
|
||||
for (size_t i=0; i<v.size(); i++, c++) {
|
||||
int16_t iq[2];
|
||||
iq[0] = c->real()*SHRT_MAX;
|
||||
iq[1] = c->imag()*SHRT_MAX;
|
||||
iq[0] = htole16(iq[0]);
|
||||
iq[1] = htole16(iq[1]);
|
||||
os.write((char*)&iq, 2*sizeof(int16_t));
|
||||
}
|
||||
}
|
||||
|
||||
void writeBurstSigned16BE(std::ofstream& os, const signalVector& v)
|
||||
{
|
||||
const complex *c = v.begin();
|
||||
for (size_t i=0; i<v.size(); i++, c++) {
|
||||
int16_t iq[2];
|
||||
iq[0] = c->real()*SHRT_MAX;
|
||||
iq[1] = c->imag()*SHRT_MAX;
|
||||
iq[0] = htobe16(iq[0]);
|
||||
iq[1] = htobe16(iq[1]);
|
||||
os.write((char*)&iq, 2*sizeof(int16_t));
|
||||
}
|
||||
}
|
||||
|
||||
void writeBurst(std::ofstream& os, const signalVector& v, FileType ftype)
|
||||
{
|
||||
switch(ftype)
|
||||
{
|
||||
case FLOAT_NORM_LE:
|
||||
writeBurstFloatNorm(os, v);
|
||||
break;
|
||||
case FLOAT16_LE:
|
||||
writeBurstFloat16LE(os, v);
|
||||
break;
|
||||
case SIGNED16_LE:
|
||||
writeBurstSigned16LE(os, v);
|
||||
break;
|
||||
case SIGNED16_BE:
|
||||
writeBurstSigned16BE(os, v);
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown file type");
|
||||
}
|
||||
}
|
||||
|
||||
// Setup configuration values
|
||||
static void print_config(struct trx_config *config)
|
||||
{
|
||||
std::ostringstream ost("");
|
||||
ost << "Config Settings" << std::endl;
|
||||
ost << " Destination file name........ " << config->filename << std::endl;
|
||||
ost << " Destination file type........ " << config->file_type << std::endl;
|
||||
ost << " Log Level.................... " << config->log_level << std::endl;
|
||||
ost << " Tx Samples-per-Symbol........ " << config->sps << std::endl;
|
||||
ost << " EDGE support................. " << (config->edge ? "Enabled" : "Disabled") << std::endl;
|
||||
ost << " Burst type................... " << config->type << std::endl;
|
||||
ost << " Burst TSC.................... " << config->tsc << std::endl;
|
||||
ost << " Signal full scale............ " << config->full_scale << std::endl;
|
||||
std::cout << ost << std::endl;
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
fprintf(stdout,
|
||||
"This utility generates waveform files aka IQ binary files in a number of formats"
|
||||
"to use them as input to osmo-trx-dec or load them into signal generators.\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -h This text\n"
|
||||
" -l LEVEL Logging level (%s)\n"
|
||||
" -e Enable EDGE receiver\n"
|
||||
" -s SPS Samples-per-symbol (1 or 4, default: %d)\n"
|
||||
" -t TSC Burst training sequence (0 to 7, default: 0)\n"
|
||||
" -f FILE File to write generated bursts to\n"
|
||||
" -F FILETYPE Format of the file - float, float16, signed16, signed16be (default: f16)\n"
|
||||
" Note: Keysight waveform format is signed16be. osmo-trx-dec accepts float16.\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG",
|
||||
DEFAULT_SPS);
|
||||
}
|
||||
|
||||
FileType option_to_file_type(const std::string &optarg)
|
||||
{
|
||||
if (optarg == "float") {
|
||||
return FLOAT_NORM_LE;
|
||||
} else if (optarg == "float16") {
|
||||
return FLOAT16_LE;
|
||||
} else if (optarg == "signed16") {
|
||||
return SIGNED16_LE;
|
||||
} else if (optarg == "signed16be") {
|
||||
return SIGNED16_BE;
|
||||
} else {
|
||||
return (FileType)-1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool handle_options(int argc, char **argv, struct trx_config *config)
|
||||
{
|
||||
int option;
|
||||
|
||||
config->log_level = "NOTICE";
|
||||
config->sps = DEFAULT_SPS;
|
||||
config->tsc = 0;
|
||||
config->full_scale = SHRT_MAX;
|
||||
config->edge = false;
|
||||
config->type = TSC;
|
||||
config->file_type = FLOAT16_LE;
|
||||
|
||||
while ((option = getopt(argc, argv, "ls:et:f:F:h")) != -1) {
|
||||
switch (option) {
|
||||
case 'l':
|
||||
config->log_level = optarg;
|
||||
break;
|
||||
case 's':
|
||||
config->sps = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
config->edge = true;
|
||||
break;
|
||||
case 't':
|
||||
config->tsc = atoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
config->filename = optarg;
|
||||
break;
|
||||
case 'F':
|
||||
config->file_type = option_to_file_type(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((config->sps != 1) && (config->sps != 4)) {
|
||||
printf("ERROR: Unsupported samples-per-symbol %i\n\n", config->sps);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->edge && (config->sps != 4)) {
|
||||
printf("ERROR: EDGE only supported at 4 samples per symbol\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->tsc > 7) {
|
||||
printf("ERROR: Invalid training sequence %i\n\n", config->tsc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->filename.length() == 0) {
|
||||
printf("ERROR: No output file name specified\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->file_type < 0) {
|
||||
printf("ERROR: Wrong output file format\n\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct trx_config config;
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
printf("Info: SSE3 support compiled in");
|
||||
if (__builtin_cpu_supports("sse3"))
|
||||
printf(" and supported by CPU\n");
|
||||
else
|
||||
printf(", but not supported by CPU\n");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE4_1
|
||||
printf("Info: SSE4.1 support compiled in");
|
||||
if (__builtin_cpu_supports("sse4.1"))
|
||||
printf(" and supported by CPU\n");
|
||||
else
|
||||
printf(", but not supported by CPU\n");
|
||||
#endif
|
||||
|
||||
convolve_init();
|
||||
convert_init();
|
||||
|
||||
// Process command line options and print config to screen
|
||||
if (!handle_options(argc, argv, &config)) {
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
print_config(&config);
|
||||
|
||||
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
|
||||
|
||||
if (!sigProcLibSetup()) {
|
||||
LOG(ALERT) << "Failed to initialize signal processing library";
|
||||
return -1;
|
||||
}
|
||||
|
||||
signalVector burst(2*BURST_LEN_FULL);
|
||||
GSM::Time gsmTime;
|
||||
PRBS9 prbs;
|
||||
|
||||
// Configure output stream
|
||||
std::cout << std::fixed;
|
||||
std::cout << std::setprecision(2);
|
||||
|
||||
std::ofstream file (config.filename.c_str(), std::ifstream::binary);
|
||||
|
||||
for (int i=0; i<511; i++) {
|
||||
signalVector *signal = genRandNormalBurst(config.tsc, config.sps, gsmTime.TN(), prbs);
|
||||
writeBurst(file, *signal, config.file_type);
|
||||
gsmTime.incTN();
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
std::cout << "Done!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -22,131 +22,59 @@
|
||||
|
||||
#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"
|
||||
}
|
||||
|
||||
/* 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 DEFAULT_CONFIG_FILE "osmo-trx.cfg"
|
||||
|
||||
/*
|
||||
* 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
|
||||
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
|
||||
|
||||
/* 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;
|
||||
static char* config_file = (char*)DEFAULT_CONFIG_FILE;
|
||||
|
||||
volatile bool gshutdown = false;
|
||||
|
||||
/* Setup configuration values
|
||||
* Don't query the existence of the Log.Level because it's a
|
||||
* mandatory value. That is, if it doesn't exist, the configuration
|
||||
* table will crash or will have already crashed. Everything else we
|
||||
* can survive without and use default values if the database entries
|
||||
* are empty.
|
||||
*/
|
||||
bool trx_setup_config(struct trx_config *config)
|
||||
{
|
||||
std::string refstr, fillstr, divstr, mcstr, edgestr;
|
||||
static void *tall_trx_ctx;
|
||||
static struct trx_ctx *g_trx_ctx;
|
||||
static struct ctrl_handle *g_ctrlh;
|
||||
|
||||
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;
|
||||
}
|
||||
static RadioDevice *usrp;
|
||||
static RadioInterface *radio;
|
||||
static Transceiver *transceiver;
|
||||
|
||||
/* Create radio interface
|
||||
* The interface consists of sample rate changes, frequency shifts,
|
||||
@@ -155,24 +83,24 @@ bool trx_setup_config(struct trx_config *config)
|
||||
* The radio interface connects the main transceiver with the device
|
||||
* object, which may be operating some other rate.
|
||||
*/
|
||||
RadioInterface *makeRadioInterface(struct trx_config *config,
|
||||
RadioInterface *makeRadioInterface(struct trx_ctx *trx,
|
||||
RadioDevice *usrp, int type)
|
||||
{
|
||||
RadioInterface *radio = NULL;
|
||||
|
||||
switch (type) {
|
||||
case RadioDevice::NORMAL:
|
||||
radio = new RadioInterface(usrp, config->tx_sps,
|
||||
config->rx_sps, config->chans);
|
||||
radio = new RadioInterface(usrp, trx->cfg.tx_sps,
|
||||
trx->cfg.rx_sps, trx->cfg.num_chans);
|
||||
break;
|
||||
case RadioDevice::RESAMP_64M:
|
||||
case RadioDevice::RESAMP_100M:
|
||||
radio = new RadioInterfaceResamp(usrp, config->tx_sps,
|
||||
config->rx_sps);
|
||||
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
|
||||
trx->cfg.rx_sps);
|
||||
break;
|
||||
case RadioDevice::MULTI_ARFCN:
|
||||
radio = new RadioInterfaceMulti(usrp, config->tx_sps,
|
||||
config->rx_sps, config->chans);
|
||||
radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
|
||||
trx->cfg.rx_sps, trx->cfg.num_chans);
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported radio interface configuration";
|
||||
@@ -187,203 +115,243 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
|
||||
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.
|
||||
*/
|
||||
Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
|
||||
int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
|
||||
{
|
||||
Transceiver *trx;
|
||||
VectorFIFO *fifo;
|
||||
|
||||
trx = new Transceiver(config->port, config->addr.c_str(),
|
||||
config->tx_sps, config->rx_sps, config->chans,
|
||||
GSM::Time(3,0), radio, config->rssi_offset);
|
||||
if (!trx->init(config->filler, config->rtsc,
|
||||
config->rach_delay, config->edge)) {
|
||||
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)) {
|
||||
LOG(ALERT) << "Failed to initialize transceiver";
|
||||
delete trx;
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < config->chans; i++) {
|
||||
transceiver->setSignalHandler(transc_sig_cb);
|
||||
|
||||
for (size_t i = 0; i < trx->cfg.num_chans; i++) {
|
||||
fifo = radio->receiveFIFO(i);
|
||||
if (fifo && trx->receiveFIFO(fifo, i))
|
||||
if (fifo && transceiver->receiveFIFO(fifo, i))
|
||||
continue;
|
||||
|
||||
LOG(ALERT) << "Could not attach FIFO to channel " << i;
|
||||
delete trx;
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return trx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sig_handler(int signo)
|
||||
{
|
||||
fprintf(stdout, "Received shutdown signal");
|
||||
gshutdown = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_signal_handlers()
|
||||
{
|
||||
if (signal(SIGINT, sig_handler) == SIG_ERR) {
|
||||
fprintf(stderr, "Failed to install SIGINT signal handler\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (signal(SIGTERM, sig_handler) == SIG_ERR) {
|
||||
fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
|
||||
exit( EXIT_FAILURE);
|
||||
}
|
||||
/* 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();
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
fprintf(stdout, "Options:\n"
|
||||
" -h This text\n"
|
||||
" -a UHD device args\n"
|
||||
" -l Logging level (%s)\n"
|
||||
" -i IP address of GSM core\n"
|
||||
" -p Base port number\n"
|
||||
" -e Enable EDGE receiver\n"
|
||||
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
|
||||
" -x Enable external 10 MHz reference\n"
|
||||
" -g Enable GPSDO reference\n"
|
||||
" -s Tx samples-per-symbol (1 or 4)\n"
|
||||
" -b Rx samples-per-symbol (1 or 4)\n"
|
||||
" -c Number of ARFCN channels (default=1)\n"
|
||||
" -f Enable C0 filler table\n"
|
||||
" -o Set baseband frequency offset (default=auto)\n"
|
||||
" -r Random GMSK Normal Burst test mode with given TSC\n"
|
||||
" -E Random 8-PSK Normal Burst test mode with given TSC\n"
|
||||
" -A Random Access Burst test mode with delay\n"
|
||||
" -R RSSI to dBm offset in dB (default=0)\n"
|
||||
" -S Swap channels (UmTRX only)\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||
" -h, --help This text\n"
|
||||
" -C, --config Filename The config file to use\n"
|
||||
" -V, --version Print the version of OsmoTRX\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
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)
|
||||
{
|
||||
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}
|
||||
};
|
||||
|
||||
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) {
|
||||
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) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'a':
|
||||
config->dev_args = optarg;
|
||||
print_deprecated(option);
|
||||
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
|
||||
break;
|
||||
case 'l':
|
||||
config->log_level = optarg;
|
||||
print_deprecated(option);
|
||||
log_set_log_level(osmo_stderr_target, atoi(optarg));
|
||||
break;
|
||||
case 'i':
|
||||
config->addr = optarg;
|
||||
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);
|
||||
break;
|
||||
case 'p':
|
||||
config->port = atoi(optarg);
|
||||
print_deprecated(option);
|
||||
trx->cfg.base_port = atoi(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
config->chans = atoi(optarg);
|
||||
print_deprecated(option);
|
||||
trx->cfg.num_chans = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
config->mcbts = true;
|
||||
print_deprecated(option);
|
||||
trx->cfg.multi_arfcn = true;
|
||||
break;
|
||||
case 'x':
|
||||
config->extref = true;
|
||||
print_deprecated(option);
|
||||
trx->cfg.clock_ref = REF_EXTERNAL;
|
||||
break;
|
||||
case 'g':
|
||||
config->gpsref = true;
|
||||
print_deprecated(option);
|
||||
trx->cfg.clock_ref = REF_GPS;
|
||||
break;
|
||||
case 'f':
|
||||
config->filler = Transceiver::FILLER_DUMMY;
|
||||
print_deprecated(option);
|
||||
trx->cfg.filler = FILLER_DUMMY;
|
||||
break;
|
||||
case 'o':
|
||||
config->offset = atof(optarg);
|
||||
print_deprecated(option);
|
||||
trx->cfg.offset = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
config->tx_sps = atoi(optarg);
|
||||
print_deprecated(option);
|
||||
trx->cfg.tx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
config->rx_sps = atoi(optarg);
|
||||
print_deprecated(option);
|
||||
trx->cfg.rx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
config->rtsc = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_NORM_RAND;
|
||||
break;
|
||||
case 'E':
|
||||
config->rtsc = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_EDGE_RAND;
|
||||
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;
|
||||
break;
|
||||
case 'A':
|
||||
config->rach_delay = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_ACCESS_RAND;
|
||||
print_deprecated(option);
|
||||
trx->cfg.rach_delay_set = true;
|
||||
trx->cfg.rach_delay = atoi(optarg);
|
||||
trx->cfg.filler = FILLER_ACCESS_RAND;
|
||||
break;
|
||||
case 'R':
|
||||
config->rssi_offset = atof(optarg);
|
||||
print_deprecated(option);
|
||||
trx->cfg.rssi_offset = atof(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
config->swap_channels = true;
|
||||
print_deprecated(option);
|
||||
trx->cfg.swap_channels = true;
|
||||
break;
|
||||
case 'e':
|
||||
config->edge = true;
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
exit(0);
|
||||
goto bad_config;
|
||||
}
|
||||
}
|
||||
|
||||
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
|
||||
if ((config->edge) || (config->mcbts)) {
|
||||
config->tx_sps = 4;
|
||||
config->rx_sps = 4;
|
||||
}
|
||||
/* Cmd line option specific validation & setup */
|
||||
|
||||
if (config->gpsref && config->extref) {
|
||||
printf("External and GPSDO references unavailable at the same time\n\n");
|
||||
if (trx->cfg.num_chans > TRX_CHAN_MAX) {
|
||||
LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX;
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if (!config->edge && (config->filler == Transceiver::FILLER_EDGE_RAND)) {
|
||||
printf("Can't enable EDGE filler when EDGE mode is disabled\n\n");
|
||||
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";
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if ((config->tx_sps != 1) && (config->tx_sps != 4) &&
|
||||
(config->rx_sps != 1) && (config->rx_sps != 4)) {
|
||||
printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if (config->rtsc > 7) {
|
||||
printf("Invalid training sequence %i\n\n", config->rtsc);
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
if (config->rach_delay > 68) {
|
||||
printf("RACH delay is too big %i\n\n", config->rach_delay);
|
||||
goto bad_config;
|
||||
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());
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -393,90 +361,231 @@ bad_config:
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int trx_validate_config(struct trx_ctx *trx)
|
||||
{
|
||||
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;
|
||||
if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) {
|
||||
LOG(ERROR) << "Unsupported number of channels";
|
||||
return -1;
|
||||
}
|
||||
|
||||
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
srandom(time(NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_sched_rr(unsigned int prio)
|
||||
{
|
||||
struct sched_param param;
|
||||
int rc;
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = prio;
|
||||
LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority;
|
||||
rc = sched_setscheduler(getpid(), SCHED_RR, ¶m);
|
||||
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;
|
||||
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
|
||||
|
||||
/* Create the low level device object */
|
||||
if (config.mcbts)
|
||||
if (trx->cfg.multi_arfcn)
|
||||
iface = RadioDevice::MULTI_ARFCN;
|
||||
|
||||
if (config.extref)
|
||||
ref = RadioDevice::REF_EXTERNAL;
|
||||
else if (config.gpsref)
|
||||
ref = RadioDevice::REF_GPS;
|
||||
else
|
||||
ref = RadioDevice::REF_INTERNAL;
|
||||
/* 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));
|
||||
}
|
||||
|
||||
usrp = RadioDevice::make(config.tx_sps, config.rx_sps, iface,
|
||||
config.chans, config.offset);
|
||||
type = usrp->open(config.dev_args, ref, config.swap_channels);
|
||||
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);
|
||||
if (type < 0) {
|
||||
LOG(ALERT) << "Failed to create radio device" << std::endl;
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
/* Setup the appropriate device interface */
|
||||
radio = makeRadioInterface(&config, usrp, type);
|
||||
radio = makeRadioInterface(trx, usrp, type);
|
||||
if (!radio)
|
||||
goto shutdown;
|
||||
|
||||
/* Create the transceiver core */
|
||||
trx = makeTransceiver(&config, radio);
|
||||
if (!trx)
|
||||
if (makeTransceiver(trx, radio) < 0)
|
||||
goto shutdown;
|
||||
|
||||
chans = trx->numChans();
|
||||
std::cout << "-- Transceiver active with "
|
||||
chans = transceiver->numChans();
|
||||
LOG(NOTICE) << "-- Transceiver active with "
|
||||
<< chans << " channel(s)" << std::endl;
|
||||
|
||||
while (!gshutdown)
|
||||
sleep(1);
|
||||
return 0;
|
||||
|
||||
shutdown:
|
||||
std::cout << "Shutting down transceiver..." << std::endl;
|
||||
trx_stop();
|
||||
return -1;
|
||||
}
|
||||
|
||||
delete trx;
|
||||
delete radio;
|
||||
delete usrp;
|
||||
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();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "radioInterface.h"
|
||||
#include "Resampler.h"
|
||||
#include <Logger.h>
|
||||
#include <PRBS.h>
|
||||
|
||||
extern "C" {
|
||||
#include "convert.h"
|
||||
@@ -76,6 +75,14 @@ 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);
|
||||
@@ -146,16 +153,32 @@ 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";
|
||||
#ifdef USRP1
|
||||
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
||||
(void*)this);
|
||||
#endif
|
||||
if (mRadio->requiresRadioAlign())
|
||||
mAlignRadioServiceLoopThread.start(
|
||||
(void * (*)(void*))AlignRadioServiceLoopAdapter,
|
||||
(void*)this);
|
||||
|
||||
if (!mRadio->start())
|
||||
return false;
|
||||
@@ -192,22 +215,6 @@ 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)
|
||||
{
|
||||
@@ -220,14 +227,15 @@ void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
|
||||
while (pushBuffer());
|
||||
}
|
||||
|
||||
bool RadioInterface::driveReceiveRadio()
|
||||
int RadioInterface::driveReceiveRadio()
|
||||
{
|
||||
radioVector *burst = NULL;
|
||||
|
||||
if (!mOn)
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
pullBuffer();
|
||||
if (pullBuffer() < 0)
|
||||
return -1;
|
||||
|
||||
GSM::Time rcvClock = mClock.get();
|
||||
rcvClock.decTN(receiveOffset);
|
||||
@@ -241,11 +249,11 @@ bool 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::gRACHSynchSequence.size();
|
||||
size_t head = GSM::gRACHSynchSequenceTS0.size();
|
||||
|
||||
/*
|
||||
* Form receive bursts and pass up to transceiver. Use repeating
|
||||
@@ -272,7 +280,7 @@ bool RadioInterface::driveReceiveRadio()
|
||||
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool RadioInterface::isUnderrun()
|
||||
@@ -302,13 +310,14 @@ double RadioInterface::getRxGain(size_t chan)
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterface::pullBuffer()
|
||||
int RadioInterface::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
|
||||
int numRecv;
|
||||
size_t segmentLen = recvBuffer[0]->getSegmentLen();
|
||||
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
numRecv = mRadio->readSamples(convertRecvBuffer,
|
||||
@@ -317,9 +326,9 @@ void RadioInterface::pullBuffer()
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
|
||||
if (numRecv != segmentLen) {
|
||||
if ((size_t) numRecv != segmentLen) {
|
||||
LOG(ALERT) << "Receive error " << numRecv;
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
@@ -330,6 +339,7 @@ void RadioInterface::pullBuffer()
|
||||
|
||||
underrun |= local_underrun;
|
||||
readTimestamp += numRecv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send timestamped chunk to the device with arbitrary size */
|
||||
|
||||
@@ -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 void pullBuffer(void);
|
||||
virtual int pullBuffer(void);
|
||||
|
||||
public:
|
||||
|
||||
@@ -116,8 +116,8 @@ public:
|
||||
void driveTransmitRadio(std::vector<signalVector *> &bursts,
|
||||
std::vector<bool> &zeros);
|
||||
|
||||
/** drive reception of GSM bursts */
|
||||
bool driveReceiveRadio();
|
||||
/** drive reception of GSM bursts. -1: Error. 0: Radio off. 1: Received something. */
|
||||
int driveReceiveRadio();
|
||||
|
||||
int setPowerAttenuation(int atten, size_t chan = 0);
|
||||
|
||||
@@ -130,31 +130,26 @@ 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(); }
|
||||
|
||||
#if USRP1
|
||||
protected:
|
||||
/** Minimum latency that the device can achieve */
|
||||
GSM::Time minLatency() { return mRadio->minLatency(); }
|
||||
|
||||
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();
|
||||
void pullBuffer();
|
||||
int pullBuffer();
|
||||
|
||||
public:
|
||||
RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
|
||||
@@ -167,7 +162,7 @@ public:
|
||||
class RadioInterfaceMulti : public RadioInterface {
|
||||
private:
|
||||
bool pushBuffer();
|
||||
void pullBuffer();
|
||||
int pullBuffer();
|
||||
|
||||
signalVector *outerSendBuffer;
|
||||
signalVector *outerRecvBuffer;
|
||||
|
||||
@@ -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,14 +225,15 @@ bool RadioInterfaceMulti::init(int type)
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterfaceMulti::pullBuffer()
|
||||
int RadioInterfaceMulti::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
size_t num;
|
||||
float *buf;
|
||||
unsigned int i;
|
||||
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
num = mRadio->readSamples(convertRecvBuffer,
|
||||
@@ -242,7 +243,7 @@ void RadioInterfaceMulti::pullBuffer()
|
||||
&local_underrun);
|
||||
if (num != channelizer->inputLen()) {
|
||||
LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
convert_short_float((float *) outerRecvBuffer->begin(),
|
||||
@@ -273,10 +274,22 @@ void RadioInterfaceMulti::pullBuffer()
|
||||
buf = channelizer->outputBuffer(pchan);
|
||||
size_t cLen = channelizer->outputLen();
|
||||
size_t hLen = dnsampler->len();
|
||||
size_t hSize = 2 * hLen * sizeof(float);
|
||||
|
||||
memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
|
||||
memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
|
||||
float *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++;
|
||||
}
|
||||
|
||||
float *wr_segment = recvBuffer[lchan]->getWriteSegment();
|
||||
|
||||
@@ -288,6 +301,7 @@ void RadioInterfaceMulti::pullBuffer()
|
||||
LOG(ALERT) << "Sample rate upsampling error";
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send a timestamped chunk to the device */
|
||||
|
||||
@@ -160,13 +160,13 @@ bool RadioInterfaceResamp::init(int type)
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
void RadioInterfaceResamp::pullBuffer()
|
||||
int RadioInterfaceResamp::pullBuffer()
|
||||
{
|
||||
bool local_underrun;
|
||||
int rc, num_recv;
|
||||
|
||||
if (recvBuffer[0]->getFreeSegments() <= 0)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
/* Outer buffer access size is fixed */
|
||||
num_recv = mRadio->readSamples(convertRecvBuffer,
|
||||
@@ -176,7 +176,7 @@ void RadioInterfaceResamp::pullBuffer()
|
||||
&local_underrun);
|
||||
if (num_recv != (int) resamp_outchunk) {
|
||||
LOG(ALERT) << "Receive error " << num_recv;
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
convert_short_float((float *) outerRecvBuffer->begin(),
|
||||
@@ -196,6 +196,7 @@ void 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
@@ -18,7 +18,6 @@
|
||||
#include "Vector.h"
|
||||
#include "Complex.h"
|
||||
#include "BitVector.h"
|
||||
#include "PRBS.h"
|
||||
#include "signalVector.h"
|
||||
|
||||
/* Burst lengths */
|
||||
@@ -26,24 +25,15 @@
|
||||
#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,
|
||||
@@ -62,66 +52,12 @@ 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);
|
||||
|
||||
@@ -141,7 +77,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, PRBS &prbs);
|
||||
signalVector *genRandNormalBurst(int tsc, int sps, int tn);
|
||||
|
||||
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
|
||||
signalVector *genRandAccessBurst(int delay, int sps, int tn);
|
||||
@@ -149,45 +85,6 @@ 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.
|
||||
@@ -204,68 +101,6 @@ 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 &litude,
|
||||
float &toa,
|
||||
unsigned max_toa);
|
||||
|
||||
/**
|
||||
GMSK Normal Burst correlator/detector.
|
||||
@param rxBurst The received GSM burst of interest.
|
||||
@param tsc Midamble type (0..7) also known as TSC
|
||||
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
|
||||
@param sps The number of samples per GSM symbol.
|
||||
@param amplitude The estimated amplitude of received TSC burst.
|
||||
@param toa The estimate time-of-arrival of received TSC burst.
|
||||
@param max_toa The maximum expected time-of-arrival
|
||||
@return 1 if threshold value is reached,
|
||||
negative value (-SignalError) on error,
|
||||
zero (SIGERR_NONE) if no burst is detected
|
||||
*/
|
||||
int analyzeTrafficBurst(const signalVector &burst,
|
||||
unsigned tsc,
|
||||
float threshold,
|
||||
int sps,
|
||||
complex &litude,
|
||||
float &toa,
|
||||
unsigned max_toa);
|
||||
|
||||
/**
|
||||
EDGE/8-PSK Normal Burst correlator/detector
|
||||
@param burst The received GSM burst of interest
|
||||
@param tsc Midamble type (0..7) also known as TSC
|
||||
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
|
||||
@param sps The number of samples per GSM symbol.
|
||||
@param amplitude The estimated amplitude of received TSC burst.
|
||||
@param toa The estimate time-of-arrival of received TSC burst.
|
||||
@param max_toa The maximum expected time-of-arrival
|
||||
@return 1 if threshold value is reached,
|
||||
negative value (-SignalError) on error,
|
||||
zero (SIGERR_NONE) if no burst is detected
|
||||
*/
|
||||
int detectEdgeBurst(const signalVector &burst,
|
||||
unsigned tsc,
|
||||
float threshold,
|
||||
int sps,
|
||||
complex &litude,
|
||||
float &toa,
|
||||
unsigned max_toa);
|
||||
|
||||
/**
|
||||
8-PSK/GMSK/RACH burst detector
|
||||
@param burst The received GSM burst of interest
|
||||
@@ -288,44 +123,6 @@ 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);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
#include "signalVector.h"
|
||||
|
||||
signalVector::signalVector(size_t size)
|
||||
: Vector<complex>(size),
|
||||
signalVector::signalVector(size_t size, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)
|
||||
: Vector<complex>(size, wAllocFunc, wFreeFunc),
|
||||
real(false), aligned(false), symmetry(NONE)
|
||||
{
|
||||
}
|
||||
|
||||
signalVector::signalVector(size_t size, size_t start)
|
||||
: Vector<complex>(size + start),
|
||||
signalVector::signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)
|
||||
: Vector<complex>(size + start, wAllocFunc, wFreeFunc),
|
||||
real(false), aligned(false), symmetry(NONE)
|
||||
{
|
||||
mStart = mData + start;
|
||||
}
|
||||
|
||||
signalVector::signalVector(complex *data, size_t start, size_t span)
|
||||
: Vector<complex>(NULL, data + start, data + start + span),
|
||||
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),
|
||||
real(false), aligned(false), symmetry(NONE)
|
||||
{
|
||||
}
|
||||
@@ -41,7 +41,14 @@ signalVector::signalVector(const signalVector &vector,
|
||||
void signalVector::operator=(const signalVector& vector)
|
||||
{
|
||||
resize(vector.size() + vector.getStart());
|
||||
memcpy(mData, vector.mData, bytes());
|
||||
|
||||
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()); */
|
||||
mStart = mData + vector.getStart();
|
||||
}
|
||||
|
||||
@@ -58,8 +65,13 @@ size_t signalVector::getStart() const
|
||||
size_t signalVector::updateHistory()
|
||||
{
|
||||
size_t num = getStart();
|
||||
|
||||
memmove(mData, mStart + this->size() - num, num * sizeof(complex));
|
||||
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)); */
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ enum Symmetry {
|
||||
class signalVector: public Vector<complex> {
|
||||
public:
|
||||
/** Default constructor */
|
||||
signalVector(size_t size = 0);
|
||||
signalVector(size_t size = 0, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
|
||||
|
||||
/** Construct with head room */
|
||||
signalVector(size_t size, size_t start);
|
||||
signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
|
||||
|
||||
/** Construct from existing buffer data (buffer not managed) */
|
||||
signalVector(complex *data, size_t start, size_t span);
|
||||
signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
|
||||
|
||||
/** Construct by from existing vector */
|
||||
signalVector(const signalVector &vector);
|
||||
|
||||
982
config/ax_cxx_compile_stdcxx.m4
Normal file
982
config/ax_cxx_compile_stdcxx.m4
Normal file
@@ -0,0 +1,982 @@
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXX and
|
||||
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
|
||||
# or '14' (for the C++14 standard).
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
# -std=c++11). If neither is specified, you get whatever works, with
|
||||
# preference for an extended mode.
|
||||
#
|
||||
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||
# indicates that baseline support for the specified C++ standard is
|
||||
# required and that the macro should error out if no mode with that
|
||||
# support is found. If specified 'optional', then configuration proceeds
|
||||
# regardless, after defining HAVE_CXX${VERSION} if and only if a
|
||||
# supporting mode is found.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
# Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 7
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
|
||||
AX_REQUIRE_DEFINED([AC_MSG_WARN])
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
|
||||
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
|
||||
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
[$2], [noext], [],
|
||||
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
|
||||
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||
AC_LANG_PUSH([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$2], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
switch="-std=gnu++${alternative}"
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
if test -n "$CXXCPP" ; then
|
||||
CXXCPP="$CXXCPP $switch"
|
||||
fi
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
|
||||
m4_if([$2], [ext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
if test -n "$CXXCPP" ; then
|
||||
CXXCPP="$CXXCPP $switch"
|
||||
fi
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test x$ac_success = xyes; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
AC_LANG_POP([C++])
|
||||
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
|
||||
if test x$ac_success = xno; then
|
||||
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
|
||||
fi
|
||||
fi
|
||||
if test x$ac_success = xno; then
|
||||
HAVE_CXX$1=0
|
||||
AC_MSG_NOTICE([No compiler with C++$1 support was found])
|
||||
else
|
||||
HAVE_CXX$1=1
|
||||
AC_DEFINE(HAVE_CXX$1,1,
|
||||
[define if the compiler supports basic C++$1 syntax])
|
||||
fi
|
||||
AC_SUBST(HAVE_CXX$1)
|
||||
m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])])
|
||||
])
|
||||
|
||||
|
||||
dnl Test body for checking C++11 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
)
|
||||
|
||||
|
||||
dnl Test body for checking C++14 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
)
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||
)
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++11, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201103L
|
||||
|
||||
#error "This is not a C++11 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx11
|
||||
{
|
||||
|
||||
namespace test_static_assert
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_final_override
|
||||
{
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual void f() {}
|
||||
};
|
||||
|
||||
struct Derived : public Base
|
||||
{
|
||||
virtual void f() override {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_double_right_angle_brackets
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
struct check {};
|
||||
|
||||
typedef check<void> single_type;
|
||||
typedef check<check<void>> double_type;
|
||||
typedef check<check<check<void>>> triple_type;
|
||||
typedef check<check<check<check<void>>>> quadruple_type;
|
||||
|
||||
}
|
||||
|
||||
namespace test_decltype
|
||||
{
|
||||
|
||||
int
|
||||
f()
|
||||
{
|
||||
int a = 1;
|
||||
decltype(a) b = 2;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_type_deduction
|
||||
{
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
auto
|
||||
add(T1 a1, T2 a2) -> decltype(a1 + a2)
|
||||
{
|
||||
return a1 + a2;
|
||||
}
|
||||
|
||||
int
|
||||
test(const int c, volatile int v)
|
||||
{
|
||||
static_assert(is_same<int, decltype(0)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(c)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(v)>::value == false, "");
|
||||
auto ac = c;
|
||||
auto av = v;
|
||||
auto sumi = ac + av + 'x';
|
||||
auto sumf = ac + av + 1.0;
|
||||
static_assert(is_same<int, decltype(ac)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(av)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumi)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumf)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
|
||||
return (sumf > 0.0) ? sumi : add(c, v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_noexcept
|
||||
{
|
||||
|
||||
int f() { return 0; }
|
||||
int g() noexcept { return 0; }
|
||||
|
||||
static_assert(noexcept(f()) == false, "");
|
||||
static_assert(noexcept(g()) == true, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
|
||||
{
|
||||
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
|
||||
}
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
return strlen_c_r(s, 0UL);
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("1") == 1UL, "");
|
||||
static_assert(strlen_c("example") == 7UL, "");
|
||||
static_assert(strlen_c("another\0example") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_rvalue_references
|
||||
{
|
||||
|
||||
template < int N >
|
||||
struct answer
|
||||
{
|
||||
static constexpr int value = N;
|
||||
};
|
||||
|
||||
answer<1> f(int&) { return answer<1>(); }
|
||||
answer<2> f(const int&) { return answer<2>(); }
|
||||
answer<3> f(int&&) { return answer<3>(); }
|
||||
|
||||
void
|
||||
test()
|
||||
{
|
||||
int i = 0;
|
||||
const int c = 0;
|
||||
static_assert(decltype(f(i))::value == 1, "");
|
||||
static_assert(decltype(f(c))::value == 2, "");
|
||||
static_assert(decltype(f(0))::value == 3, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_uniform_initialization
|
||||
{
|
||||
|
||||
struct test
|
||||
{
|
||||
static const int zero {};
|
||||
static const int one {1};
|
||||
};
|
||||
|
||||
static_assert(test::zero == 0, "");
|
||||
static_assert(test::one == 1, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambdas
|
||||
{
|
||||
|
||||
void
|
||||
test1()
|
||||
{
|
||||
auto lambda1 = [](){};
|
||||
auto lambda2 = lambda1;
|
||||
lambda1();
|
||||
lambda2();
|
||||
}
|
||||
|
||||
int
|
||||
test2()
|
||||
{
|
||||
auto a = [](int i, int j){ return i + j; }(1, 2);
|
||||
auto b = []() -> int { return '0'; }();
|
||||
auto c = [=](){ return a + b; }();
|
||||
auto d = [&](){ return c; }();
|
||||
auto e = [a, &b](int x) mutable {
|
||||
const auto identity = [](int y){ return y; };
|
||||
for (auto i = 0; i < a; ++i)
|
||||
a += b--;
|
||||
return x + identity(a + b);
|
||||
}(0);
|
||||
return a + b + c + d + e;
|
||||
}
|
||||
|
||||
int
|
||||
test3()
|
||||
{
|
||||
const auto nullary = [](){ return 0; };
|
||||
const auto unary = [](int x){ return x; };
|
||||
using nullary_t = decltype(nullary);
|
||||
using unary_t = decltype(unary);
|
||||
const auto higher1st = [](nullary_t f){ return f(); };
|
||||
const auto higher2nd = [unary](nullary_t f1){
|
||||
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
|
||||
};
|
||||
return higher1st(nullary) + higher2nd(nullary)(unary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_variadic_templates
|
||||
{
|
||||
|
||||
template <int...>
|
||||
struct sum;
|
||||
|
||||
template <int N0, int... N1toN>
|
||||
struct sum<N0, N1toN...>
|
||||
{
|
||||
static constexpr auto value = N0 + sum<N1toN...>::value;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct sum<>
|
||||
{
|
||||
static constexpr auto value = 0;
|
||||
};
|
||||
|
||||
static_assert(sum<>::value == 0, "");
|
||||
static_assert(sum<1>::value == 1, "");
|
||||
static_assert(sum<23>::value == 23, "");
|
||||
static_assert(sum<1, 2>::value == 3, "");
|
||||
static_assert(sum<5, 5, 11>::value == 21, "");
|
||||
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
|
||||
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
|
||||
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
|
||||
// because of this.
|
||||
namespace test_template_alias_sfinae
|
||||
{
|
||||
|
||||
struct foo {};
|
||||
|
||||
template<typename T>
|
||||
using member = typename T::member_type;
|
||||
|
||||
template<typename T>
|
||||
void func(...) {}
|
||||
|
||||
template<typename T>
|
||||
void func(member<T>*) {}
|
||||
|
||||
void test();
|
||||
|
||||
void test() { func<foo>(0); }
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx11
|
||||
|
||||
#endif // __cplusplus >= 201103L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++14
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++14, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201402L
|
||||
|
||||
#error "This is not a C++14 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx14
|
||||
{
|
||||
|
||||
namespace test_polymorphic_lambdas
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
const auto lambda = [](auto&&... args){
|
||||
const auto istiny = [](auto x){
|
||||
return (sizeof(x) == 1UL) ? 1 : 0;
|
||||
};
|
||||
const int aretiny[] = { istiny(args)... };
|
||||
return aretiny[0];
|
||||
};
|
||||
return lambda(1, 1L, 1.0f, '1');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_binary_literals
|
||||
{
|
||||
|
||||
constexpr auto ivii = 0b0000000000101010;
|
||||
static_assert(ivii == 42, "wrong value");
|
||||
|
||||
}
|
||||
|
||||
namespace test_generalized_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
constexpr unsigned long
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
auto length = 0UL;
|
||||
for (auto p = s; *p; ++p)
|
||||
++length;
|
||||
return length;
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("x") == 1UL, "");
|
||||
static_assert(strlen_c("test") == 4UL, "");
|
||||
static_assert(strlen_c("another\0test") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_init_capture
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
const auto lambda1 = [a = x](int b){ return a + b; };
|
||||
const auto lambda2 = [a = lambda1(x)](){ return a; };
|
||||
return lambda2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_digit_separators
|
||||
{
|
||||
|
||||
constexpr auto ten_million = 100'000'000;
|
||||
static_assert(ten_million == 100000000, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_return_type_deduction
|
||||
{
|
||||
|
||||
auto f(int& x) { return x; }
|
||||
decltype(auto) g(int& x) { return x; }
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static constexpr auto value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static constexpr auto value = true;
|
||||
};
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
static_assert(is_same<int, decltype(f(x))>::value, "");
|
||||
static_assert(is_same<int&, decltype(g(x))>::value, "");
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx14
|
||||
|
||||
#endif // __cplusplus >= 201402L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++17
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++17, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus <= 201402L
|
||||
|
||||
#error "This is not a C++17 compiler"
|
||||
|
||||
#else
|
||||
|
||||
#if defined(__clang__)
|
||||
#define REALLY_CLANG
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define REALLY_GCC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
namespace cxx17
|
||||
{
|
||||
|
||||
#if !defined(REALLY_CLANG)
|
||||
namespace test_constexpr_lambdas
|
||||
{
|
||||
|
||||
// TODO: test it with clang++ from git
|
||||
|
||||
constexpr int foo = [](){return 42;}();
|
||||
|
||||
}
|
||||
#endif // !defined(REALLY_CLANG)
|
||||
|
||||
namespace test::nested_namespace::definitions
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
namespace test_fold_expression
|
||||
{
|
||||
|
||||
template<typename... Args>
|
||||
int multiply(Args... args)
|
||||
{
|
||||
return (args * ... * 1);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool all(Args... args)
|
||||
{
|
||||
return (args && ...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_extended_static_assert
|
||||
{
|
||||
|
||||
static_assert (true);
|
||||
|
||||
}
|
||||
|
||||
namespace test_auto_brace_init_list
|
||||
{
|
||||
|
||||
auto foo = {5};
|
||||
auto bar {5};
|
||||
|
||||
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
|
||||
static_assert(std::is_same<int, decltype(bar)>::value);
|
||||
}
|
||||
|
||||
namespace test_typename_in_template_template_parameter
|
||||
{
|
||||
|
||||
template<template<typename> typename X> struct D;
|
||||
|
||||
}
|
||||
|
||||
namespace test_fallthrough_nodiscard_maybe_unused_attributes
|
||||
{
|
||||
|
||||
int f1()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
[[nodiscard]] int f2()
|
||||
{
|
||||
[[maybe_unused]] auto unused = f1();
|
||||
|
||||
switch (f1())
|
||||
{
|
||||
case 17:
|
||||
f1();
|
||||
[[fallthrough]];
|
||||
case 42:
|
||||
f1();
|
||||
}
|
||||
return f1();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_extended_aggregate_initialization
|
||||
{
|
||||
|
||||
struct base1
|
||||
{
|
||||
int b1, b2 = 42;
|
||||
};
|
||||
|
||||
struct base2
|
||||
{
|
||||
base2() {
|
||||
b3 = 42;
|
||||
}
|
||||
int b3;
|
||||
};
|
||||
|
||||
struct derived : base1, base2
|
||||
{
|
||||
int d;
|
||||
};
|
||||
|
||||
derived d1 {{1, 2}, {}, 4}; // full initialization
|
||||
derived d2 {{}, {}, 4}; // value-initialized bases
|
||||
|
||||
}
|
||||
|
||||
namespace test_general_range_based_for_loop
|
||||
{
|
||||
|
||||
struct iter
|
||||
{
|
||||
int i;
|
||||
|
||||
int& operator* ()
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
const int& operator* () const
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
iter& operator++()
|
||||
{
|
||||
++i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct sentinel
|
||||
{
|
||||
int i;
|
||||
};
|
||||
|
||||
bool operator== (const iter& i, const sentinel& s)
|
||||
{
|
||||
return i.i == s.i;
|
||||
}
|
||||
|
||||
bool operator!= (const iter& i, const sentinel& s)
|
||||
{
|
||||
return !(i == s);
|
||||
}
|
||||
|
||||
struct range
|
||||
{
|
||||
iter begin() const
|
||||
{
|
||||
return {0};
|
||||
}
|
||||
|
||||
sentinel end() const
|
||||
{
|
||||
return {5};
|
||||
}
|
||||
};
|
||||
|
||||
void f()
|
||||
{
|
||||
range r {};
|
||||
|
||||
for (auto i : r)
|
||||
{
|
||||
[[maybe_unused]] auto v = i;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_capture_asterisk_this_by_value
|
||||
{
|
||||
|
||||
struct t
|
||||
{
|
||||
int i;
|
||||
int foo()
|
||||
{
|
||||
return [*this]()
|
||||
{
|
||||
return i;
|
||||
}();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_enum_class_construction
|
||||
{
|
||||
|
||||
enum class byte : unsigned char
|
||||
{};
|
||||
|
||||
byte foo {42};
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr_if
|
||||
{
|
||||
|
||||
template <bool cond>
|
||||
int f ()
|
||||
{
|
||||
if constexpr(cond)
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_selection_statement_with_initializer
|
||||
{
|
||||
|
||||
int f()
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
|
||||
int f2()
|
||||
{
|
||||
if (auto i = f(); i > 0)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
switch (auto i = f(); i + 4)
|
||||
{
|
||||
case 17:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if !defined(REALLY_CLANG)
|
||||
namespace test_template_argument_deduction_for_class_templates
|
||||
{
|
||||
|
||||
// TODO: test it with clang++ from git
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct pair
|
||||
{
|
||||
pair (T1 p1, T2 p2)
|
||||
: m1 {p1},
|
||||
m2 {p2}
|
||||
{}
|
||||
|
||||
T1 m1;
|
||||
T2 m2;
|
||||
};
|
||||
|
||||
void f()
|
||||
{
|
||||
[[maybe_unused]] auto p = pair{13, 42u};
|
||||
}
|
||||
|
||||
}
|
||||
#endif // !defined(REALLY_CLANG)
|
||||
|
||||
namespace test_non_type_auto_template_parameters
|
||||
{
|
||||
|
||||
template <auto n>
|
||||
struct B
|
||||
{};
|
||||
|
||||
B<5> b1;
|
||||
B<'a'> b2;
|
||||
|
||||
}
|
||||
|
||||
#if !defined(REALLY_CLANG)
|
||||
namespace test_structured_bindings
|
||||
{
|
||||
|
||||
// TODO: test it with clang++ from git
|
||||
|
||||
int arr[2] = { 1, 2 };
|
||||
std::pair<int, int> pr = { 1, 2 };
|
||||
|
||||
auto f1() -> int(&)[2]
|
||||
{
|
||||
return arr;
|
||||
}
|
||||
|
||||
auto f2() -> std::pair<int, int>&
|
||||
{
|
||||
return pr;
|
||||
}
|
||||
|
||||
struct S
|
||||
{
|
||||
int x1 : 2;
|
||||
volatile double y1;
|
||||
};
|
||||
|
||||
S f3()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto [ x1, y1 ] = f1();
|
||||
auto& [ xr1, yr1 ] = f1();
|
||||
auto [ x2, y2 ] = f2();
|
||||
auto& [ xr2, yr2 ] = f2();
|
||||
const auto [ x3, y3 ] = f3();
|
||||
|
||||
}
|
||||
#endif // !defined(REALLY_CLANG)
|
||||
|
||||
#if !defined(REALLY_CLANG)
|
||||
namespace test_exception_spec_type_system
|
||||
{
|
||||
|
||||
// TODO: test it with clang++ from git
|
||||
|
||||
struct Good {};
|
||||
struct Bad {};
|
||||
|
||||
void g1() noexcept;
|
||||
void g2();
|
||||
|
||||
template<typename T>
|
||||
Bad
|
||||
f(T*, T*);
|
||||
|
||||
template<typename T1, typename T2>
|
||||
Good
|
||||
f(T1*, T2*);
|
||||
|
||||
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
|
||||
|
||||
}
|
||||
#endif // !defined(REALLY_CLANG)
|
||||
|
||||
namespace test_inline_variables
|
||||
{
|
||||
|
||||
template<class T> void f(T)
|
||||
{}
|
||||
|
||||
template<class T> inline T g(T)
|
||||
{
|
||||
return T{};
|
||||
}
|
||||
|
||||
template<> inline void f<>(int)
|
||||
{}
|
||||
|
||||
template<> int g<>(int)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx17
|
||||
|
||||
#endif // __cplusplus <= 201402L
|
||||
|
||||
]])
|
||||
39
config/ax_cxx_compile_stdcxx_11.m4
Normal file
39
config/ax_cxx_compile_stdcxx_11.m4
Normal file
@@ -0,0 +1,39 @@
|
||||
# =============================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
|
||||
# =============================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the C++11
|
||||
# standard; if necessary, add switches to CXX and CXXCPP to enable
|
||||
# support.
|
||||
#
|
||||
# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
|
||||
# macro with the version set to C++11. The two optional arguments are
|
||||
# forwarded literally as the second and third argument respectively.
|
||||
# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
|
||||
# more information. If you want to use this macro, you also need to
|
||||
# download the ax_cxx_compile_stdcxx.m4 file.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 18
|
||||
|
||||
AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])
|
||||
@@ -1,79 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_avx_xgetbv.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_GCC_X86_AVX_XGETBV
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# On later x86 processors with AVX SIMD support, with gcc or a compiler
|
||||
# that has a compatible syntax for inline assembly instructions, run a
|
||||
# small program that executes the xgetbv instruction with input OP. This
|
||||
# can be used to detect if the OS supports AVX instruction usage.
|
||||
#
|
||||
# On output, the values of the eax and edx registers are stored as
|
||||
# hexadecimal strings as "eax:edx" in the cache variable
|
||||
# ax_cv_gcc_x86_avx_xgetbv.
|
||||
#
|
||||
# If the xgetbv instruction fails (because you are running a
|
||||
# cross-compiler, or because you are not using gcc, or because you are on
|
||||
# a processor that doesn't have this instruction),
|
||||
# ax_cv_gcc_x86_avx_xgetbv_OP is set to the string "unknown".
|
||||
#
|
||||
# This macro mainly exists to be used in AX_EXT.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 1
|
||||
|
||||
AC_DEFUN([AX_GCC_X86_AVX_XGETBV],
|
||||
[AC_REQUIRE([AC_PROG_CC])
|
||||
AC_LANG_PUSH([C])
|
||||
AC_CACHE_CHECK(for x86-AVX xgetbv $1 output, ax_cv_gcc_x86_avx_xgetbv_$1,
|
||||
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
|
||||
int op = $1, eax, edx;
|
||||
FILE *f;
|
||||
/* Opcodes for xgetbv */
|
||||
__asm__(".byte 0x0f, 0x01, 0xd0"
|
||||
: "=a" (eax), "=d" (edx)
|
||||
: "c" (op));
|
||||
f = fopen("conftest_xgetbv", "w"); if (!f) return 1;
|
||||
fprintf(f, "%x:%x\n", eax, edx);
|
||||
fclose(f);
|
||||
return 0;
|
||||
])],
|
||||
[ax_cv_gcc_x86_avx_xgetbv_$1=`cat conftest_xgetbv`; rm -f conftest_xgetbv],
|
||||
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown; rm -f conftest_xgetbv],
|
||||
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown])])
|
||||
AC_LANG_POP([C])
|
||||
])
|
||||
@@ -1,79 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_GCC_X86_CPUID(OP)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# On Pentium and later x86 processors, with gcc or a compiler that has a
|
||||
# compatible syntax for inline assembly instructions, run a small program
|
||||
# that executes the cpuid instruction with input OP. This can be used to
|
||||
# detect the CPU type.
|
||||
#
|
||||
# On output, the values of the eax, ebx, ecx, and edx registers are stored
|
||||
# as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable
|
||||
# ax_cv_gcc_x86_cpuid_OP.
|
||||
#
|
||||
# If the cpuid instruction fails (because you are running a
|
||||
# cross-compiler, or because you are not using gcc, or because you are on
|
||||
# a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP
|
||||
# is set to the string "unknown".
|
||||
#
|
||||
# This macro mainly exists to be used in AX_GCC_ARCHFLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
# Copyright (c) 2008 Matteo Frigo
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 7
|
||||
|
||||
AC_DEFUN([AX_GCC_X86_CPUID],
|
||||
[AC_REQUIRE([AC_PROG_CC])
|
||||
AC_LANG_PUSH([C])
|
||||
AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1,
|
||||
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
|
||||
int op = $1, eax, ebx, ecx, edx;
|
||||
FILE *f;
|
||||
__asm__("cpuid"
|
||||
: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
|
||||
: "a" (op));
|
||||
f = fopen("conftest_cpuid", "w"); if (!f) return 1;
|
||||
fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx);
|
||||
fclose(f);
|
||||
return 0;
|
||||
])],
|
||||
[ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid],
|
||||
[ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid],
|
||||
[ax_cv_gcc_x86_cpuid_$1=unknown])])
|
||||
AC_LANG_POP([C])
|
||||
])
|
||||
@@ -4,14 +4,13 @@
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_EXT
|
||||
# AX_SSE
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Find supported SIMD extensions by requesting cpuid. When an SIMD
|
||||
# extension is found, the -m"simdextensionname" is added to SIMD_FLAGS if
|
||||
# compiler supports it. For example, if "sse2" is available, then "-msse2"
|
||||
# is added to SIMD_FLAGS.
|
||||
# Find SIMD extensions supported by compiler. The -m"simdextensionname" is
|
||||
# added to SIMD_FLAGS if compiler supports it. For example, if "sse2" is
|
||||
# available, then "-msse2" is added to SIMD_FLAGS.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
@@ -19,7 +18,7 @@
|
||||
#
|
||||
# And defines:
|
||||
#
|
||||
# HAVE_MMX / HAVE_SSE / HAVE_SSE2 / HAVE_SSE3 / HAVE_SSSE3 / HAVE_SSE4.1 / HAVE_SSE4.2 / HAVE_AVX
|
||||
# HAVE_SSE3 / HAVE_SSE4.1
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
@@ -41,32 +40,29 @@ AC_DEFUN([AX_SSE],
|
||||
[
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
|
||||
AM_CONDITIONAL(HAVE_SSE3, false)
|
||||
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
||||
|
||||
case $host_cpu in
|
||||
i[[3456]]86*|x86_64*|amd64*)
|
||||
|
||||
AC_REQUIRE([AX_GCC_X86_CPUID])
|
||||
AC_REQUIRE([AX_GCC_X86_AVX_XGETBV])
|
||||
|
||||
AX_GCC_X86_CPUID(0x00000001)
|
||||
|
||||
AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse3_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse3"
|
||||
AC_DEFINE(HAVE_SSE3,,[Support SSE3 (Streaming SIMD Extensions 3) instructions])
|
||||
AM_CONDITIONAL(HAVE_SSE3, true)
|
||||
AC_DEFINE(HAVE_SSE3,,
|
||||
[Support SSE3 (Streaming SIMD Extensions 3) instructions])
|
||||
AM_CONDITIONAL(HAVE_SSE3, true)
|
||||
else
|
||||
AC_MSG_WARN([Your compiler does not support sse3 instructions, can you try another compiler?])
|
||||
AM_CONDITIONAL(HAVE_SSE3, false)
|
||||
AC_MSG_WARN([Your compiler does not support SSE3 instructions])
|
||||
fi
|
||||
|
||||
AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, [])
|
||||
if test x"$ax_cv_support_sse41_ext" = x"yes"; then
|
||||
SIMD_FLAGS="$SIMD_FLAGS -msse4.1"
|
||||
AC_DEFINE(HAVE_SSE4_1,,[Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions])
|
||||
AM_CONDITIONAL(HAVE_SSE4_1, true)
|
||||
AC_DEFINE(HAVE_SSE4_1,,
|
||||
[Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions])
|
||||
AM_CONDITIONAL(HAVE_SSE4_1, true)
|
||||
else
|
||||
AC_MSG_WARN([Your compiler does not support sse4.1 instructions, can you try another compiler?])
|
||||
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
||||
AC_MSG_WARN([Your compiler does not support SSE4.1])
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
209
configure.ac
209
configure.ac
@@ -18,12 +18,15 @@ dnl You should have received a copy of the GNU General Public License
|
||||
dnl along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
dnl
|
||||
|
||||
AC_INIT(openbts,P2.8TRUNK)
|
||||
AC_INIT([osmo-trx],
|
||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[openbsc@lists.osmocom.org])
|
||||
AC_PREREQ(2.57)
|
||||
AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
AC_CONFIG_MACRO_DIR([config])
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
@@ -34,12 +37,26 @@ AM_INIT_AUTOMAKE([subdir-objects])
|
||||
dnl Linux kernel KBuild style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl include release helper
|
||||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
AM_PROG_AS
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AX_CXX_COMPILE_STDCXX_11
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_INSTALL
|
||||
AC_PATH_PROG([RM_PROG], [rm])
|
||||
AC_LANG([C++])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
AC_MSG_WARN([You need to install pkg-config])
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AC_LIBTOOL_WIN32_DLL
|
||||
AC_ENABLE_SHARED dnl do build shared libraries
|
||||
@@ -58,11 +75,56 @@ AC_TYPE_SIZE_T
|
||||
AC_HEADER_TIME
|
||||
AC_C_BIGENDIAN
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[werror=$enableval], [werror="no"])
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
fi
|
||||
|
||||
|
||||
AC_ARG_WITH(uhd, [
|
||||
AS_HELP_STRING([--with-uhd],
|
||||
[enable UHD based transceiver])
|
||||
])
|
||||
|
||||
AC_ARG_WITH(usrp1, [
|
||||
AS_HELP_STRING([--with-usrp1],
|
||||
[enable USRP1 gnuradio based transceiver])
|
||||
])
|
||||
|
||||
AC_ARG_WITH(lms, [
|
||||
AS_HELP_STRING([--with-lms],
|
||||
[enable LimeSuite based transceiver])
|
||||
])
|
||||
|
||||
AC_ARG_WITH(singledb, [
|
||||
AS_HELP_STRING([--with-singledb],
|
||||
[enable single daughterboard use on USRP1])
|
||||
@@ -93,10 +155,30 @@ AS_IF([test "x$with_neon_vfpv4" = "xyes"], [
|
||||
])
|
||||
|
||||
AS_IF([test "x$with_usrp1" = "xyes"], [
|
||||
AC_CHECK_HEADER([boost/config.hpp],[],
|
||||
[AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])])
|
||||
PKG_CHECK_MODULES(USRP, usrp >= 3.3)
|
||||
])
|
||||
|
||||
AS_IF([test "x$with_usrp1" != "xyes"],[
|
||||
AS_IF([test "x$with_lms" = "xyes"], [
|
||||
PKG_CHECK_MODULES(LMS, LimeSuite)
|
||||
|
||||
# LimeSuite dc124e4e2ed9b549b142410af172f0592f9f0c23 > 18.10 broke API compatibility:
|
||||
_cflags_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $LMS_CFLAGS"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <lime/LimeSuite.h>]],
|
||||
[[LMS_VCTCXOWrite(NULL, 0, false); LMS_VCTCXORead(NULL, 0, false);]]
|
||||
)],
|
||||
[AC_DEFINE([HAVE_LMS_VCTCXO_EEPROM_SAVING], [1],
|
||||
[LMS_VCTCXO* requires memory parameter])],
|
||||
[AC_DEFINE([HAVE_LMS_VCTCXO_EEPROM_SAVING], [0],
|
||||
[LMS_VCTCXO* has no memory parameter])])
|
||||
CFLAGS=$_cflags_save
|
||||
])
|
||||
|
||||
AS_IF([test "x$with_uhd" != "xno"],[
|
||||
PKG_CHECK_MODULES(UHD, uhd >= 003.011,
|
||||
[AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)],
|
||||
[PKG_CHECK_MODULES(UHD, uhd >= 003.009,
|
||||
@@ -104,8 +186,6 @@ AS_IF([test "x$with_usrp1" != "xyes"],[
|
||||
[PKG_CHECK_MODULES(UHD, uhd >= 003.005)]
|
||||
)]
|
||||
)
|
||||
AC_DEFINE(USE_UHD, 1, All UHD versions)
|
||||
PKG_CHECK_MODULES(FFTWF, fftw3f)
|
||||
])
|
||||
|
||||
AS_IF([test "x$with_singledb" = "xyes"], [
|
||||
@@ -120,17 +200,104 @@ AS_IF([test "x$with_sse" != "xno"], [
|
||||
AM_CONDITIONAL(HAVE_SSE4_1, false)
|
||||
])
|
||||
|
||||
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
|
||||
dnl Check if the compiler supports specified GCC's built-in function
|
||||
AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
|
||||
AC_CACHE_CHECK(
|
||||
[whether ${CC} has $1 built-in],
|
||||
[osmo_cv_cc_has_builtin], [
|
||||
AC_LINK_IFELSE([
|
||||
AC_LANG_PROGRAM([], [
|
||||
__builtin_cpu_supports("sse");
|
||||
])
|
||||
],
|
||||
[AS_VAR_SET([osmo_cv_cc_has_builtin], [yes])],
|
||||
[AS_VAR_SET([osmo_cv_cc_has_builtin], [no])])
|
||||
]
|
||||
)
|
||||
|
||||
AS_IF([test yes = AS_VAR_GET([osmo_cv_cc_has_builtin])], [
|
||||
AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
|
||||
[Define to 1 if compiler has the '$1' built-in function])
|
||||
], [
|
||||
AC_MSG_WARN($2)
|
||||
])
|
||||
])
|
||||
|
||||
dnl Check if the compiler supports runtime SIMD detection
|
||||
CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
|
||||
[Runtime SIMD detection will be disabled])
|
||||
|
||||
AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" != "xno"])
|
||||
AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
|
||||
AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
|
||||
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
||||
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
|
||||
|
||||
AC_CHECK_LIB(sqlite3, sqlite3_open, , AC_MSG_ERROR(sqlite3 is not available))
|
||||
|
||||
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
|
||||
PKG_CHECK_MODULES(SQLITE3, sqlite3)
|
||||
PKG_CHECK_MODULES(FFTWF, fftw3f)
|
||||
|
||||
AC_CHECK_HEADER([boost/config.hpp],[],
|
||||
[AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])])
|
||||
# Generate manuals
|
||||
AC_ARG_ENABLE(manuals,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-manuals],
|
||||
[Generate manual PDFs [default=no]],
|
||||
)],
|
||||
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
|
||||
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
|
||||
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
|
||||
fallback])
|
||||
if test x"$osmo_ac_build_manuals" = x"yes"
|
||||
then
|
||||
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
|
||||
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
|
||||
else
|
||||
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
|
||||
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
|
||||
else
|
||||
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
|
||||
fi
|
||||
fi
|
||||
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
|
||||
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
|
||||
fi
|
||||
|
||||
# Find and run check-depends
|
||||
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
|
||||
if ! test -x "$CHECK_DEPENDS"; then
|
||||
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
|
||||
fi
|
||||
if ! $CHECK_DEPENDS; then
|
||||
AC_MSG_ERROR("missing dependencies for --enable-manuals")
|
||||
fi
|
||||
|
||||
# Put in Makefile with absolute path
|
||||
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
|
||||
AC_SUBST([OSMO_GSM_MANUALS_DIR])
|
||||
fi
|
||||
|
||||
# https://www.freedesktop.org/software/systemd/man/daemon.html
|
||||
AC_ARG_WITH([systemdsystemunitdir],
|
||||
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||
[with_systemdsystemunitdir=auto])
|
||||
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
|
||||
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
|
||||
|
||||
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
|
||||
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
|
||||
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
|
||||
with_systemdsystemunitdir=no],
|
||||
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
|
||||
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
|
||||
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
|
||||
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
|
||||
|
||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
|
||||
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
|
||||
|
||||
dnl Output files
|
||||
AC_CONFIG_FILES([\
|
||||
@@ -138,8 +305,22 @@ AC_CONFIG_FILES([\
|
||||
CommonLibs/Makefile \
|
||||
GSM/Makefile \
|
||||
Transceiver52M/Makefile \
|
||||
Transceiver52M/arm/Makefile \
|
||||
Transceiver52M/x86/Makefile \
|
||||
Transceiver52M/arch/Makefile \
|
||||
Transceiver52M/arch/common/Makefile \
|
||||
Transceiver52M/arch/arm/Makefile \
|
||||
Transceiver52M/arch/x86/Makefile \
|
||||
Transceiver52M/device/Makefile \
|
||||
Transceiver52M/device/uhd/Makefile \
|
||||
Transceiver52M/device/usrp1/Makefile \
|
||||
Transceiver52M/device/lms/Makefile \
|
||||
tests/Makefile \
|
||||
tests/CommonLibs/Makefile \
|
||||
tests/Transceiver52M/Makefile \
|
||||
doc/Makefile \
|
||||
doc/examples/Makefile \
|
||||
contrib/Makefile \
|
||||
contrib/systemd/Makefile \
|
||||
])
|
||||
|
||||
AC_OUTPUT
|
||||
AC_OUTPUT(
|
||||
doc/manuals/Makefile)
|
||||
|
||||
1
contrib/Makefile.am
Normal file
1
contrib/Makefile.am
Normal file
@@ -0,0 +1 @@
|
||||
SUBDIRS = systemd
|
||||
@@ -1,7 +1,110 @@
|
||||
#!/bin/sh
|
||||
# jenkins build helper script for osmo-trx. This is how we build on jenkins.osmocom.org
|
||||
#
|
||||
# environment variables:
|
||||
# * INSTR: configure the CPU instruction set ("--with-sse", "--with-neon" or "--with-neon-vfpv4")
|
||||
# * WITH_MANUALS: build manual PDFs if set to "1"
|
||||
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
|
||||
# * INSIDE_CHROOT: (used internally) set to "1" when the script runs with QEMU in an ARM chroot
|
||||
#
|
||||
set -ex
|
||||
|
||||
substr() { [ -z "${2##*$1*}" ]; }
|
||||
|
||||
#apt-get install qemu qemu-user-static qemu-system-arm debootstrap fakeroot proot
|
||||
mychroot_nocwd() {
|
||||
# LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container
|
||||
# PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH.
|
||||
# PROOT_NO_SECCOMP is requried due to proot bug #106
|
||||
LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@"
|
||||
}
|
||||
|
||||
mychroot() {
|
||||
mychroot_nocwd -w / "$@"
|
||||
}
|
||||
|
||||
base="$PWD"
|
||||
deps="$base/deps"
|
||||
inst="$deps/install"
|
||||
export deps inst
|
||||
|
||||
if [ -z "${INSIDE_CHROOT}" ]; then
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
# Only use ARM chroot if host is not ARM and the target is ARM:
|
||||
if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
|
||||
|
||||
OSMOTRX_DIR="$PWD" # we assume we are called as contrib/jenkins.sh
|
||||
ROOTFS_PREFIX="${ROOTFS_PREFIX:-$HOME}"
|
||||
ROOTFS="${ROOTFS_PREFIX}/qemu-img"
|
||||
mkdir -p "${ROOTFS_PREFIX}"
|
||||
|
||||
# Prepare chroot:
|
||||
if [ ! -d "$ROOTFS" ]; then
|
||||
mkdir -p "$ROOTFS"
|
||||
if [ "x${USE_DEBOOTSTRAP}" = "x1" ]; then
|
||||
fakeroot qemu-debootstrap --foreign --include="linux-image-armmp-lpae" --arch=armhf stretch "$ROOTFS" http://ftp.de.debian.org/debian/
|
||||
# Hack to avoid debootstrap trying to mount /proc, as it will fail with "no permissions" and anyway proot takes care of it:
|
||||
sed -i "s/setup_proc//g" "$ROOTFS/debootstrap/suite-script"
|
||||
mychroot /debootstrap/debootstrap --second-stage --verbose http://ftp.de.debian.org/debian/
|
||||
else
|
||||
YESTERDAY=$(python -c 'import datetime ; print((datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y%m%d"))')
|
||||
wget -nc -q "https://uk.images.linuxcontainers.org/images/debian/stretch/armhf/default/${YESTERDAY}_22:42/rootfs.tar.xz"
|
||||
tar -xf rootfs.tar.xz -C "$ROOTFS/" || true
|
||||
echo "nameserver 8.8.8.8" > "$ROOTFS/etc/resolv.conf"
|
||||
fi
|
||||
mychroot -b /dev apt-get update
|
||||
mychroot apt-get -y install build-essential dh-autoreconf pkg-config libuhd-dev libusb-1.0-0-dev libusb-dev git libtalloc-dev libgnutls28-dev stow
|
||||
fi
|
||||
# Run jenkins.sh inside the chroot:
|
||||
INSIDE_CHROOT=1 mychroot_nocwd \
|
||||
-w /osmo-trx \
|
||||
-b "$OSMOTRX_DIR:/osmo-trx" \
|
||||
-b "$(which osmo-clean-workspace.sh):/usr/bin/osmo-clean-workspace.sh" \
|
||||
-b "$(which osmo-build-dep.sh):/usr/bin/osmo-build-dep.sh" \
|
||||
-b "$(which osmo-deps.sh):/usr/bin/osmo-deps.sh" \
|
||||
./contrib/jenkins.sh
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir "$deps" || true
|
||||
|
||||
osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc"
|
||||
osmo-build-dep.sh libusrp
|
||||
|
||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms $INSTR"
|
||||
|
||||
# Additional configure options and depends
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="$CONFIG --enable-manuals"
|
||||
fi
|
||||
|
||||
set +x
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo " =============================== osmo-trx ==============================="
|
||||
echo
|
||||
set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure
|
||||
./configure $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user