mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-03 05:33:16 +00:00
Compare commits
396 Commits
achemeris/
...
0.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
15f9d95f5f | ||
|
|
73dbccda78 | ||
|
|
5e65b531e0 | ||
|
|
b992d0a515 | ||
|
|
d6ae8648ff | ||
|
|
e51a8f029e | ||
|
|
e8ae9fcf38 | ||
|
|
f5bf33b287 | ||
|
|
fe9769833f | ||
|
|
7e07cf2346 | ||
|
|
dfe0aef184 | ||
|
|
131f82bfac | ||
|
|
78b5627fa1 | ||
|
|
de116e90c0 | ||
|
|
15da7e1f7e | ||
|
|
6031734f44 | ||
|
|
5d2a36a113 | ||
|
|
2af14407a8 | ||
|
|
92bdfb86ac | ||
|
|
ae91f13ecb | ||
|
|
9d53ecf666 | ||
|
|
e0c12189d4 | ||
|
|
1470fcdb5a | ||
|
|
6e1dffd486 | ||
|
|
0229d22d2e | ||
|
|
f7717acd0c | ||
|
|
b34e60c105 | ||
|
|
4e6c938024 | ||
|
|
4aa548f0c2 | ||
|
|
f9e78beea5 | ||
|
|
f0189c47be | ||
|
|
c708816be1 | ||
|
|
e56bf3a0e5 | ||
|
|
38b69871ae | ||
|
|
7db522b6d9 | ||
|
|
ae09b04e26 | ||
|
|
b61c610cd9 | ||
|
|
132fb247b1 | ||
|
|
1c0b8b355c | ||
|
|
1dd05cf35a | ||
|
|
14d13b67dc | ||
|
|
89bca9b2de | ||
|
|
9270a5aa2e | ||
|
|
4793f4679b | ||
|
|
802b86502d | ||
|
|
a93f789e50 | ||
|
|
72bf762b42 | ||
|
|
2dee3e996e | ||
|
|
1f1cebb2e5 | ||
|
|
d1b28bd766 | ||
|
|
833e97e9ba | ||
|
|
e6d059f0c9 | ||
|
|
012a1b345b | ||
|
|
80cb08071b | ||
|
|
44c7f41d75 | ||
|
|
9436fbbf3c | ||
|
|
93ca09ea61 | ||
|
|
365bc38bee | ||
|
|
43242efc85 | ||
|
|
76b98cf236 | ||
|
|
aa15d62a8c | ||
|
|
2e5e2c537b | ||
|
|
8f0ccf618d | ||
|
|
06676ead63 | ||
|
|
4609f3285c | ||
|
|
7c741ec6a6 | ||
|
|
2f3e60bc1f | ||
|
|
cbfef6e40a | ||
|
|
b577ef014f | ||
|
|
c37594f3b9 | ||
|
|
ffee30d190 | ||
|
|
24575a6530 | ||
|
|
1e9801411b | ||
|
|
64464e6c34 | ||
|
|
e88710881b | ||
|
|
a84e162672 | ||
|
|
7676427816 | ||
|
|
35222296fe | ||
|
|
28670fb5da | ||
|
|
05c6feb71d | ||
|
|
2e4ed10722 | ||
|
|
c8c4eac55e | ||
|
|
37c52c79cf | ||
|
|
58e9591f9e | ||
|
|
19174f581b | ||
|
|
1ba69e7762 | ||
|
|
f931cf226b | ||
|
|
e476231deb | ||
|
|
e90c24c8d5 | ||
|
|
3b093bb13b | ||
|
|
3f4a13f049 | ||
|
|
0fe41a583c | ||
|
|
a5e0f1cdba | ||
|
|
2c650a6895 | ||
|
|
d4555f267e | ||
|
|
047956259b | ||
|
|
d2b070369d | ||
|
|
9664c3a6e7 | ||
|
|
1ab5e7f7bc | ||
|
|
5efe05021a | ||
|
|
78d1fc9a13 | ||
|
|
a8cf208616 | ||
|
|
f84232d30a | ||
|
|
9bd649ec73 | ||
|
|
871b87829f | ||
|
|
d17b189cbc | ||
|
|
7fec3030d4 | ||
|
|
af717b2d3c | ||
|
|
8ee2f38a87 | ||
|
|
4dfd64aa9e | ||
|
|
b0aefcbf47 | ||
|
|
d325343ecc | ||
|
|
5cd70dc4ec | ||
|
|
465694027b | ||
|
|
2079a3c664 | ||
|
|
99cf930f9a | ||
|
|
283b22dbce | ||
|
|
f147b17447 | ||
|
|
d4d3daa12e | ||
|
|
c312905f43 | ||
|
|
c4eab8795f | ||
|
|
cc6f79b1c0 | ||
|
|
5a0680655f | ||
|
|
3722920100 | ||
|
|
f3b9af65ed | ||
|
|
e692ce986c | ||
|
|
81c6873205 | ||
|
|
c052aa1d4c | ||
|
|
130a8007fa | ||
|
|
72e8619632 | ||
|
|
2beb1adcea | ||
|
|
2b542100a0 | ||
|
|
2268c8558c | ||
|
|
50747dc65d | ||
|
|
1e9b4d57da | ||
|
|
954b118bfa | ||
|
|
dbe26abcb9 | ||
|
|
e8905a03a5 | ||
|
|
909ffbfd23 | ||
|
|
351fd76706 | ||
|
|
6a2bf0d87b | ||
|
|
2966048b07 | ||
|
|
f5fd578d60 | ||
|
|
57ef87d061 | ||
|
|
5721920a08 | ||
|
|
194a9b1982 | ||
|
|
1fe5282133 | ||
|
|
4438a9fd8f | ||
|
|
64ad712daa | ||
|
|
5c7c178369 | ||
|
|
90f7a01d1d | ||
|
|
e171425b99 | ||
|
|
4d029d8965 | ||
|
|
6613331459 | ||
|
|
577cd020c1 | ||
|
|
88bbf1aafd | ||
|
|
2cc2ddda41 | ||
|
|
d7610cf0b8 | ||
|
|
722d4f70a4 | ||
|
|
93b7f37309 | ||
|
|
4ad9ea69ab | ||
|
|
eb54bddf47 | ||
|
|
a4d1a41244 | ||
|
|
b999759175 | ||
|
|
1ae25561fa | ||
|
|
187225cf33 | ||
|
|
ccb73e15f3 | ||
|
|
a5c83aeb56 | ||
|
|
c283916e41 | ||
|
|
cecfb2e8f9 | ||
|
|
8e17df7374 | ||
|
|
3ebc772dc2 | ||
|
|
18d3b833bc | ||
|
|
e788239fba | ||
|
|
635f1bf2af | ||
|
|
15d743efaf | ||
|
|
af506441b3 | ||
|
|
4de70be9a6 | ||
|
|
fb827d04ba | ||
|
|
2c79110969 | ||
|
|
85b179d125 | ||
|
|
2e622ff131 | ||
|
|
3f32ab5afa | ||
|
|
8c33679fa5 | ||
|
|
477b77c558 | ||
|
|
d3fccea05f | ||
|
|
cb269a32dd | ||
|
|
1882099d15 | ||
|
|
a0179e37f8 | ||
|
|
ef25dba4e7 | ||
|
|
2d0c00bd3d | ||
|
|
e90a42becc | ||
|
|
30421a7e25 | ||
|
|
34bbef754f | ||
|
|
2c1f85a10c | ||
|
|
a2fe91a688 | ||
|
|
e1ce92599a | ||
|
|
b075dd2f73 | ||
|
|
94edaaeee6 | ||
|
|
0e0e1f4363 | ||
|
|
d0f3ca3e94 | ||
|
|
f8bc7c351f | ||
|
|
a4cf48cf8b | ||
|
|
7940335586 | ||
|
|
f79c4d06ff | ||
|
|
20eb6d64fd | ||
|
|
e0fa2bfd93 | ||
|
|
6f4906e375 | ||
|
|
0a3dc4c210 | ||
|
|
acc22fa3ff | ||
|
|
7553aa973f | ||
|
|
7e4e536b1b | ||
|
|
204a9f135a | ||
|
|
0169b310de | ||
|
|
66e2dd2543 | ||
|
|
f078273a8a | ||
|
|
d647ec5dc1 | ||
|
|
c289d7a409 | ||
|
|
035bee5c32 | ||
|
|
cf910dcdda | ||
|
|
69762fd7c6 | ||
|
|
fffd987f22 | ||
|
|
17bbb9b755 | ||
|
|
a1a3ab4bab | ||
|
|
fa3a787ccb | ||
|
|
010fff783b | ||
|
|
61b4a6ad9f | ||
|
|
de1648ca6b | ||
|
|
3952d80d05 | ||
|
|
fe269fe31d | ||
|
|
c064124429 | ||
|
|
69d14c9a14 | ||
|
|
c1f7c42a33 | ||
|
|
2c282f5e12 | ||
|
|
92c16df875 | ||
|
|
d5a80c3dc6 | ||
|
|
dafb33700e | ||
|
|
0e44ab360e | ||
|
|
092f757ec2 | ||
|
|
a57bc8a3b9 | ||
|
|
84c60f6679 | ||
|
|
865bca42d6 | ||
|
|
c88d8d53f8 | ||
|
|
9ccd9f2c3c | ||
|
|
8181b0104a | ||
|
|
9471d7635a | ||
|
|
03e6ecf977 | ||
|
|
3eaae80c90 | ||
|
|
e57004d0c3 | ||
|
|
6aa1f18f41 | ||
|
|
e5dcfc4f80 | ||
|
|
83e0689e76 | ||
|
|
d24cc2cd96 | ||
|
|
ddd6defb43 | ||
|
|
96794cb746 | ||
|
|
7e06806ff0 | ||
|
|
cb69f08410 | ||
|
|
312e387630 | ||
|
|
f2293b8cfa | ||
|
|
e3e8814948 | ||
|
|
02d88d1380 | ||
|
|
b4cb4e23c0 |
52
.gitignore
vendored
Normal file
52
.gitignore
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# build results
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
Transceiver52M/osmo-trx-uhd
|
||||
Transceiver52M/osmo-trx-usrp1
|
||||
|
||||
# tests
|
||||
tests/CommonLibs/BitVectorTest
|
||||
tests/CommonLibs/F16Test
|
||||
tests/CommonLibs/InterthreadTest
|
||||
tests/CommonLibs/LogTest
|
||||
tests/CommonLibs/RegexpTest
|
||||
tests/CommonLibs/SocketsTest
|
||||
tests/CommonLibs/TimevalTest
|
||||
tests/CommonLibs/URLEncodeTest
|
||||
tests/CommonLibs/VectorTest
|
||||
tests/CommonLibs/PRBSTest
|
||||
tests/Transceiver52M/convolve_test
|
||||
|
||||
# automake/autoconf
|
||||
*.in
|
||||
.deps
|
||||
.libs
|
||||
.dirstamp
|
||||
*~
|
||||
Makefile
|
||||
config.log
|
||||
config.status
|
||||
config.h
|
||||
config.guess
|
||||
config.sub
|
||||
config/*
|
||||
configure
|
||||
compile
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
depcomp
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
stamp-h1
|
||||
INSTALL
|
||||
tests/package.m4
|
||||
tests/testsuite
|
||||
tests/atconfig
|
||||
tests/testsuite.dir
|
||||
tests/testsuite.log
|
||||
|
||||
# vim
|
||||
*.sw?
|
||||
3
.gitreview
Normal file
3
.gitreview
Normal file
@@ -0,0 +1,3 @@
|
||||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=osmo-trx
|
||||
83
AUTHORS
83
AUTHORS
@@ -1,18 +1,18 @@
|
||||
#
|
||||
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
#
|
||||
#
|
||||
# This file is part of GNU Radio
|
||||
#
|
||||
#
|
||||
# GNU Radio is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
#
|
||||
# GNU Radio is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
@@ -23,34 +23,17 @@ David A. Burgess, dburgess@kestrelsp.com:
|
||||
CLI/CLI.h
|
||||
CommonLibs/Assert.h
|
||||
CommonLibs/BitVector.cpp
|
||||
CommonLibs/BitVectorTest.cpp
|
||||
CommonLibs/Configuration.cpp
|
||||
CommonLibs/Configuration.h
|
||||
CommonLibs/ConfigurationTest.cpp
|
||||
CommonLibs/Interthread.h
|
||||
CommonLibs/InterthreadTest.cpp
|
||||
CommonLibs/LinkedLists.cpp
|
||||
CommonLibs/LinkedLists.h
|
||||
CommonLibs/Regexp.h
|
||||
CommonLibs/RegexpTest.cpp
|
||||
CommonLibs/Sockets.cpp
|
||||
CommonLibs/Sockets.h
|
||||
CommonLibs/SocketsTest.cpp
|
||||
CommonLibs/Threads.cpp
|
||||
CommonLibs/Threads.h
|
||||
CommonLibs/Timeval.cpp
|
||||
CommonLibs/Timeval.h
|
||||
CommonLibs/TimevalTest.cpp
|
||||
CommonLibs/Vector.h
|
||||
CommonLibs/VectorTest.cpp
|
||||
Control/CallControl.cpp
|
||||
Control/ControlCommon.cpp
|
||||
Control/ControlCommon.h
|
||||
Control/FACCHDispatch.cpp
|
||||
Control/MobilityManagement.cpp
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
Control/SDCCHDispatch.cpp
|
||||
GSM/GSM610Tables.cpp
|
||||
GSM/GSM610Tables.h
|
||||
GSM/GSMCommon.cpp
|
||||
@@ -82,29 +65,15 @@ David A. Burgess, dburgess@kestrelsp.com:
|
||||
GSM/GSMTransfer.cpp
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
SIP/SIPEngine.h
|
||||
SIP/SIPInterface.h
|
||||
SMS/SMSMessages.cpp
|
||||
SMS/SMSMessages.h
|
||||
SMS/SMSTransfer.cpp
|
||||
SMS/SMSTransfer.h
|
||||
TRXManager/TRXManager.cpp
|
||||
Transceiver/Complex.h
|
||||
apps/OpenBTS900.cpp
|
||||
apps/OpenBTS850.cpp
|
||||
apps/OpenBTS25c3.cpp
|
||||
tests/AGCHTest.cpp
|
||||
tests/BeaconTest.cpp
|
||||
tests/CallTest.cpp
|
||||
tests/CallTest2.cpp
|
||||
tests/LAPDmTest.cpp
|
||||
tests/LoopbackTest.cpp
|
||||
tests/RegistrationTest.cpp
|
||||
tests/TRXSimulator.cpp
|
||||
tests/CommonLibs/BitVectorTest.cpp
|
||||
tests/CommonLibs/InterthreadTest.cpp
|
||||
tests/CommonLibs/SocketsTest.cpp
|
||||
tests/CommonLibs/TimevalTest.cpp
|
||||
tests/CommonLibs/VectorTest.cpp
|
||||
|
||||
Harvind S. Samra, hssamra@kestrelsp.com:
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
@@ -126,13 +95,6 @@ Harvind S. Samra, hssamra@kestrelsp.com:
|
||||
Transceiver/testRadio.cpp
|
||||
|
||||
Raffi Sevlian, raffisev@gmail.com:
|
||||
Control/CallControl.cpp
|
||||
Control/ControlCommon.cpp
|
||||
Control/ControlCommon.h
|
||||
Control/FACCHDispatch.cpp
|
||||
Control/MobilityManagement.cpp
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
GSM/GSMCommon.h
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSML1FEC.h
|
||||
@@ -157,36 +119,9 @@ Raffi Sevlian, raffisev@gmail.com:
|
||||
GSM/GSMSAPMux.h
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
SIP/SIPEngine.cpp
|
||||
SIP/SIPInterface.cpp
|
||||
SIP/SIPInterface.h
|
||||
SIP/SIPMessage.cpp
|
||||
SIP/SIPMessage.h
|
||||
SIP/SIPUtility.cpp
|
||||
SIP/SIPUtility.h
|
||||
SMS/CMMessage.cpp
|
||||
SMS/CMMessage.h
|
||||
SMS/CMProcessor.cpp
|
||||
SMS/CMProcessor.h
|
||||
SMS/CMTest.cpp
|
||||
SMS/RLMessage.cpp
|
||||
SMS/RLMessage.h
|
||||
SMS/RLProcessor.cpp
|
||||
SMS/RLProcessor.h
|
||||
SMS/SMSMessages.cpp
|
||||
SMS/SMSMessages.h
|
||||
SMS/SMSProcessors.cpp
|
||||
SMS/SMSProcessors.h
|
||||
SMS/SMSTransfer.cpp
|
||||
SMS/SMSTransfer.h
|
||||
SMS/TLMessage.cpp
|
||||
SMS/TLMessage.h
|
||||
SMS/TLProcessor.cpp
|
||||
SMS/TLProcessor.h
|
||||
TRXManager/TRXManager.h
|
||||
|
||||
Alon Levy, alonlevy1@gmail.com
|
||||
RRLPMessages.cpp
|
||||
RRLPMessages.h
|
||||
RRLPTest.cpp
|
||||
|
||||
|
||||
28
COPYING
28
COPYING
@@ -673,16 +673,16 @@ on the AGPLv3 text.
|
||||
=========================================================================
|
||||
|
||||
|
||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS
|
||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX
|
||||
|
||||
|
||||
Permissive Terms Supplementing the License
|
||||
|
||||
1. Remote Interaction Through IP Networks.
|
||||
|
||||
OpenBTS includes an implementation of the GSM network cellular air interface,
|
||||
OsmoTRX is an implementation of the GSM network cellular air interface,
|
||||
as well as other interfaces to IP networks. The interaction of cellular
|
||||
handsets with the OpenBTS software is considered "remote network interaction"
|
||||
handsets with the OsmoTRX software is considered "remote network interaction"
|
||||
for the purposes of the Affero General Public License and cellular users are
|
||||
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
|
||||
Network Interaction; Use with the GNU General Public License").
|
||||
@@ -694,17 +694,6 @@ interfaces other than the GSM air interface from the requirements of Section 13
|
||||
is an additional permission granted to you.
|
||||
|
||||
|
||||
Non-Permissive Terms Supplementing The License
|
||||
|
||||
1. Trademarks.
|
||||
|
||||
"OpenBTS" is a trademark of Range Networks, Inc., registered with
|
||||
the US Patent and Trademark Office. Your use of OpenBTS software under a GPL
|
||||
license does not include the right to use the OpenBTS trademark in commerce.
|
||||
This additional non-permissive term is consistent with Section 7 of the AGPLv3
|
||||
license.
|
||||
|
||||
|
||||
END OF ADDITIONAL TERMS
|
||||
|
||||
|
||||
@@ -712,13 +701,8 @@ END OF ADDITIONAL TERMS
|
||||
How to comply with Section 13 of the AGPLv3 license.
|
||||
|
||||
The recommended method for compliance with Section 13 of the AGPLv3 license is
|
||||
to deliver a text message to each handset that attaches to the OpenBTS cellular
|
||||
network. At a minimum, that text message should include the string "OpenBTS
|
||||
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
|
||||
to deliver a text message to each handset that attaches to the cellular
|
||||
network which uses OsmoTRX. At a minimum, that text message should include the string
|
||||
"OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This
|
||||
message need not be delivered to handsets that are denied registration with the
|
||||
network, since those handsets have been denied service.
|
||||
|
||||
In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message"
|
||||
feature. See the OpenBTS.config.example file for more information on the use of
|
||||
this feature for AGPLv3 compliance.
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <math.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -199,49 +200,6 @@ void BitVector::LSB8MSB()
|
||||
|
||||
|
||||
|
||||
uint64_t BitVector::syndrome(Generator& gen) const
|
||||
{
|
||||
gen.clear();
|
||||
const char *dp = mStart;
|
||||
while (dp<mEnd) gen.syndromeShift(*dp++);
|
||||
return gen.state();
|
||||
}
|
||||
|
||||
|
||||
uint64_t BitVector::parity(Generator& gen) const
|
||||
{
|
||||
gen.clear();
|
||||
const char *dp = mStart;
|
||||
while (dp<mEnd) gen.encoderShift(*dp++);
|
||||
return gen.state();
|
||||
}
|
||||
|
||||
|
||||
void BitVector::encode(const ViterbiR2O4& coder, BitVector& target)
|
||||
{
|
||||
size_t sz = size();
|
||||
assert(sz*coder.iRate() == target.size());
|
||||
|
||||
// Build a "history" array where each element contains the full history.
|
||||
uint32_t history[sz];
|
||||
uint32_t accum = 0;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
accum = (accum<<1) | bit(i);
|
||||
history[i] = accum;
|
||||
}
|
||||
|
||||
// Look up histories in the pre-generated state table.
|
||||
char *op = target.begin();
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
unsigned index = coder.cMask() & history[i];
|
||||
for (unsigned g=0; g<coder.iRate(); g++) {
|
||||
*op++ = coder.stateTable(g,index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned BitVector::sum() const
|
||||
{
|
||||
unsigned sum = 0;
|
||||
@@ -287,148 +245,12 @@ ostream& operator<<(ostream& os, const BitVector& hv)
|
||||
|
||||
|
||||
|
||||
ViterbiR2O4::ViterbiR2O4()
|
||||
{
|
||||
assert(mDeferral < 32);
|
||||
mCoeffs[0] = 0x019;
|
||||
mCoeffs[1] = 0x01b;
|
||||
computeStateTables(0);
|
||||
computeStateTables(1);
|
||||
computeGeneratorTable();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::initializeStates()
|
||||
{
|
||||
for (unsigned i=0; i<mIStates; i++) clear(mSurvivors[i]);
|
||||
for (unsigned i=0; i<mNumCands; i++) clear(mCandidates[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::computeStateTables(unsigned g)
|
||||
{
|
||||
assert(g<mIRate);
|
||||
for (unsigned state=0; state<mIStates; state++) {
|
||||
// 0 input
|
||||
uint32_t inputVal = state<<1;
|
||||
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
|
||||
// 1 input
|
||||
inputVal |= 1;
|
||||
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
|
||||
}
|
||||
}
|
||||
|
||||
void ViterbiR2O4::computeGeneratorTable()
|
||||
{
|
||||
for (unsigned index=0; index<mIStates*2; index++) {
|
||||
mGeneratorTable[index] = (mStateTable[0][index]<<1) | mStateTable[1][index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::branchCandidates()
|
||||
{
|
||||
// Branch to generate new input states.
|
||||
const vCand *sp = mSurvivors;
|
||||
for (unsigned i=0; i<mNumCands; i+=2) {
|
||||
// extend and suffix
|
||||
const uint32_t iState0 = (sp->iState) << 1; // input state for 0
|
||||
const uint32_t iState1 = iState0 | 0x01; // input state for 1
|
||||
const uint32_t oStateShifted = (sp->oState) << mIRate; // shifted output
|
||||
const float cost = sp->cost;
|
||||
sp++;
|
||||
// 0 input extension
|
||||
mCandidates[i].cost = cost;
|
||||
mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask];
|
||||
mCandidates[i].iState = iState0;
|
||||
// 1 input extension
|
||||
mCandidates[i+1].cost = cost;
|
||||
mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask];
|
||||
mCandidates[i+1].iState = iState1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost)
|
||||
{
|
||||
const float *cTab[2] = {matchCost,mismatchCost};
|
||||
for (unsigned i=0; i<mNumCands; i++) {
|
||||
vCand& thisCand = mCandidates[i];
|
||||
// We examine input bits 2 at a time for a rate 1/2 coder.
|
||||
const unsigned mismatched = inSample ^ (thisCand.oState);
|
||||
thisCand.cost += cTab[mismatched&0x01][1] + cTab[(mismatched>>1)&0x01][0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViterbiR2O4::pruneCandidates()
|
||||
{
|
||||
const vCand* c1 = mCandidates; // 0-prefix
|
||||
const vCand* c2 = mCandidates + mIStates; // 1-prefix
|
||||
for (unsigned i=0; i<mIStates; i++) {
|
||||
if (c1[i].cost < c2[i].cost) mSurvivors[i] = c1[i];
|
||||
else mSurvivors[i] = c2[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const ViterbiR2O4::vCand& ViterbiR2O4::minCost() const
|
||||
{
|
||||
int minIndex = 0;
|
||||
float minCost = mSurvivors[0].cost;
|
||||
for (unsigned i=1; i<mIStates; i++) {
|
||||
const float thisCost = mSurvivors[i].cost;
|
||||
if (thisCost>=minCost) continue;
|
||||
minCost = thisCost;
|
||||
minIndex=i;
|
||||
}
|
||||
return mSurvivors[minIndex];
|
||||
}
|
||||
|
||||
|
||||
const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs)
|
||||
{
|
||||
branchCandidates();
|
||||
getSoftCostMetrics(inSample,probs,iprobs);
|
||||
pruneCandidates();
|
||||
return minCost();
|
||||
}
|
||||
|
||||
|
||||
uint64_t Parity::syndrome(const BitVector& receivedCodeword)
|
||||
{
|
||||
return receivedCodeword.syndrome(*this);
|
||||
}
|
||||
|
||||
|
||||
void Parity::writeParityWord(const BitVector& data, BitVector& parityTarget, bool invert)
|
||||
{
|
||||
uint64_t pWord = data.parity(*this);
|
||||
if (invert) pWord = ~pWord;
|
||||
parityTarget.fillField(0,pWord,size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SoftVector::SoftVector(const BitVector& source)
|
||||
{
|
||||
resize(source.size());
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
if (source.bit(i)) mStart[i]=1.0F;
|
||||
else mStart[i]=0.0F;
|
||||
else mStart[i]=-1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,102 +260,20 @@ BitVector SoftVector::sliced() const
|
||||
size_t sz = size();
|
||||
BitVector newSig(sz);
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
if (mStart[i]>0.5F) newSig[i]=1;
|
||||
if (mStart[i]>0.0F) newSig[i]=1;
|
||||
else newSig[i] = 0;
|
||||
}
|
||||
return newSig;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
|
||||
{
|
||||
const size_t sz = size();
|
||||
const unsigned deferral = decoder.deferral();
|
||||
const size_t ctsz = sz + deferral*decoder.iRate();
|
||||
assert(sz <= decoder.iRate()*target.size());
|
||||
|
||||
// Build a "history" array where each element contains the full history.
|
||||
uint32_t history[ctsz];
|
||||
{
|
||||
BitVector bits = sliced();
|
||||
uint32_t accum = 0;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
accum = (accum<<1) | bits.bit(i);
|
||||
history[i] = accum;
|
||||
}
|
||||
// Repeat last bit at the end.
|
||||
for (size_t i=sz; i<ctsz; i++) {
|
||||
accum = (accum<<1) | (accum & 0x01);
|
||||
history[i] = accum;
|
||||
}
|
||||
}
|
||||
|
||||
// Precompute metric tables.
|
||||
float matchCostTable[ctsz];
|
||||
float mismatchCostTable[ctsz];
|
||||
{
|
||||
const float *dp = mStart;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
// pVal is the probability that a bit is correct.
|
||||
// ipVal is the probability that a bit is incorrect.
|
||||
float pVal = dp[i];
|
||||
if (pVal>0.5F) pVal = 1.0F-pVal;
|
||||
float ipVal = 1.0F-pVal;
|
||||
// This is a cheap approximation to an ideal cost function.
|
||||
if (pVal<0.01F) pVal = 0.01;
|
||||
if (ipVal<0.01F) ipVal = 0.01;
|
||||
matchCostTable[i] = 0.25F/ipVal;
|
||||
mismatchCostTable[i] = 0.25F/pVal;
|
||||
}
|
||||
|
||||
// pad end of table with unknowns
|
||||
for (size_t i=sz; i<ctsz; i++) {
|
||||
matchCostTable[i] = 0.5F;
|
||||
mismatchCostTable[i] = 0.5F;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
decoder.initializeStates();
|
||||
// Each sample of history[] carries its history.
|
||||
// So we only have to process every iRate-th sample.
|
||||
const unsigned step = decoder.iRate();
|
||||
// input pointer
|
||||
const uint32_t *ip = history + step - 1;
|
||||
// output pointers
|
||||
char *op = target.begin();
|
||||
const char *const opt = target.end();
|
||||
// table pointers
|
||||
const float* match = matchCostTable;
|
||||
const float* mismatch = mismatchCostTable;
|
||||
size_t oCount = 0;
|
||||
while (op<opt) {
|
||||
// Viterbi algorithm
|
||||
assert(match-matchCostTable<sizeof(matchCostTable)/sizeof(matchCostTable[0])-1);
|
||||
assert(mismatch-mismatchCostTable<sizeof(mismatchCostTable)/sizeof(mismatchCostTable[0])-1);
|
||||
const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch);
|
||||
ip += step;
|
||||
match += step;
|
||||
mismatch += step;
|
||||
// output
|
||||
if (oCount>=deferral) *op++ = (minCost.iState >> deferral)&0x01;
|
||||
oCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// (pat) Added 6-22-2012
|
||||
float SoftVector::getEnergy(float *plow) const
|
||||
{
|
||||
const SoftVector &vec = *this;
|
||||
int len = vec.size();
|
||||
float avg = 0; float low = 1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
float bit = vec[i];
|
||||
float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
|
||||
float energy = fabsf(vec[i]);
|
||||
if (energy < low) low = energy;
|
||||
avg += energy/len;
|
||||
}
|
||||
@@ -545,8 +285,12 @@ float SoftVector::getEnergy(float *plow) const
|
||||
ostream& operator<<(ostream& os, const SoftVector& sv)
|
||||
{
|
||||
for (size_t i=0; i<sv.size(); i++) {
|
||||
if (sv[i]<0.25) os << "0";
|
||||
else if (sv[i]>0.75) os << "1";
|
||||
if (sv[i]<-0.5) os << "0";
|
||||
else if (sv[i]<-0.25) os << "o";
|
||||
else if (sv[i]<0.0) os << ".";
|
||||
else if (sv[i]>0.5) os << "1";
|
||||
else if (sv[i]>0.25) os << "|";
|
||||
else if (sv[i]>0.0) os << "'";
|
||||
else os << "-";
|
||||
}
|
||||
return os;
|
||||
|
||||
@@ -30,201 +30,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
class BitVector;
|
||||
class SoftVector;
|
||||
|
||||
|
||||
|
||||
/** Shift-register (LFSR) generator. */
|
||||
class Generator {
|
||||
|
||||
private:
|
||||
|
||||
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
|
||||
uint64_t mState; ///< shift register state. LSB is most recent.
|
||||
uint64_t mMask; ///< mask for reading state
|
||||
unsigned mLen; ///< number of bits used in shift register
|
||||
unsigned mLen_1; ///< mLen - 1
|
||||
|
||||
public:
|
||||
|
||||
Generator(uint64_t wCoeff, unsigned wLen)
|
||||
:mCoeff(wCoeff),mState(0),
|
||||
mMask((1ULL<<wLen)-1),
|
||||
mLen(wLen),mLen_1(wLen-1)
|
||||
{ assert(wLen<64); }
|
||||
|
||||
void clear() { mState=0; }
|
||||
|
||||
/**@name Accessors */
|
||||
//@{
|
||||
uint64_t state() const { return mState & mMask; }
|
||||
unsigned size() const { return mLen; }
|
||||
//@}
|
||||
|
||||
/**
|
||||
Calculate one bit of a syndrome.
|
||||
This is in the .h for inlining.
|
||||
*/
|
||||
void syndromeShift(unsigned inBit)
|
||||
{
|
||||
const unsigned fb = (mState>>(mLen_1)) & 0x01;
|
||||
mState = (mState<<1) ^ (inBit & 0x01);
|
||||
if (fb) mState ^= mCoeff;
|
||||
}
|
||||
|
||||
/**
|
||||
Update the generator state by one cycle.
|
||||
This is in the .h for inlining.
|
||||
*/
|
||||
void encoderShift(unsigned inBit)
|
||||
{
|
||||
const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01;
|
||||
mState <<= 1;
|
||||
if (fb) mState ^= mCoeff;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Parity (CRC-type) generator and checker based on a Generator. */
|
||||
class Parity : public Generator {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned mCodewordSize;
|
||||
|
||||
public:
|
||||
|
||||
Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize)
|
||||
:Generator(wCoefficients, wParitySize),
|
||||
mCodewordSize(wCodewordSize)
|
||||
{ }
|
||||
|
||||
/** Compute the parity word and write it into the target segment. */
|
||||
void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true);
|
||||
|
||||
/** Compute the syndrome of a received sequence. */
|
||||
uint64_t syndrome(const BitVector& receivedCodeword);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Class to represent convolutional coders/decoders of rate 1/2, memory length 4.
|
||||
This is the "workhorse" coder for most GSM channels.
|
||||
*/
|
||||
class ViterbiR2O4 {
|
||||
|
||||
private:
|
||||
/**name Lots of precomputed elements so the compiler can optimize like hell. */
|
||||
//@{
|
||||
/**@name Core values. */
|
||||
//@{
|
||||
static const unsigned mIRate = 2; ///< reciprocal of rate
|
||||
static const unsigned mOrder = 4; ///< memory length of generators
|
||||
//@}
|
||||
/**@name Derived values. */
|
||||
//@{
|
||||
static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors
|
||||
static const uint32_t mSMask = mIStates-1; ///< survivor mask
|
||||
static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask
|
||||
static const uint32_t mOMask = (0x01<<mIRate)-1; ///< ouput mask, all iRate low bits set
|
||||
static const unsigned mNumCands = mIStates*2; ///< number of candidates to generate during branching
|
||||
static const unsigned mDeferral = 6*mOrder; ///< deferral to be used
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/** Precomputed tables. */
|
||||
//@{
|
||||
uint32_t mCoeffs[mIRate]; ///< polynomial for each generator
|
||||
uint32_t mStateTable[mIRate][2*mIStates]; ///< precomputed generator output tables
|
||||
uint32_t mGeneratorTable[2*mIStates]; ///< precomputed coder output table
|
||||
//@}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
A candidate sequence in a Viterbi decoder.
|
||||
The 32-bit state register can support a deferral of 6 with a 4th-order coder.
|
||||
*/
|
||||
typedef struct candStruct {
|
||||
uint32_t iState; ///< encoder input associated with this candidate
|
||||
uint32_t oState; ///< encoder output associated with this candidate
|
||||
float cost; ///< cost (metric value), float to support soft inputs
|
||||
} vCand;
|
||||
|
||||
/** Clear a structure. */
|
||||
void clear(vCand& v)
|
||||
{
|
||||
v.iState=0;
|
||||
v.oState=0;
|
||||
v.cost=0;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**@name Survivors and candidates. */
|
||||
//@{
|
||||
vCand mSurvivors[mIStates]; ///< current survivor pool
|
||||
vCand mCandidates[2*mIStates]; ///< current candidate pool
|
||||
//@}
|
||||
|
||||
public:
|
||||
|
||||
unsigned iRate() const { return mIRate; }
|
||||
uint32_t cMask() const { return mCMask; }
|
||||
uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
|
||||
unsigned deferral() const { return mDeferral; }
|
||||
|
||||
|
||||
ViterbiR2O4();
|
||||
|
||||
/** Set all cost metrics to zero. */
|
||||
void initializeStates();
|
||||
|
||||
/**
|
||||
Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
|
||||
@return reference to minimum-cost candidate.
|
||||
*/
|
||||
const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
|
||||
|
||||
private:
|
||||
|
||||
/** Branch survivors into new candidates. */
|
||||
void branchCandidates();
|
||||
|
||||
/** Compute cost metrics for soft-inputs. */
|
||||
void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
|
||||
|
||||
/** Select survivors from the candidate set. */
|
||||
void pruneCandidates();
|
||||
|
||||
/** Find the minimum cost survivor. */
|
||||
const vCand& minCost() const;
|
||||
|
||||
/**
|
||||
Precompute the state tables.
|
||||
@param g Generator index 0..((1/rate)-1)
|
||||
*/
|
||||
void computeStateTables(unsigned g);
|
||||
|
||||
/**
|
||||
Precompute the generator outputs.
|
||||
mCoeffs must be defined first.
|
||||
*/
|
||||
void computeGeneratorTable();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class BitVector : public Vector<char> {
|
||||
|
||||
|
||||
@@ -282,16 +87,6 @@ class BitVector : public Vector<char> {
|
||||
|
||||
void zero() { fill(0); }
|
||||
|
||||
/**@name FEC operations. */
|
||||
//@{
|
||||
/** Calculate the syndrome of the vector with the given Generator. */
|
||||
uint64_t syndrome(Generator& gen) const;
|
||||
/** Calculate the parity word for the vector with the given Generator. */
|
||||
uint64_t parity(Generator& gen) const;
|
||||
/** Encode the signal with the GSM rate 1/2 convolutional encoder. */
|
||||
void encode(const ViterbiR2O4& encoder, BitVector& target);
|
||||
//@}
|
||||
|
||||
|
||||
/** Invert 0<->1. */
|
||||
void invert();
|
||||
@@ -427,23 +222,20 @@ class SoftVector: public Vector<float> {
|
||||
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
|
||||
//@}
|
||||
|
||||
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
|
||||
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
|
||||
|
||||
// (pat) How good is the SoftVector in the sense of the bits being solid?
|
||||
// Result of 1 is perfect and 0 means all the bits were 0.5
|
||||
// How good is the SoftVector in the sense of the bits being solid?
|
||||
// Result of 1 is perfect and 0 means all the bits were 0.0
|
||||
// If plow is non-NULL, also return the lowest energy bit.
|
||||
float getEnergy(float *low=0) const;
|
||||
|
||||
/** Fill with "unknown" values. */
|
||||
void unknown() { fill(0.5F); }
|
||||
void unknown() { fill(0.0F); }
|
||||
|
||||
/** Return a hard bit value from a given index by slicing. */
|
||||
bool bit(size_t index) const
|
||||
{
|
||||
const float *dp = mStart+index;
|
||||
assert(dp<mEnd);
|
||||
return (*dp)>0.5F;
|
||||
return (*dp)>0.0F;
|
||||
}
|
||||
|
||||
/** Slice the whole signal into bits. */
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "BitVector.h"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BitVector v1("0000111100111100101011110000");
|
||||
cout << v1 << endl;
|
||||
v1.LSB8MSB();
|
||||
cout << v1 << endl;
|
||||
ViterbiR2O4 vCoder;
|
||||
BitVector v2(v1.size()*2);
|
||||
v1.encode(vCoder,v2);
|
||||
cout << v2 << endl;
|
||||
SoftVector sv2(v2);
|
||||
cout << sv2 << endl;
|
||||
for (unsigned i=0; i<sv2.size()/4; i++) sv2[random()%sv2.size()]=0.5;
|
||||
cout << sv2 << endl;
|
||||
BitVector v3(v1.size());
|
||||
sv2.decode(vCoder,v3);
|
||||
cout << v3 << endl;
|
||||
|
||||
cout << v3.segment(3,4) << endl;
|
||||
|
||||
BitVector v4(v3.segment(0,4),v3.segment(8,4));
|
||||
cout << v4 << endl;
|
||||
|
||||
BitVector v5("000011110000");
|
||||
int r1 = v5.peekField(0,8);
|
||||
int r2 = v5.peekField(4,4);
|
||||
int r3 = v5.peekField(4,8);
|
||||
cout << r1 << ' ' << r2 << ' ' << r3 << endl;
|
||||
cout << v5 << endl;
|
||||
v5.fillField(0,0xa,4);
|
||||
int r4 = v5.peekField(0,8);
|
||||
cout << v5 << endl;
|
||||
cout << r4 << endl;
|
||||
|
||||
v5.reverse8();
|
||||
cout << v5 << endl;
|
||||
|
||||
BitVector mC = "000000000000111100000000000001110000011100001101000011000000000000000111000011110000100100001010000010100000101000001010000010100000010000000000000000000000000000000000000000000000001100001111000000000000000000000000000000000000000000000000000010010000101000001010000010100000101000001010000001000000000000000000000000110000111100000000000001110000101000001100000001000000000000";
|
||||
SoftVector mCS(mC);
|
||||
BitVector mU(mC.size()/2);
|
||||
mCS.decode(vCoder,mU);
|
||||
cout << "c=" << mCS << endl;
|
||||
cout << "u=" << mU << endl;
|
||||
|
||||
|
||||
unsigned char ts[9] = "abcdefgh";
|
||||
BitVector tp(70);
|
||||
cout << "ts=" << ts << endl;
|
||||
tp.unpack(ts);
|
||||
cout << "tp=" << tp << endl;
|
||||
tp.pack(ts);
|
||||
cout << "ts=" << ts << endl;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011, 2012 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONFIGURATION_H
|
||||
#define CONFIGURATION_H
|
||||
|
||||
|
||||
#include "sqlite3util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <Threads.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/** A class for configuration file errors. */
|
||||
class ConfigurationTableError {};
|
||||
extern char gCmdName[]; // Gotta be global, gotta be char*, gotta love it.
|
||||
|
||||
/** An exception thrown when a given config key isn't found. */
|
||||
class ConfigurationTableKeyNotFound : public ConfigurationTableError {
|
||||
|
||||
private:
|
||||
|
||||
std::string mKey;
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationTableKeyNotFound(const std::string& wKey)
|
||||
:mKey(wKey)
|
||||
{ }
|
||||
|
||||
const std::string& key() const { return mKey; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ConfigurationRecord {
|
||||
|
||||
private:
|
||||
|
||||
std::string mValue;
|
||||
long mNumber;
|
||||
bool mDefined;
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationRecord(bool wDefined=true):
|
||||
mDefined(wDefined)
|
||||
{ }
|
||||
|
||||
ConfigurationRecord(const std::string& wValue):
|
||||
mValue(wValue),
|
||||
mNumber(strtol(wValue.c_str(),NULL,0)),
|
||||
mDefined(true)
|
||||
{ }
|
||||
|
||||
ConfigurationRecord(const char* wValue):
|
||||
mValue(std::string(wValue)),
|
||||
mNumber(strtol(wValue,NULL,0)),
|
||||
mDefined(true)
|
||||
{ }
|
||||
|
||||
|
||||
const std::string& value() const { return mValue; }
|
||||
long number() const { return mNumber; }
|
||||
bool defined() const { return mDefined; }
|
||||
|
||||
float floatNumber() const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** A string class that uses a hash function for comparison. */
|
||||
class HashString : public std::string {
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
uint64_t mHash;
|
||||
|
||||
void computeHash();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
HashString(const char* src)
|
||||
:std::string(src)
|
||||
{
|
||||
computeHash();
|
||||
}
|
||||
|
||||
HashString(const std::string& src)
|
||||
:std::string(src)
|
||||
{
|
||||
computeHash();
|
||||
}
|
||||
|
||||
HashString()
|
||||
{
|
||||
mHash=0;
|
||||
}
|
||||
|
||||
HashString& operator=(std::string& src)
|
||||
{
|
||||
std::string::operator=(src);
|
||||
computeHash();
|
||||
return *this;
|
||||
}
|
||||
|
||||
HashString& operator=(const char* src)
|
||||
{
|
||||
std::string::operator=(src);
|
||||
computeHash();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const HashString& other)
|
||||
{
|
||||
return mHash==other.mHash;
|
||||
}
|
||||
|
||||
bool operator<(const HashString& other)
|
||||
{
|
||||
return mHash<other.mHash;
|
||||
}
|
||||
|
||||
bool operator>(const HashString& other)
|
||||
{
|
||||
return mHash<other.mHash;
|
||||
}
|
||||
|
||||
uint64_t hash() const { return mHash; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap;
|
||||
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
|
||||
class ConfigurationKey;
|
||||
typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap;
|
||||
|
||||
/**
|
||||
A class for maintaining a configuration key-value table,
|
||||
based on sqlite3 and a local map-based cache.
|
||||
Thread-safe, too.
|
||||
*/
|
||||
class ConfigurationTable {
|
||||
|
||||
private:
|
||||
|
||||
sqlite3* mDB; ///< database connection
|
||||
ConfigurationMap mCache; ///< cache of recently access configuration values
|
||||
mutable Mutex mLock; ///< control for multithreaded access to the cache
|
||||
std::vector<std::string> (*mCrossCheck)(const std::string&); ///< cross check callback pointer
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic
|
||||
|
||||
ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap());
|
||||
|
||||
/** Generate an up-to-date example sql file for new installs. */
|
||||
std::string getDefaultSQL(const std::string& program, const std::string& version);
|
||||
|
||||
/** Generate an up-to-date TeX snippet. */
|
||||
std::string getTeX(const std::string& program, const std::string& version);
|
||||
|
||||
/** Return true if the key is used in the table. */
|
||||
bool defines(const std::string& key);
|
||||
|
||||
/** Return true if the application's schema knows about this key. */
|
||||
bool keyDefinedInSchema(const std::string& name);
|
||||
|
||||
/** Return true if the provided value validates correctly against the defined schema. */
|
||||
bool isValidValue(const std::string& name, const std::string& val);
|
||||
|
||||
/** Return true if the provided value validates correctly against the defined schema. */
|
||||
bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); }
|
||||
|
||||
/** Return a map of all similar keys in the defined schema. */
|
||||
ConfigurationKeyMap getSimilarKeys(const std::string& snippet);
|
||||
|
||||
/** Return true if this key is identified as static. */
|
||||
bool isStatic(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a string parameter from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
std::string getStr(const std::string& key);
|
||||
|
||||
|
||||
/**
|
||||
Get a boolean from the table.
|
||||
Return false if NULL or 0, true otherwise.
|
||||
*/
|
||||
bool getBool(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a numeric parameter from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
long getNum(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a vector of strings from the table.
|
||||
*/
|
||||
std::vector<std::string> getVectorOfStrings(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a float from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
float getFloat(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a numeric vector from the table.
|
||||
*/
|
||||
std::vector<unsigned> getVector(const std::string& key);
|
||||
|
||||
/** Get length of a vector */
|
||||
unsigned getVectorLength(const std::string &key)
|
||||
{ return getVector(key).size(); }
|
||||
|
||||
/** Set or change a value in the table. */
|
||||
bool set(const std::string& key, const std::string& value);
|
||||
|
||||
/** Set or change a value in the table. */
|
||||
bool set(const std::string& key, long value);
|
||||
|
||||
/** Create an entry in the table, no value though. */
|
||||
bool set(const std::string& key);
|
||||
|
||||
/**
|
||||
Remove an entry from the table.
|
||||
Will not alter required values.
|
||||
@param key The key of the item to be removed.
|
||||
@return true if anything was actually removed.
|
||||
*/
|
||||
bool remove(const std::string& key);
|
||||
|
||||
/** Search the table, dumping to a stream. */
|
||||
void find(const std::string& pattern, std::ostream&) const;
|
||||
|
||||
/** Return all key/value pairs stored in the ConfigurationTable */
|
||||
ConfigurationRecordMap getAllPairs() const;
|
||||
|
||||
/** Define the callback to purge the cache whenever the database changes. */
|
||||
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
|
||||
|
||||
/** Define the callback for cross checking. */
|
||||
void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&));
|
||||
|
||||
/** Execute the application specific value cross checking logic. */
|
||||
std::vector<std::string> crossCheck(const std::string& key);
|
||||
|
||||
/** purege cache if it exceeds a certain age */
|
||||
void checkCacheAge();
|
||||
|
||||
/** Delete all records from the cache. */
|
||||
void purge();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
Attempt to lookup a record, cache if needed.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
Caller should hold mLock because the returned reference points into the cache.
|
||||
*/
|
||||
const ConfigurationRecord& lookup(const std::string& key);
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<HashString, std::string> HashStringMap;
|
||||
|
||||
class SimpleKeyValue {
|
||||
|
||||
protected:
|
||||
|
||||
HashStringMap mMap;
|
||||
|
||||
public:
|
||||
|
||||
/** Take a C string "A=B" and set map["A"]="B". */
|
||||
void addItem(const char*);
|
||||
|
||||
/** Take a C string "A=B C=D E=F ..." and add all of the pairs to the map. */
|
||||
void addItems(const char*s);
|
||||
|
||||
/** Return a reference to the string at map["key"]. */
|
||||
const char* get(const char*) const;
|
||||
};
|
||||
|
||||
|
||||
class ConfigurationKey {
|
||||
|
||||
public:
|
||||
|
||||
enum VisibilityLevel
|
||||
{
|
||||
CUSTOMER,
|
||||
CUSTOMERSITE,
|
||||
CUSTOMERTUNE,
|
||||
CUSTOMERWARN,
|
||||
DEVELOPER,
|
||||
FACTORY
|
||||
};
|
||||
|
||||
enum Type
|
||||
{
|
||||
BOOLEAN,
|
||||
CHOICE_OPT,
|
||||
CHOICE,
|
||||
CIDR_OPT,
|
||||
CIDR,
|
||||
FILEPATH_OPT,
|
||||
FILEPATH,
|
||||
IPADDRESS_OPT,
|
||||
IPADDRESS,
|
||||
IPANDPORT,
|
||||
MIPADDRESS_OPT,
|
||||
MIPADDRESS,
|
||||
PORT_OPT,
|
||||
PORT,
|
||||
REGEX_OPT,
|
||||
REGEX,
|
||||
STRING_OPT,
|
||||
STRING,
|
||||
VALRANGE
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
std::string mName;
|
||||
std::string mDefaultValue;
|
||||
std::string mUnits;
|
||||
VisibilityLevel mVisibility;
|
||||
Type mType;
|
||||
std::string mValidValues;
|
||||
bool mIsStatic;
|
||||
std::string mDescription;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription):
|
||||
mName(wName),
|
||||
mDefaultValue(wDefaultValue),
|
||||
mUnits(wUnits),
|
||||
mVisibility(wVisibility),
|
||||
mType(wType),
|
||||
mValidValues(wValidValues),
|
||||
mIsStatic(wIsStatic),
|
||||
mDescription(wDescription)
|
||||
{ }
|
||||
|
||||
ConfigurationKey()
|
||||
{ }
|
||||
|
||||
const std::string& getName() const { return mName; }
|
||||
const std::string& getDefaultValue() const { return mDefaultValue; }
|
||||
void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; }
|
||||
void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); }
|
||||
const std::string& getUnits() const { return mUnits; }
|
||||
const VisibilityLevel& getVisibility() const { return mVisibility; }
|
||||
const Type& getType() const { return mType; }
|
||||
const std::string& getValidValues() const { return mValidValues; }
|
||||
bool isStatic() const { return mIsStatic; }
|
||||
const std::string& getDescription() const { return mDescription; }
|
||||
|
||||
static bool isValidIP(const std::string& ip);
|
||||
static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping);
|
||||
template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger);
|
||||
static const std::string visibilityLevelToString(const VisibilityLevel& visibility);
|
||||
static const std::string typeToString(const ConfigurationKey::Type& type);
|
||||
static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os);
|
||||
static void printDescription(const ConfigurationKey &key, std::ostream& os);
|
||||
static const std::string getARFCNsString();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Configuration.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ConfigurationKeyMap getConfigurationKeys();
|
||||
ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys());
|
||||
|
||||
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
|
||||
{
|
||||
//cout << "update hook" << endl;
|
||||
gConfig.purge();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
gConfig.setUpdateHook(purgeConfig);
|
||||
|
||||
char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
gConfig.set(keys[i],i);
|
||||
}
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
cout << "table[" << keys[i] << "]=" << gConfig.getStr(keys[i]) << endl;
|
||||
cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl;
|
||||
}
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl;
|
||||
}
|
||||
|
||||
gConfig.set("key5","100 200 300 400 ");
|
||||
std::vector<unsigned> vect = gConfig.getVector("key5");
|
||||
cout << "vect length " << vect.size() << ": ";
|
||||
for (unsigned i=0; i<vect.size(); i++) cout << " " << vect[i];
|
||||
cout << endl;
|
||||
std::vector<string> svect = gConfig.getVectorOfStrings("key5");
|
||||
cout << "vect length " << svect.size() << ": ";
|
||||
for (unsigned i=0; i<svect.size(); i++) cout << " " << svect[i] << ":";
|
||||
cout << endl;
|
||||
|
||||
cout << "bool " << gConfig.getBool("booltest") << endl;
|
||||
gConfig.set("booltest",1);
|
||||
cout << "bool " << gConfig.getBool("booltest") << endl;
|
||||
gConfig.set("booltest",0);
|
||||
cout << "bool " << gConfig.getBool("booltest") << endl;
|
||||
|
||||
gConfig.getStr("newstring");
|
||||
gConfig.getNum("numnumber");
|
||||
|
||||
|
||||
SimpleKeyValue pairs;
|
||||
pairs.addItems(" a=1 b=34 dd=143 ");
|
||||
cout<< pairs.get("a") << endl;
|
||||
cout<< pairs.get("b") << endl;
|
||||
cout<< pairs.get("dd") << endl;
|
||||
|
||||
gConfig.set("fkey","123.456");
|
||||
float fval = gConfig.getFloat("fkey");
|
||||
cout << "fkey " << fval << endl;
|
||||
|
||||
cout << "search fkey:" << endl;
|
||||
gConfig.find("fkey",cout);
|
||||
cout << "search fkey:" << endl;
|
||||
gConfig.find("fkey",cout);
|
||||
gConfig.remove("fkey");
|
||||
cout << "search fkey:" << endl;
|
||||
gConfig.find("fkey",cout);
|
||||
|
||||
try {
|
||||
gConfig.getNum("supposedtoabort");
|
||||
} catch (ConfigurationTableKeyNotFound) {
|
||||
cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurationKeyMap getConfigurationKeys()
|
||||
{
|
||||
ConfigurationKeyMap map;
|
||||
ConfigurationKey *tmp;
|
||||
|
||||
tmp = new ConfigurationKey("booltest","0",
|
||||
"",
|
||||
ConfigurationKey::DEVELOPER,
|
||||
ConfigurationKey::BOOLEAN,
|
||||
"",
|
||||
false,
|
||||
""
|
||||
);
|
||||
map[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
|
||||
tmp = new ConfigurationKey("numnumber","42",
|
||||
"",
|
||||
ConfigurationKey::DEVELOPER,
|
||||
ConfigurationKey::VALRANGE,
|
||||
"0-100",
|
||||
false,
|
||||
""
|
||||
);
|
||||
map[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
|
||||
tmp = new ConfigurationKey("newstring","new string value",
|
||||
"",
|
||||
ConfigurationKey::DEVELOPER,
|
||||
ConfigurationKey::STRING,
|
||||
"",
|
||||
false,
|
||||
""
|
||||
);
|
||||
map[tmp->getName()] = *tmp;
|
||||
free(tmp);
|
||||
|
||||
return map;
|
||||
}
|
||||
210
CommonLibs/F16.h
210
CommonLibs/F16.h
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef F16_H
|
||||
#define F16_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ostream>
|
||||
|
||||
|
||||
|
||||
/** Round a float to the appropriate F16 value. */
|
||||
inline int32_t _f16_round(float f)
|
||||
{
|
||||
if (f>0.0F) return (int32_t)(f+0.5F);
|
||||
if (f<0.0F) return (int32_t)(f-0.5F);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** A class for F15.16 fixed point arithmetic with saturation. */
|
||||
class F16 {
|
||||
|
||||
|
||||
private:
|
||||
|
||||
int32_t mV;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
F16() {}
|
||||
|
||||
F16(int i) { mV = i<<16; }
|
||||
F16(float f) { mV = _f16_round(f*65536.0F); }
|
||||
F16(double f) { mV = _f16_round((float)f*65536.0F); }
|
||||
|
||||
int32_t& raw() { return mV; }
|
||||
const int32_t& raw() const { return mV; }
|
||||
|
||||
float f() const { return mV/65536.0F; }
|
||||
|
||||
//operator float() const { return mV/65536.0F; }
|
||||
//operator int() const { return mV>>16; }
|
||||
|
||||
F16 operator=(float f)
|
||||
{
|
||||
mV = _f16_round(f*65536.0F);
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator=(int i)
|
||||
{
|
||||
mV = i<<16;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator=(const F16& other)
|
||||
{
|
||||
mV = other.mV;
|
||||
return mV;
|
||||
}
|
||||
|
||||
F16 operator+(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV + other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator+=(const F16& other)
|
||||
{
|
||||
mV += other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator-(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV - other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator-=(const F16& other)
|
||||
{
|
||||
mV -= other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator*(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
int64_t p = (int64_t)mV * (int64_t)other.mV;
|
||||
retVal.mV = p>>16;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator*=(const F16& other)
|
||||
{
|
||||
int64_t p = (int64_t)mV * (int64_t)other.mV;
|
||||
mV = p>>16;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator*(float f) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV * f;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator*=(float f)
|
||||
{
|
||||
mV *= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator/(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
int64_t pV = (int64_t)mV << 16;
|
||||
retVal.mV = pV / other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator/=(const F16& other)
|
||||
{
|
||||
int64_t pV = (int64_t)mV << 16;
|
||||
mV = pV / other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator/(float f) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV / f;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator/=(float f)
|
||||
{
|
||||
mV /= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator>(const F16& other) const
|
||||
{
|
||||
return mV>other.mV;
|
||||
}
|
||||
|
||||
bool operator<(const F16& other) const
|
||||
{
|
||||
return mV<other.mV;
|
||||
}
|
||||
|
||||
bool operator==(const F16& other) const
|
||||
{
|
||||
return mV==other.mV;
|
||||
}
|
||||
|
||||
bool operator>(float f) const
|
||||
{
|
||||
return (mV/65536.0F) > f;
|
||||
}
|
||||
|
||||
bool operator<(float f) const
|
||||
{
|
||||
return (mV/65536.0F) < f;
|
||||
}
|
||||
|
||||
bool operator==(float f) const
|
||||
{
|
||||
return (mV/65536.0F) == f;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const F16& v)
|
||||
{
|
||||
os << v.f();
|
||||
return os;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "F16.h"
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
F16 a = 2.5;
|
||||
F16 b = 1.5;
|
||||
F16 c = 2.5 * 1.5;
|
||||
F16 d = c + a;
|
||||
F16 e = 10;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << endl;
|
||||
|
||||
a *= 3;
|
||||
b *= 0.3;
|
||||
c *= e;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
|
||||
|
||||
a /= 3;
|
||||
b /= 0.3;
|
||||
c = d * 0.05;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
|
||||
|
||||
F16 f = a/d;
|
||||
cout << f << ' ' << f+0.5 << endl;
|
||||
}
|
||||
@@ -1,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.
|
||||
@@ -30,248 +28,34 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h> // For gettimeofday
|
||||
|
||||
#include "Configuration.h"
|
||||
#include "Logger.h"
|
||||
#include "Threads.h" // pat added
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Reference to a global config table, used all over the system.
|
||||
extern ConfigurationTable gConfig;
|
||||
|
||||
|
||||
/**@ The global alarms table. */
|
||||
//@{
|
||||
Mutex alarmsLock;
|
||||
list<string> alarmsList;
|
||||
void addAlarm(const string&);
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
// (pat) If Log messages are printed before the classes in this module are inited
|
||||
// (which happens when static classes have constructors that do work)
|
||||
// the OpenBTS just crashes.
|
||||
// Prevent that by setting sLoggerInited to true when this module is inited.
|
||||
static bool sLoggerInited = 0;
|
||||
static struct CheckLoggerInitStatus {
|
||||
CheckLoggerInitStatus() { sLoggerInited = 1; }
|
||||
} sCheckloggerInitStatus;
|
||||
|
||||
|
||||
|
||||
/** Names of the logging levels. */
|
||||
const char *levelNames[] = {
|
||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||
};
|
||||
int numLevels = 8;
|
||||
bool gLogToConsole = 0;
|
||||
FILE *gLogToFile = NULL;
|
||||
Mutex gLogToLock;
|
||||
|
||||
|
||||
int levelStringToInt(const string& name)
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
|
||||
{
|
||||
// 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;
|
||||
return os << ss.str();
|
||||
}
|
||||
|
||||
/** Given a string, return the corresponding level name. */
|
||||
int lookupLevel(const string& key)
|
||||
{
|
||||
string val = gConfig.getStr(key);
|
||||
int level = levelStringToInt(val);
|
||||
|
||||
if (level == -1) {
|
||||
string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
|
||||
level = levelStringToInt(defaultLevel);
|
||||
_LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
|
||||
gConfig.set(key, defaultLevel);
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
int getLoggingLevel(const char* filename)
|
||||
{
|
||||
// Default level?
|
||||
if (!filename) return lookupLevel("Log.Level");
|
||||
|
||||
// This can afford to be inefficient since it is not called that often.
|
||||
const string keyName = string("Log.Level.") + string(filename);
|
||||
if (gConfig.defines(keyName)) return lookupLevel(keyName);
|
||||
return lookupLevel("Log.Level");
|
||||
}
|
||||
|
||||
|
||||
|
||||
int gGetLoggingLevel(const char* filename)
|
||||
{
|
||||
// This is called a lot and needs to be efficient.
|
||||
|
||||
static Mutex sLogCacheLock;
|
||||
static map<uint64_t,int> sLogCache;
|
||||
static unsigned sCacheCount;
|
||||
static const unsigned sCacheRefreshCount = 1000;
|
||||
|
||||
if (filename==NULL) return gGetLoggingLevel("");
|
||||
|
||||
HashString hs(filename);
|
||||
uint64_t key = hs.hash();
|
||||
|
||||
sLogCacheLock.lock();
|
||||
// Time for a cache flush?
|
||||
if (sCacheCount>sCacheRefreshCount) {
|
||||
sLogCache.clear();
|
||||
sCacheCount=0;
|
||||
}
|
||||
// Is it cached already?
|
||||
map<uint64_t,int>::const_iterator where = sLogCache.find(key);
|
||||
sCacheCount++;
|
||||
if (where!=sLogCache.end()) {
|
||||
int retVal = where->second;
|
||||
sLogCacheLock.unlock();
|
||||
return retVal;
|
||||
}
|
||||
// Look it up in the config table and cache it.
|
||||
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
|
||||
// (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
|
||||
sLogCacheLock.unlock();
|
||||
int level = getLoggingLevel(filename);
|
||||
sLogCacheLock.lock();
|
||||
sLogCache.insert(pair<uint64_t,int>(key,level));
|
||||
sLogCacheLock.unlock();
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// copies the alarm list and returns it. list supposed to be small.
|
||||
list<string> gGetLoggerAlarms()
|
||||
{
|
||||
alarmsLock.lock();
|
||||
list<string> ret;
|
||||
// excuse the "complexity", but to use std::copy with a list you need
|
||||
// an insert_iterator - copy technically overwrites, doesn't insert.
|
||||
insert_iterator< list<string> > ii(ret, ret.begin());
|
||||
copy(alarmsList.begin(), alarmsList.end(), ii);
|
||||
alarmsLock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Add an alarm to the alarm list. */
|
||||
void addAlarm(const string& s)
|
||||
{
|
||||
alarmsLock.lock();
|
||||
alarmsList.push_back(s);
|
||||
unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
|
||||
while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
|
||||
alarmsLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
if (mDummyInit) return;
|
||||
// Anything at or above LOG_CRIT is an "alarm".
|
||||
// Save alarms in the local list and echo them to stderr.
|
||||
if (mPriority <= LOG_CRIT) {
|
||||
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||
cerr << mStream.str() << endl;
|
||||
}
|
||||
// Current logging level was already checked by the macro.
|
||||
// So just log.
|
||||
syslog(mPriority, "%s", mStream.str().c_str());
|
||||
// pat added for easy debugging.
|
||||
if (gLogToConsole||gLogToFile) {
|
||||
int mlen = mStream.str().size();
|
||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||
gLogToLock.lock();
|
||||
if (gLogToConsole) {
|
||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||
// so just use std::cout.
|
||||
std::cout << mStream.str();
|
||||
if (neednl) std::cout<<"\n";
|
||||
}
|
||||
if (gLogToFile) {
|
||||
fputs(mStream.str().c_str(),gLogToFile);
|
||||
if (neednl) {fputc('\n',gLogToFile);}
|
||||
fflush(gLogToFile);
|
||||
}
|
||||
gLogToLock.unlock();
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Pat added, tired of the syslog facility.
|
||||
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
|
||||
string str = gConfig.getStr("Log.File");
|
||||
if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
||||
const char *fn = str.c_str();
|
||||
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
|
||||
gLogToFile = fopen(fn,"w"); // New log file each time we start.
|
||||
if (gLogToFile) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
fprintf(gLogToFile,"Starting at %s",ctime(&now));
|
||||
fflush(gLogToFile);
|
||||
std::cout << "Logging to file: " << fn << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the log connection.
|
||||
openlog(name,0,facility);
|
||||
}
|
||||
|
||||
|
||||
void gLogEarly(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsyslog(level | LOG_USER, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
|
||||
@@ -23,72 +23,38 @@
|
||||
|
||||
*/
|
||||
|
||||
// (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 OBJLOG(wLevel) \
|
||||
LOG(wLevel) << "obj: " << this << ' '
|
||||
|
||||
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
|
||||
|
||||
|
||||
#include "Threads.h" // must be after defines above, if these files are to be allowed to use LOG()
|
||||
#include "Utils.h"
|
||||
#define LOGC(category, level) \
|
||||
Log(category, LOGL_##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.
|
||||
*/
|
||||
@@ -98,41 +64,27 @@ 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; // Pat added for easy debugging.
|
||||
|
||||
|
||||
|
||||
std::list<std::string> gGetLoggerAlarms(); ///< Get a copy of the recent alarm list.
|
||||
|
||||
|
||||
/**@ Global control and initialization of the logging system. */
|
||||
//@{
|
||||
/** Initialize the global logging system. */
|
||||
void gLogInit(const char* name, const char* level=NULL, int facility=LOG_USER);
|
||||
/** Get the logging level associated with a given file. */
|
||||
int gGetLoggingLevel(const char *filename=NULL);
|
||||
/** Allow early logging when still in constructors */
|
||||
void gLogEarly(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
//@}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,78 +33,20 @@ libcommon_la_SOURCES = \
|
||||
Sockets.cpp \
|
||||
Threads.cpp \
|
||||
Timeval.cpp \
|
||||
Reporting.cpp \
|
||||
Logger.cpp \
|
||||
Configuration.cpp \
|
||||
sqlite3util.cpp \
|
||||
URLEncode.cpp \
|
||||
Utils.cpp
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
BitVectorTest \
|
||||
InterthreadTest \
|
||||
SocketsTest \
|
||||
TimevalTest \
|
||||
RegexpTest \
|
||||
VectorTest \
|
||||
ConfigurationTest \
|
||||
LogTest \
|
||||
URLEncodeTest \
|
||||
F16Test
|
||||
|
||||
# ReportingTest
|
||||
trx_vty.c \
|
||||
debug.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
BitVector.h \
|
||||
PRBS.h \
|
||||
Interthread.h \
|
||||
LinkedLists.h \
|
||||
Sockets.h \
|
||||
Threads.h \
|
||||
Timeval.h \
|
||||
Regexp.h \
|
||||
Vector.h \
|
||||
Configuration.h \
|
||||
Reporting.h \
|
||||
F16.h \
|
||||
URLEncode.h \
|
||||
Utils.h \
|
||||
Logger.h \
|
||||
sqlite3util.h
|
||||
|
||||
URLEncodeTest_SOURCES = URLEncodeTest.cpp
|
||||
URLEncodeTest_LDADD = libcommon.la
|
||||
|
||||
BitVectorTest_SOURCES = BitVectorTest.cpp
|
||||
BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
InterthreadTest_SOURCES = InterthreadTest.cpp
|
||||
InterthreadTest_LDADD = libcommon.la
|
||||
InterthreadTest_LDFLAGS = -lpthread
|
||||
|
||||
SocketsTest_SOURCES = SocketsTest.cpp
|
||||
SocketsTest_LDADD = libcommon.la
|
||||
SocketsTest_LDFLAGS = -lpthread
|
||||
|
||||
TimevalTest_SOURCES = TimevalTest.cpp
|
||||
TimevalTest_LDADD = libcommon.la
|
||||
|
||||
VectorTest_SOURCES = VectorTest.cpp
|
||||
VectorTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
RegexpTest_SOURCES = RegexpTest.cpp
|
||||
RegexpTest_LDADD = libcommon.la
|
||||
|
||||
ConfigurationTest_SOURCES = ConfigurationTest.cpp
|
||||
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
# ReportingTest_SOURCES = ReportingTest.cpp
|
||||
# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
LogTest_SOURCES = LogTest.cpp
|
||||
LogTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
F16Test_SOURCES = F16Test.cpp
|
||||
|
||||
MOSTLYCLEANFILES += testSource testDestination
|
||||
|
||||
|
||||
trx_vty.h \
|
||||
debug.h \
|
||||
config_defs.h
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
#ifndef _MEMORYLEAK_
|
||||
#define _MEMORYLEAK_ 1
|
||||
#include <map>
|
||||
#include "ScalarTypes.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace Utils {
|
||||
|
||||
struct MemStats {
|
||||
// Enumerates the classes that are checked.
|
||||
// Redundancies are ok, for example, we check BitVector and also
|
||||
// several descendants of BitVector.
|
||||
enum MemoryNames {
|
||||
mZeroIsUnused,
|
||||
mVector,
|
||||
mVectorData,
|
||||
mBitVector,
|
||||
mByteVector,
|
||||
mByteVectorData,
|
||||
mRLCRawBlock,
|
||||
mRLCUplinkDataBlock,
|
||||
mRLCMessage,
|
||||
mRLCMsgPacketDownlinkDummyControlBlock, // Redundant with RLCMessage
|
||||
mTBF,
|
||||
mLlcEngine,
|
||||
mSgsnDownlinkMsg,
|
||||
mRachInfo,
|
||||
mPdpPdu,
|
||||
mFECDispatchInfo,
|
||||
mL3Frame,
|
||||
msignalVector,
|
||||
mSoftVector,
|
||||
mScramblingCode,
|
||||
mURlcDownSdu,
|
||||
mURlcPdu,
|
||||
// Must be last:
|
||||
mMax,
|
||||
};
|
||||
int mMemTotal[mMax]; // In elements, not bytes.
|
||||
int mMemNow[mMax];
|
||||
const char *mMemName[mMax];
|
||||
MemStats();
|
||||
void memChkNew(MemoryNames memIndex, const char *id);
|
||||
void memChkDel(MemoryNames memIndex, const char *id);
|
||||
void text(std::ostream &os);
|
||||
// We would prefer to use an unordered_map, but that requires special compile switches.
|
||||
// What a super great language.
|
||||
typedef std::map<std::string,Int_z> MemMapType;
|
||||
MemMapType mMemMap;
|
||||
};
|
||||
extern struct MemStats gMemStats;
|
||||
extern int gMemLeakDebug;
|
||||
|
||||
// This is a memory leak detector.
|
||||
// Use by putting RN_MEMCHKNEW and RN_MEMCHKDEL in class constructors/destructors,
|
||||
// or use the DEFINE_MEMORY_LEAK_DETECTOR class and add the defined class
|
||||
// as an ancestor to the class to be memory leak checked.
|
||||
|
||||
struct MemLabel {
|
||||
std::string mccKey;
|
||||
virtual ~MemLabel() {
|
||||
Int_z &tmp = Utils::gMemStats.mMemMap[mccKey]; tmp = tmp - 1;
|
||||
}
|
||||
};
|
||||
|
||||
#if RN_DISABLE_MEMORY_LEAK_TEST
|
||||
#define RN_MEMCHKNEW(type)
|
||||
#define RN_MEMCHKDEL(type)
|
||||
#define RN_MEMLOG(type,ptr)
|
||||
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
|
||||
struct checkerClass {};
|
||||
#else
|
||||
|
||||
#define RN_MEMCHKNEW(type) { Utils::gMemStats.memChkNew(Utils::MemStats::m##type,#type); }
|
||||
#define RN_MEMCHKDEL(type) { Utils::gMemStats.memChkDel(Utils::MemStats::m##type,#type); }
|
||||
|
||||
#define RN_MEMLOG(type,ptr) { \
|
||||
static std::string key = format("%s_%s:%d",#type,__FILE__,__LINE__); \
|
||||
(ptr)->/* MemCheck##type:: */ mccKey = key; \
|
||||
Utils::gMemStats.mMemMap[key]++; \
|
||||
}
|
||||
|
||||
// TODO: The above assumes that checkclass is MemCheck ## subClass
|
||||
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
|
||||
struct checkerClass : public virtual Utils::MemLabel { \
|
||||
checkerClass() { RN_MEMCHKNEW(subClass); } \
|
||||
virtual ~checkerClass() { \
|
||||
RN_MEMCHKDEL(subClass); \
|
||||
} \
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
#endif
|
||||
110
CommonLibs/PRBS.h
Normal file
110
CommonLibs/PRBS.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef PRBS_H
|
||||
#define PRBS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/** Pseudo-random binary sequence (PRBS) generator (a Galois LFSR implementation). */
|
||||
class PRBS {
|
||||
public:
|
||||
|
||||
PRBS(unsigned wLen, uint64_t wCoeff, uint64_t wState = 0x01)
|
||||
: mCoeff(wCoeff), mStartState(wState), mState(wState), mLen(wLen)
|
||||
{ assert(wLen<=64); }
|
||||
|
||||
/**@name Accessors */
|
||||
//@{
|
||||
uint64_t coeff() const { return mCoeff; }
|
||||
uint64_t state() const { return mState; }
|
||||
void state(uint64_t state) { mState = state & mask(); }
|
||||
unsigned size() const { return mLen; }
|
||||
//@}
|
||||
|
||||
/**
|
||||
Calculate one bit of a PRBS
|
||||
*/
|
||||
unsigned generateBit()
|
||||
{
|
||||
const unsigned result = mState & 0x01;
|
||||
processBit(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
Update the generator state by one bit.
|
||||
If you want to synchronize your PRBS to a known state, call this function
|
||||
size() times passing your PRBS to it bit by bit.
|
||||
*/
|
||||
void processBit(unsigned inBit)
|
||||
{
|
||||
mState >>= 1;
|
||||
if (inBit) mState ^= mCoeff;
|
||||
}
|
||||
|
||||
/** Return true when PRBS is wrapping through initial state */
|
||||
bool isFinished() const { return mStartState == mState; }
|
||||
|
||||
protected:
|
||||
|
||||
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
|
||||
uint64_t mStartState; ///< initial shift register state.
|
||||
uint64_t mState; ///< shift register state.
|
||||
unsigned mLen; ///< number of bits used in shift register
|
||||
|
||||
/** Return mask for the state register */
|
||||
uint64_t mask() const { return (mLen==64)?0xFFFFFFFFFFFFFFFFUL:((1<<mLen)-1); }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
A standard 9-bit based pseudorandom binary sequence (PRBS) generator.
|
||||
Polynomial: x^9 + x^5 + 1
|
||||
*/
|
||||
class PRBS9 : public PRBS {
|
||||
public:
|
||||
PRBS9(uint64_t wState = 0x01)
|
||||
: PRBS(9, 0x0110, wState)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
A standard 15-bit based pseudorandom binary sequence (PRBS) generator.
|
||||
Polynomial: x^15 + x^14 + 1
|
||||
*/
|
||||
class PRBS15 : public PRBS {
|
||||
public:
|
||||
PRBS15(uint64_t wState = 0x01)
|
||||
: PRBS(15, 0x6000, wState)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
A standard 64-bit based pseudorandom binary sequence (PRBS) generator.
|
||||
Polynomial: x^64 + x^63 + x^61 + x^60 + 1
|
||||
*/
|
||||
class PRBS64 : public PRBS {
|
||||
public:
|
||||
PRBS64(uint64_t wState = 0x01)
|
||||
: PRBS(64, 0xD800000000000000ULL, wState)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif // PRBS_H
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REGEXPW_H
|
||||
#define REGEXPW_H
|
||||
|
||||
#include <regex.h>
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
class Regexp {
|
||||
|
||||
private:
|
||||
|
||||
regex_t mRegex;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Regexp(const char* regexp, int flags=REG_EXTENDED)
|
||||
{
|
||||
int result = regcomp(&mRegex, regexp, flags);
|
||||
if (result) {
|
||||
char msg[256];
|
||||
regerror(result,&mRegex,msg,255);
|
||||
std::cerr << "Regexp compilation of " << regexp << " failed: " << msg << std::endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
~Regexp()
|
||||
{ regfree(&mRegex); }
|
||||
|
||||
bool match(const char *text, int flags=0) const
|
||||
{ return regexec(&mRegex, text, 0, NULL, flags)==0; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,145 +0,0 @@
|
||||
/**@file Module for performance-reporting mechanisms. */
|
||||
/*
|
||||
* Copyright 2012 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "Reporting.h"
|
||||
#include "Logger.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char* createReportingTable = {
|
||||
"CREATE TABLE IF NOT EXISTS REPORTING ("
|
||||
"NAME TEXT UNIQUE NOT NULL, "
|
||||
"VALUE INTEGER DEFAULT 0, "
|
||||
"CLEAREDTIME INTEGER NOT NULL, "
|
||||
"UPDATETIME INTEGER DEFAULT 0 "
|
||||
")"
|
||||
};
|
||||
|
||||
|
||||
ReportingTable::ReportingTable(const char* filename)
|
||||
{
|
||||
gLogEarly(LOG_INFO | mFacility, "opening reporting table from path %s", filename);
|
||||
// Connect to the database.
|
||||
int rc = sqlite3_open(filename,&mDB);
|
||||
if (rc) {
|
||||
gLogEarly(LOG_EMERG | mFacility, "cannot open reporting database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
|
||||
sqlite3_close(mDB);
|
||||
mDB = NULL;
|
||||
return;
|
||||
}
|
||||
// Create the table, if needed.
|
||||
if (!sqlite3_command(mDB,createReportingTable)) {
|
||||
gLogEarly(LOG_EMERG | mFacility, "cannot create reporting table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::create(const char* paramName)
|
||||
{
|
||||
char cmd[200];
|
||||
sprintf(cmd,"INSERT OR IGNORE INTO REPORTING (NAME,CLEAREDTIME) VALUES (\"%s\",%ld)", paramName, time(NULL));
|
||||
if (!sqlite3_command(mDB,cmd)) {
|
||||
gLogEarly(LOG_CRIT|mFacility, "cannot create reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ReportingTable::incr(const char* paramName)
|
||||
{
|
||||
char cmd[200];
|
||||
sprintf(cmd,"UPDATE REPORTING SET VALUE=VALUE+1, UPDATETIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
|
||||
if (!sqlite3_command(mDB,cmd)) {
|
||||
gLogEarly(LOG_CRIT|mFacility, "cannot increment reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ReportingTable::max(const char* paramName, unsigned newVal)
|
||||
{
|
||||
char cmd[200];
|
||||
sprintf(cmd,"UPDATE REPORTING SET VALUE=MAX(VALUE,%u), UPDATETIME=%ld WHERE NAME=\"%s\"", newVal, time(NULL), paramName);
|
||||
if (!sqlite3_command(mDB,cmd)) {
|
||||
gLogEarly(LOG_CRIT|mFacility, "cannot maximize reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::clear(const char* paramName)
|
||||
{
|
||||
char cmd[200];
|
||||
sprintf(cmd,"UPDATE REPORTING SET VALUE=0, UPDATETIME=0, CLEAREDTIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
|
||||
if (!sqlite3_command(mDB,cmd)) {
|
||||
gLogEarly(LOG_CRIT|mFacility, "cannot clear reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::create(const char* baseName, unsigned minIndex, unsigned maxIndex)
|
||||
{
|
||||
size_t sz = strlen(baseName);
|
||||
for (unsigned i = minIndex; i<=maxIndex; i++) {
|
||||
char name[sz+10];
|
||||
sprintf(name,"%s.%u",baseName,i);
|
||||
if (!create(name)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReportingTable::incr(const char* baseName, unsigned index)
|
||||
{
|
||||
char name[strlen(baseName)+10];
|
||||
sprintf(name,"%s.%u",baseName,index);
|
||||
return incr(name);
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::max(const char* baseName, unsigned index, unsigned newVal)
|
||||
{
|
||||
char name[strlen(baseName)+10];
|
||||
sprintf(name,"%s.%u",baseName,index);
|
||||
return max(name,newVal);
|
||||
}
|
||||
|
||||
|
||||
bool ReportingTable::clear(const char* baseName, unsigned index)
|
||||
{
|
||||
char name[strlen(baseName)+10];
|
||||
sprintf(name,"%s.%u",baseName,index);
|
||||
return clear(name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/**@file Module for performance-reporting mechanisms. */
|
||||
/*
|
||||
* Copyright 2012 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef REPORTING_H
|
||||
#define REPORTING_H
|
||||
|
||||
#include <sqlite3util.h>
|
||||
#include <ostream>
|
||||
|
||||
|
||||
/**
|
||||
Collect performance statistics into a database.
|
||||
Parameters are counters or max/min trackers, all integer.
|
||||
*/
|
||||
class ReportingTable {
|
||||
|
||||
private:
|
||||
|
||||
sqlite3* mDB; ///< database connection
|
||||
int mFacility; ///< rsyslogd facility
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Open the database connection;
|
||||
create the table if it does not exist yet.
|
||||
*/
|
||||
ReportingTable(const char* filename);
|
||||
|
||||
/** Create a new parameter. */
|
||||
bool create(const char* paramName);
|
||||
|
||||
/** Create an indexed parameter set. */
|
||||
bool create(const char* baseBame, unsigned minIndex, unsigned maxIndex);
|
||||
|
||||
/** Increment a counter. */
|
||||
bool incr(const char* paramName);
|
||||
|
||||
/** Increment an indexed counter. */
|
||||
bool incr(const char* baseName, unsigned index);
|
||||
|
||||
/** Take a max of a parameter. */
|
||||
bool max(const char* paramName, unsigned newVal);
|
||||
|
||||
/** Take a max of an indexed parameter. */
|
||||
bool max(const char* paramName, unsigned index, unsigned newVal);
|
||||
|
||||
/** Clear a value. */
|
||||
bool clear(const char* paramName);
|
||||
|
||||
/** Clear an indexed value. */
|
||||
bool clear(const char* paramName, unsigned index);
|
||||
|
||||
/** Dump the database to a stream. */
|
||||
void dump(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef SCALARTYPES_H
|
||||
#define SCALARTYPES_H
|
||||
#include <iostream> // For size_t
|
||||
#include <stdint.h>
|
||||
//#include "GSMCommon.h" // Was included for Z100Timer
|
||||
|
||||
// We dont bother to define *= /= etc.; you'll have to convert: a*=b; to: a=a*b;
|
||||
#define _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
|
||||
Classname() : value(Init) {} \
|
||||
Classname(Basetype wvalue) { value = wvalue; } /* Can set from basetype. */ \
|
||||
operator Basetype(void) const { return value; } /* Converts from basetype. */ \
|
||||
Basetype operator=(Basetype wvalue) { return value = wvalue; } \
|
||||
Basetype* operator&() { return &value; }
|
||||
|
||||
#define _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) \
|
||||
Basetype operator++() { return ++value; } \
|
||||
Basetype operator++(int) { return value++; } \
|
||||
Basetype operator--() { return --value; } \
|
||||
Basetype operator--(int) { return value--; } \
|
||||
Basetype operator+=(Basetype wvalue) { return value = value + wvalue; } \
|
||||
Basetype operator-=(Basetype wvalue) { return value = value - wvalue; }
|
||||
|
||||
#define _INITIALIZED_SCALAR_FUNCS(Classname,Basetype,Init) \
|
||||
_INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
|
||||
_INITIALIZED_SCALAR_ARITH_FUNCS(Basetype)
|
||||
|
||||
|
||||
#define _DECLARE_SCALAR_TYPE(Classname_i,Classname_z,Basetype) \
|
||||
template <Basetype Init> \
|
||||
struct Classname_i { \
|
||||
Basetype value; \
|
||||
_INITIALIZED_SCALAR_FUNCS(Classname_i,Basetype,Init) \
|
||||
}; \
|
||||
typedef Classname_i<0> Classname_z;
|
||||
|
||||
|
||||
// Usage:
|
||||
// Where 'classname' is one of the types listed below, then:
|
||||
// classname_z specifies a zero initialized type;
|
||||
// classname_i<value> initializes the type to the specified value.
|
||||
// We also define Float_z.
|
||||
_DECLARE_SCALAR_TYPE(Int_i, Int_z, int)
|
||||
_DECLARE_SCALAR_TYPE(Char_i, Char_z, signed char)
|
||||
_DECLARE_SCALAR_TYPE(Int16_i, Int16_z, int16_t)
|
||||
_DECLARE_SCALAR_TYPE(Int32_i, Int32_z, int32_t)
|
||||
_DECLARE_SCALAR_TYPE(UInt_i, UInt_z, unsigned)
|
||||
_DECLARE_SCALAR_TYPE(UChar_i, UChar_z, unsigned char)
|
||||
_DECLARE_SCALAR_TYPE(UInt16_i, UInt16_z, uint16_t)
|
||||
_DECLARE_SCALAR_TYPE(UInt32_i, UInt32_z, uint32_t)
|
||||
_DECLARE_SCALAR_TYPE(Size_t_i, Size_t_z, size_t)
|
||||
|
||||
// Bool is special because it cannot accept some arithmetic funcs
|
||||
//_DECLARE_SCALAR_TYPE(Bool_i, Bool_z, bool)
|
||||
template <bool Init>
|
||||
struct Bool_i {
|
||||
bool value;
|
||||
_INITIALIZED_SCALAR_BASE_FUNCS(Bool_i,bool,Init)
|
||||
};
|
||||
typedef Bool_i<0> Bool_z;
|
||||
|
||||
// float is special, because C++ does not permit the template initalization:
|
||||
struct Float_z {
|
||||
float value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Float_z,float,0)
|
||||
};
|
||||
struct Double_z {
|
||||
double value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Double_z,double,0)
|
||||
};
|
||||
|
||||
|
||||
class ItemWithValueAndWidth {
|
||||
public:
|
||||
virtual unsigned getValue() const = 0;
|
||||
virtual unsigned getWidth() const = 0;
|
||||
};
|
||||
|
||||
// A Range Networks Field with a specified width.
|
||||
// See RLCMessages.h for examples.
|
||||
template <int Width=32, unsigned Init=0>
|
||||
class Field_i : public ItemWithValueAndWidth
|
||||
{
|
||||
public:
|
||||
unsigned value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Field_i,unsigned,Init)
|
||||
unsigned getWidth() const { return Width; }
|
||||
unsigned getValue() const { return value; }
|
||||
};
|
||||
|
||||
// Synonym for Field_i, but no way to do it.
|
||||
template <int Width, unsigned Init=0>
|
||||
class Field_z : public ItemWithValueAndWidth
|
||||
{
|
||||
public:
|
||||
unsigned value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Field_z,unsigned,Init)
|
||||
unsigned getWidth() const { return Width; }
|
||||
unsigned getValue() const { return value; }
|
||||
};
|
||||
|
||||
// This is an uninitialized field.
|
||||
template <int Width=32, unsigned Init=0>
|
||||
class Field : public ItemWithValueAndWidth
|
||||
{
|
||||
public:
|
||||
unsigned value;
|
||||
_INITIALIZED_SCALAR_FUNCS(Field,unsigned,Init)
|
||||
unsigned getWidth() const { return Width; }
|
||||
unsigned getValue() const { return value; }
|
||||
};
|
||||
|
||||
|
||||
// A Z100Timer with an initial value specified.
|
||||
//template <int Init>
|
||||
//class Z100Timer_i : public GSM::Z100Timer {
|
||||
// public:
|
||||
// Z100Timer_i() : GSM::Z100Timer(Init) {}
|
||||
//};
|
||||
|
||||
#endif
|
||||
@@ -187,24 +187,20 @@ int DatagramSocket::send(const struct sockaddr* dest, const char * message)
|
||||
return send(dest,message,length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int DatagramSocket::read(char* buffer)
|
||||
int DatagramSocket::read(char* buffer, size_t length)
|
||||
{
|
||||
socklen_t temp_len = sizeof(mSource);
|
||||
int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0,
|
||||
(struct sockaddr*)&mSource,&temp_len);
|
||||
if ((length==-1) && (errno!=EAGAIN)) {
|
||||
socklen_t addr_len = sizeof(mSource);
|
||||
int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
|
||||
(struct sockaddr*) &mSource, &addr_len);
|
||||
|
||||
if ((rd_length==-1) && (errno!=EAGAIN)) {
|
||||
perror("DatagramSocket::read() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
return length;
|
||||
return rd_length;
|
||||
}
|
||||
|
||||
|
||||
int DatagramSocket::read(char* buffer, unsigned timeout)
|
||||
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
@@ -218,7 +214,7 @@ int DatagramSocket::read(char* buffer, unsigned timeout)
|
||||
throw SocketError();
|
||||
}
|
||||
if (sel==0) return -1;
|
||||
if (FD_ISSET(mSocketFD,&fds)) return read(buffer);
|
||||
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -227,18 +223,18 @@ int DatagramSocket::read(char* buffer, unsigned timeout)
|
||||
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort)
|
||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort);
|
||||
open(wSrcPort, wSrcIP);
|
||||
}
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort,
|
||||
const char * wDestIP, unsigned short wDestPort )
|
||||
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
|
||||
const char *wDestIP, unsigned short wDestPort)
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort);
|
||||
open(wSrcPort, wSrcIP);
|
||||
destination(wDestPort, wDestIP);
|
||||
}
|
||||
|
||||
@@ -250,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
|
||||
}
|
||||
|
||||
|
||||
void UDPSocket::open(unsigned short localPort)
|
||||
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
||||
@@ -269,7 +265,7 @@ void UDPSocket::open(unsigned short localPort)
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_addr.s_addr = inet_addr(wlocalIP);
|
||||
address.sin_port = htons(localPort);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
@@ -288,50 +284,4 @@ unsigned short UDPSocket::port() const
|
||||
return ntohs(name.sin_port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UDDSocket::UDDSocket(const char* localPath, const char* remotePath)
|
||||
:DatagramSocket()
|
||||
{
|
||||
if (localPath!=NULL) open(localPath);
|
||||
if (remotePath!=NULL) destination(remotePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDDSocket::open(const char* localPath)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0);
|
||||
if (mSocketFD<0) {
|
||||
perror("socket() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
|
||||
// bind
|
||||
struct sockaddr_un address;
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sun_family = AF_UNIX;
|
||||
strcpy(address.sun_path,localPath);
|
||||
unlink(localPath);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDDSocket::destination(const char* remotePath)
|
||||
{
|
||||
struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination;
|
||||
strcpy(unAddr->sun_path,remotePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim:ts=4:sw=4
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
|
||||
@return The number of bytes received or -1 on non-blocking pass.
|
||||
*/
|
||||
int read(char* buffer);
|
||||
int read(char* buffer, size_t length);
|
||||
|
||||
/**
|
||||
Receive a packet with a timeout.
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
@param maximum wait time in milliseconds
|
||||
@return The number of bytes received or -1 on timeout.
|
||||
*/
|
||||
int read(char* buffer, unsigned timeout);
|
||||
int read(char* buffer, size_t length, unsigned timeout);
|
||||
|
||||
|
||||
/** Send a packet to a given destination, other than the default. */
|
||||
@@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket {
|
||||
public:
|
||||
|
||||
/** Open a USP socket with an OS-assigned port and no default destination. */
|
||||
UDPSocket( unsigned short localPort=0);
|
||||
UDPSocket(const char *localIP, unsigned short localPort);
|
||||
|
||||
/** Given a full specification, open the socket and set the dest address. */
|
||||
UDPSocket( unsigned short localPort,
|
||||
const char * remoteIP, unsigned short remotePort);
|
||||
UDPSocket(const char *localIP, unsigned short localPort,
|
||||
const char *remoteIP, unsigned short remotePort);
|
||||
|
||||
/** Set the destination port. */
|
||||
void destination( unsigned short wDestPort, const char * wDestIP );
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
unsigned short port() const;
|
||||
|
||||
/** Open and bind the UDP socket to a local port. */
|
||||
void open(unsigned short localPort=0);
|
||||
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
||||
@@ -166,26 +166,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** Unix Domain Datagram Socket */
|
||||
class UDDSocket : public DatagramSocket {
|
||||
|
||||
public:
|
||||
|
||||
UDDSocket(const char* localPath=NULL, const char* remotePath=NULL);
|
||||
|
||||
void destination(const char* remotePath);
|
||||
|
||||
void open(const char* localPath);
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; }
|
||||
|
||||
size_t addressSize() const { return sizeof(struct sockaddr_un); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -172,8 +172,15 @@ class Thread {
|
||||
void start(void *(*task)(void*), void *arg);
|
||||
|
||||
/** Join a thread that will stop on its own. */
|
||||
void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
|
||||
void join() {
|
||||
if (mThread) {
|
||||
int s = pthread_join(mThread, NULL);
|
||||
assert(!s);
|
||||
}
|
||||
}
|
||||
|
||||
/** Send cancelation to thread */
|
||||
void cancel() { pthread_cancel(mThread); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/* Copyright 2011, Range Networks, Inc. */
|
||||
|
||||
#include <URLEncode.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
//based on javascript encodeURIComponent()
|
||||
string URLEncode(const string &c)
|
||||
{
|
||||
static const char *digits = "01234567890ABCDEF";
|
||||
string retVal="";
|
||||
for (size_t i=0; i<c.length(); i++)
|
||||
{
|
||||
const char ch = c[i];
|
||||
if (isalnum(ch) || strchr("-_.!~'()",ch)) {
|
||||
retVal += ch;
|
||||
} else {
|
||||
retVal += '%';
|
||||
retVal += digits[(ch>>4) & 0x0f];
|
||||
retVal += digits[ch & 0x0f];
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
# include <string>
|
||||
|
||||
std::string URLEncode(const std::string&);
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
#include "URLEncode.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
string test = string("Testing: !@#$%^&*() " __DATE__ " " __TIME__);
|
||||
cout << test << endl;
|
||||
cout << URLEncode(test) << endl;
|
||||
}
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include <unistd.h> // For usleep
|
||||
#include <sys/time.h> // For gettimeofday
|
||||
#include <stdio.h> // For vsnprintf
|
||||
#include <ostream> // For ostream
|
||||
#include <sstream> // For ostringstream
|
||||
#include <string.h> // For strcpy
|
||||
//#include "GSMCommon.h"
|
||||
#include "Utils.h"
|
||||
#include "MemoryLeak.h"
|
||||
|
||||
namespace Utils {
|
||||
|
||||
MemStats gMemStats;
|
||||
int gMemLeakDebug = 0;
|
||||
|
||||
MemStats::MemStats()
|
||||
{
|
||||
memset(mMemNow,0,sizeof(mMemNow));
|
||||
memset(mMemTotal,0,sizeof(mMemTotal));
|
||||
memset(mMemName,0,sizeof(mMemName));
|
||||
}
|
||||
|
||||
void MemStats::text(std::ostream &os)
|
||||
{
|
||||
os << "Structs current total:\n";
|
||||
for (int i = 0; i < mMax; i++) {
|
||||
os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void MemStats::memChkNew(MemoryNames memIndex, const char *id)
|
||||
{
|
||||
/*std::cout << "new " #type "\n";*/
|
||||
mMemNow[memIndex]++;
|
||||
mMemTotal[memIndex]++;
|
||||
mMemName[memIndex] = id;
|
||||
}
|
||||
|
||||
void MemStats::memChkDel(MemoryNames memIndex, const char *id)
|
||||
{
|
||||
/*std::cout << "del " #type "\n";*/
|
||||
mMemNow[memIndex]--;
|
||||
if (mMemNow[memIndex] < 0) {
|
||||
LOG(ERR) << "Memory underflow on type "<<id;
|
||||
if (gMemLeakDebug) assert(0);
|
||||
mMemNow[memIndex] += 100; // Prevent another message for a while.
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
|
||||
{
|
||||
return os << ss.str();
|
||||
}
|
||||
|
||||
std::ostream &osprintf(std::ostream &os, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[300];
|
||||
va_start(ap,fmt);
|
||||
int n = vsnprintf(buf,300,fmt,ap);
|
||||
va_end(ap);
|
||||
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
|
||||
os << buf;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string format(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[300];
|
||||
va_start(ap,fmt);
|
||||
int n = vsnprintf(buf,300,fmt,ap);
|
||||
va_end(ap);
|
||||
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// Return time in seconds with high resolution.
|
||||
// Note: In the past I found this to be a surprisingly expensive system call in linux.
|
||||
double timef()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
return tv.tv_usec / 1000000.0 + tv.tv_sec;
|
||||
}
|
||||
|
||||
const std::string timestr()
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm tm;
|
||||
gettimeofday(&tv,NULL);
|
||||
localtime_r(&tv.tv_sec,&tm);
|
||||
unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
|
||||
return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
|
||||
}
|
||||
|
||||
// High resolution sleep for the specified time.
|
||||
// Return FALSE if time is already past.
|
||||
void sleepf(double howlong)
|
||||
{
|
||||
if (howlong <= 0.00001) return; // Less than 10 usecs, forget it.
|
||||
usleep((useconds_t) (1000000.0 * howlong));
|
||||
}
|
||||
|
||||
//bool sleepuntil(double untilwhen)
|
||||
//{
|
||||
//double now = timef();
|
||||
//double howlong = untilwhen - now; // Fractional time in seconds.
|
||||
// We are not worrying about overflow because all times should be in the near future.
|
||||
//if (howlong <= 0.00001) return false; // Less than 10 usecs, forget it.
|
||||
//sleepf(sleeptime);
|
||||
//}
|
||||
|
||||
std::string Text2Str::str() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
text(ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Text2Str *val)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (val) {
|
||||
val->text(ss);
|
||||
os << ss.str();
|
||||
} else {
|
||||
os << "(null)";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
// Greatest Common Denominator.
|
||||
// This is by Doug Brown.
|
||||
int gcd(int x, int y)
|
||||
{
|
||||
if (x > y) {
|
||||
return x % y == 0 ? y : gcd(y, x % y);
|
||||
} else {
|
||||
return y % x == 0 ? x : gcd(x, y % x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Split a C string into an argc,argv array in place; the input string is modified.
|
||||
// Returns argc, and places results in argv, up to maxargc elements.
|
||||
// The final argv receives the rest of the input string from maxargc on,
|
||||
// even if it contains additional splitchars.
|
||||
// The correct idiom for use is to make a copy of your string, like this:
|
||||
// char *copy = strcpy((char*)alloca(the_string.length()+1),the_string.c_str());
|
||||
// char *argv[2];
|
||||
// int argc = cstrSplit(copy,argv,2,NULL);
|
||||
// If you want to detect the error of too many arguments, add 1 to argv, like this:
|
||||
// char *argv[3];
|
||||
// int argc = cstrSplit(copy,argv,3,NULL);
|
||||
// if (argc == 3) { error("too many arguments"; }
|
||||
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars)
|
||||
{
|
||||
if (splitchars == NULL) { splitchars = " \t\r\n"; } // Default is any space.
|
||||
int argc = 0;
|
||||
while (argc < maxargc) {
|
||||
while (*in && strchr(splitchars,*in)) {in++;} // scan past any splitchars
|
||||
if (! *in) return argc; // return if finished.
|
||||
pargv[argc++] = in; // save ptr to start of arg.
|
||||
in = strpbrk(in,splitchars); // go to end of arg.
|
||||
if (!in) return argc; // return if finished.
|
||||
*in++ = 0; // zero terminate this arg.
|
||||
}
|
||||
return argc;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat) { stat.text(os); return os; }
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat) { stat.text(os); return os; }
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat) { stat.text(os); return os; }
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat) { stat.text(os); return os; }
|
||||
|
||||
std::string replaceAll(const std::string input, const std::string search, const std::string replace)
|
||||
{
|
||||
std::string output = input;
|
||||
int index = 0;
|
||||
|
||||
while (true) {
|
||||
index = output.find(search, index);
|
||||
if (index == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
output.replace(index, replace.length(), replace);
|
||||
index += replace.length();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef GPRSUTILS_H
|
||||
#define GPRSUTILS_H
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <math.h> // for sqrtf
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
namespace Utils {
|
||||
|
||||
extern double timef(); // high resolution time
|
||||
extern const std::string timestr(); // A timestamp to print in messages.
|
||||
extern void sleepf(double howlong); // high resolution sleep
|
||||
extern int gcd(int x, int y);
|
||||
|
||||
// It is irritating to create a string just to interface to the brain-damaged
|
||||
// C++ stream class, but this is only used for debug messages.
|
||||
std::string format(const char *fmt, ...) __attribute__((format (printf,1,2)));
|
||||
|
||||
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars=NULL);
|
||||
|
||||
// For classes with a text() function, provide a function to return a String,
|
||||
// and also a standard << stream function that takes a pointer to the object.
|
||||
// We dont provide the function that takes a reference to the object
|
||||
// because it is too highly overloaded and generally doesnt work.
|
||||
class Text2Str {
|
||||
public:
|
||||
virtual void text(std::ostream &os) const = 0;
|
||||
std::string str() const;
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const Text2Str *val);
|
||||
|
||||
#if 0
|
||||
// Generic Activity Timer. Lots of controls to make everybody happy.
|
||||
class ATimer {
|
||||
double mStart;
|
||||
//bool mActive;
|
||||
double mLimitTime;
|
||||
public:
|
||||
ATimer() : mStart(0), mLimitTime(0) { }
|
||||
ATimer(double wLimitTime) : mStart(0), mLimitTime(wLimitTime) { }
|
||||
void start() { mStart=timef(); }
|
||||
void stop() { mStart=0; }
|
||||
bool active() { return !!mStart; }
|
||||
double elapsed() { return timef() - mStart; }
|
||||
bool expired() { return elapsed() > mLimitTime; }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
struct BitSet {
|
||||
unsigned mBits;
|
||||
void setBit(unsigned whichbit) { mBits |= 1<<whichbit; }
|
||||
void clearBit(unsigned whichbit) { mBits &= ~(1<<whichbit); }
|
||||
unsigned getBit(unsigned whichbit) const { return mBits & (1<<whichbit); }
|
||||
bool isSet(unsigned whichbit) const { return mBits & (1<<whichbit); }
|
||||
unsigned bits() const { return mBits; }
|
||||
operator int(void) const { return mBits; }
|
||||
BitSet() { mBits = 0; }
|
||||
};
|
||||
|
||||
// Store current, min, max and compute running average and standard deviation.
|
||||
template<class Type> struct Statistic {
|
||||
Type mCurrent, mMin, mMax; // min,max optional initialization so you can print before adding any values.
|
||||
unsigned mCnt;
|
||||
double mSum;
|
||||
//double mSum2; // sum of squares.
|
||||
// (Type) cast needed in case Type is an enum, stupid language.
|
||||
Statistic() : mCurrent((Type)0), mMin((Type)0), mMax((Type)0), mCnt(0), mSum(0) /*,mSum2(0)*/ {}
|
||||
// Set the current value and add a statisical point.
|
||||
void addPoint(Type val) {
|
||||
mCurrent = val;
|
||||
if (mCnt == 0 || val < mMin) {mMin = val;}
|
||||
if (mCnt == 0 || val > mMax) {mMax = val;}
|
||||
mCnt++;
|
||||
mSum += val;
|
||||
//mSum2 += val * val;
|
||||
}
|
||||
Type getCurrent() const { // Return current value.
|
||||
return mCnt ? mCurrent : 0;
|
||||
}
|
||||
double getAvg() const { // Return average.
|
||||
return mCnt==0 ? 0 : mSum/mCnt;
|
||||
};
|
||||
//float getSD() const { // Return standard deviation. Use low precision square root function.
|
||||
// return mCnt==0 ? 0 : sqrtf(mCnt * mSum2 - mSum*mSum) / mCnt;
|
||||
//}
|
||||
|
||||
void text(std::ostream &os) const { // Print everything in parens.
|
||||
os << "("<<mCurrent;
|
||||
if (mMin != mMax) { // Not point in printing all this stuff if min == max.
|
||||
os <<LOGVAR2("min",mMin)<<LOGVAR2("max",mMax)<<LOGVAR2("avg",getAvg());
|
||||
if (mCnt <= 999999) {
|
||||
os <<LOGVAR2("N",mCnt);
|
||||
} else { // Shorten this up:
|
||||
char buf[10], *ep;
|
||||
sprintf(buf,"%.3g",round(mCnt));
|
||||
if ((ep = strchr(buf,'e')) && ep[1] == '+') { strcpy(ep+1,ep+2); }
|
||||
os << LOGVAR2("N",buf);
|
||||
}
|
||||
// os<<LOGVAR2("sd",getSD()) standard deviation not interesting
|
||||
}
|
||||
os << ")";
|
||||
// " min="<<mMin <<" max="<<mMax <<format(" avg=%4g sd=%3g)",getAvg(),getSD());
|
||||
}
|
||||
// Not sure if this works:
|
||||
//std::string statStr() const {
|
||||
// return (std::string)mCurrent + " min=" + (std::string) mMin +" max="+(string)mMax+ format(" avg=%4g sd=%3g",getAvg(),getSD());
|
||||
//}
|
||||
};
|
||||
|
||||
// This I/O mechanism is so dumb:
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat);
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat);
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat);
|
||||
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat);
|
||||
|
||||
|
||||
// Yes, they botched and left this out:
|
||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
|
||||
|
||||
std::ostream &osprintf(std::ostream &os, const char *fmt, ...) __attribute__((format (printf,2,3)));
|
||||
|
||||
std::string replaceAll(const std::string input, const std::string search, const std::string replace);
|
||||
|
||||
}; // namespace
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
#endif
|
||||
@@ -92,6 +92,13 @@ template <class T> class Vector {
|
||||
mEnd = mStart + newSize;
|
||||
}
|
||||
|
||||
/** Reduce addressable size of the Vector, keeping content. */
|
||||
void shrink(size_t newSize)
|
||||
{
|
||||
assert(newSize <= mEnd - mStart);
|
||||
mEnd = mStart + newSize;
|
||||
}
|
||||
|
||||
/** Release memory and clear pointers. */
|
||||
void clear() { resize(0); }
|
||||
|
||||
@@ -111,8 +118,8 @@ template <class T> class Vector {
|
||||
/** Build an empty Vector of a given size. */
|
||||
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
||||
|
||||
/** Build a Vector by shifting the data block. */
|
||||
Vector(Vector<T>& other)
|
||||
/** Build a Vector by moving another. */
|
||||
Vector(Vector<T>&& other)
|
||||
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
||||
{ other.mData=NULL; }
|
||||
|
||||
@@ -222,6 +229,21 @@ template <class T> class Vector {
|
||||
memcpy(other.mStart,base,span*sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
Move (copy) a segment of this vector into a different position in the vector
|
||||
@param from Start point from which to copy.
|
||||
@param to Start point to which to copy.
|
||||
@param span The number of elements to copy.
|
||||
*/
|
||||
void segmentMove(size_t from, size_t to, size_t span)
|
||||
{
|
||||
const T* baseFrom = mStart + from;
|
||||
T* baseTo = mStart + to;
|
||||
assert(baseFrom+span<=mEnd);
|
||||
assert(baseTo+span<=mEnd);
|
||||
memmove(baseTo,baseFrom,span*sizeof(T));
|
||||
}
|
||||
|
||||
void fill(const T& val)
|
||||
{
|
||||
T* dp=mStart;
|
||||
|
||||
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,
|
||||
};
|
||||
18
CommonLibs/debug.c
Normal file
18
CommonLibs/debug.c
Normal file
@@ -0,0 +1,18 @@
|
||||
#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,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = default_categories,
|
||||
.num_cat = ARRAY_SIZE(default_categories),
|
||||
};
|
||||
8
CommonLibs/debug.h
Normal file
8
CommonLibs/debug.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
extern const struct log_info log_info;
|
||||
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DMAIN,
|
||||
};
|
||||
@@ -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
|
||||
574
CommonLibs/trx_vty.c
Normal file
574
CommonLibs/trx_vty.c
Normal file
@@ -0,0 +1,574 @@
|
||||
/*
|
||||
* 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, "gspdo" },
|
||||
{ 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:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -41,10 +41,24 @@ const BitVector GSM::gTrainingSequence[] = {
|
||||
BitVector("11101111000100101110111100"),
|
||||
};
|
||||
|
||||
const BitVector GSM::gEdgeTrainingSequence[] = {
|
||||
BitVector("111111001111111001111001001001111111111111001111111111001111111001111001001001"),
|
||||
BitVector("111111001111001001111001001001111001001001001111111111001111001001111001001001"),
|
||||
BitVector("111001111111111111001001001111001001001111001111111001111111111111001001001111"),
|
||||
BitVector("111001111111111001001001001111001001111001111111111001111111111001001001001111"),
|
||||
BitVector("111111111001001111001111001001001111111001111111111111111001001111001111001001"),
|
||||
BitVector("111001111111001001001111001111001001111111111111111001111111001001001111001111"),
|
||||
BitVector("001111001111111001001001001001111001001111111111001111001111111001001001001001"),
|
||||
BitVector("001001001111001001001001111111111001111111001111001001001111001001001001111111"),
|
||||
};
|
||||
|
||||
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
||||
|
||||
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
|
||||
|
||||
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
||||
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
||||
|
||||
|
||||
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
|
||||
{
|
||||
|
||||
@@ -46,12 +46,15 @@ namespace GSM {
|
||||
|
||||
/** GSM Training sequences from GSM 05.02 5.2.3. */
|
||||
extern const BitVector gTrainingSequence[];
|
||||
extern const BitVector gEdgeTrainingSequence[];
|
||||
|
||||
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
|
||||
extern const BitVector gDummyBurst;
|
||||
|
||||
/** Random access burst synch. sequence */
|
||||
extern const BitVector gRACHSynchSequence;
|
||||
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
||||
extern const BitVector gRACHBurst;
|
||||
|
||||
|
||||
/**@name Modulus operations for frame numbers. */
|
||||
|
||||
22
INSTALLATION
22
INSTALLATION
@@ -2,32 +2,18 @@ Installation Requirements
|
||||
|
||||
|
||||
|
||||
OpenBTS compiles to a simple Unix binary and does not require special
|
||||
osmo-trx compiles to a simple Unix binary and does not require special
|
||||
installation.
|
||||
|
||||
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
|
||||
running configure.
|
||||
|
||||
To run OpenBTS, the following should be installed:
|
||||
|
||||
Asterisk (http://www.asterisk.org), running SIP on port 5060.
|
||||
|
||||
libosip2 (http://www.gnu.org/software/osip/)
|
||||
|
||||
libortp (http://freshmeat.net/projects/ortp/)
|
||||
|
||||
libusrp (http://gnuradio.org).
|
||||
To run osmo-trx, the following should be installed:
|
||||
libuhd (https://gnuradio.org).
|
||||
This is part of the GNURadio installation.
|
||||
It is the only part used by OpenBTS.
|
||||
|
||||
|
||||
OpenBTS logs to syslogd as facility LOG_LOCAL7. Please set your /etc/syslog.conf
|
||||
accordingly.
|
||||
|
||||
|
||||
For information on specific executables, see tests/README.tests and
|
||||
apps/README.apps.
|
||||
|
||||
See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more
|
||||
See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
|
||||
information.
|
||||
|
||||
|
||||
20
LEGAL
20
LEGAL
@@ -1,5 +1,8 @@
|
||||
OpenBTS
|
||||
|
||||
The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details.
|
||||
|
||||
The related copyrights:
|
||||
Most parts copyright 2008-2011 Free Software Foundation.
|
||||
Some parts copyright 2010 Kestrel Signal Processing, Inc.
|
||||
Some parts copyright 2011 Range Networks, Inc.
|
||||
@@ -12,17 +15,9 @@ patented technologies. The user of this software is required to take whatever
|
||||
actions are necessary to avoid patent infringement.
|
||||
|
||||
|
||||
Trademark
|
||||
|
||||
"OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a
|
||||
California corporation. Range reserves the right to control the use of this
|
||||
trademark. Do not use this trademark in commerce without permission and do not
|
||||
rebrand OpenBTS under a different trademark.
|
||||
|
||||
|
||||
Telecom and Radio Spectrum Laws
|
||||
|
||||
The primary function of OpenBTS is the provision of telecommunications service
|
||||
The primary function of OsmoTRX is the provision of telecommunications service
|
||||
over a radio link. This activity is heavily regulated nearly everywhere in
|
||||
the world. Users of this software are expected to comply with local and national
|
||||
regulations in the jurisdictions where this sortware is used with radio equipment.
|
||||
@@ -39,7 +34,7 @@ The legal restrictions listed here are not necessarily exhaustive.
|
||||
|
||||
Note to US Government Users
|
||||
|
||||
The OpenBTS software applications and associated documentation are "Commercial
|
||||
The OsmoTRX software applications and associated documentation are "Commercial
|
||||
Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of
|
||||
"Commercial Computer Software" and "Commercial Computer Software Documentation,"
|
||||
as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
|
||||
@@ -54,13 +49,12 @@ and AGPLv3.
|
||||
Note to US Government Contractors
|
||||
|
||||
GPL is not compatible with "government purpose rights" (GPR). If you receive
|
||||
OpenBTS software under a GPL and deliver it under GPR, you will be in violation
|
||||
OsmoTRX software under a GPL and deliver it under GPR, you will be in violation
|
||||
of GPL and possibly subject to enforcement actions by the original authors and
|
||||
copyright holders, including the Free Software Foundation, Inc.
|
||||
|
||||
|
||||
Software Licensing and Distribution
|
||||
|
||||
A subset of OpenBTS is distributed publicly under AGPLv3. Range reserves the right to
|
||||
distribute most of this source code other licenses as well. See the COPYING file
|
||||
The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file
|
||||
for more information on the license for this distribution.
|
||||
|
||||
14
Makefile.am
14
Makefile.am
@@ -20,17 +20,18 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
ACLOCAL_AMFLAGS = -I config
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -pthread -ldl
|
||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
AM_CXXFLAGS = -Wall -pthread
|
||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
|
||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
|
||||
|
||||
# Order must be preserved
|
||||
SUBDIRS = \
|
||||
sqlite3 \
|
||||
CommonLibs \
|
||||
GSM \
|
||||
Transceiver52M
|
||||
Transceiver52M \
|
||||
tests
|
||||
|
||||
EXTRA_DIST = \
|
||||
autogen.sh \
|
||||
@@ -39,6 +40,9 @@ EXTRA_DIST = \
|
||||
COPYING \
|
||||
README
|
||||
|
||||
.PHONY: release
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
dox: FORCE
|
||||
doxygen doxconfig
|
||||
|
||||
@@ -18,24 +18,21 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
#hack to get around symlink svn:externals in git -kurtis
|
||||
top_srcdir = $(abs_top_srcdir)
|
||||
top_builddir = $(abs_top_builddir)
|
||||
|
||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
||||
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
|
||||
|
||||
SVNDEV = -D'SVN_REV="$(shell svnversion -n $(top_builddir))"'
|
||||
|
||||
STD_DEFINES_AND_INCLUDES = \
|
||||
$(SVNDEV) \
|
||||
-I$(COMMON_INCLUDEDIR) \
|
||||
-I$(GSM_INCLUDEDIR) \
|
||||
-I$(SQLITE_INCLUDEDIR)
|
||||
-I$(GSM_INCLUDEDIR)
|
||||
|
||||
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
||||
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
||||
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl
|
||||
|
||||
if ARCH_ARM
|
||||
ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la
|
||||
else
|
||||
ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la
|
||||
endif
|
||||
|
||||
MOSTLYCLEANFILES = *~
|
||||
|
||||
260
README
260
README
@@ -1,168 +1,116 @@
|
||||
Welcome to the OpenBTS source code.
|
||||
This is the interface to the transcevier.
|
||||
|
||||
|
||||
For free support, please subscribe to openbts-discuss@lists.sourceforge.net.
|
||||
See http://sourceforge.net/mailarchive/forum.php?forum_name=openbts-discuss
|
||||
and https://lists.sourceforge.net/lists/listinfo/openbts-discuss for details.
|
||||
|
||||
For additional information, refer to http://openbts.org.
|
||||
|
||||
|
||||
These are the directories:
|
||||
|
||||
AsteriskConfig Asterisk configuration files for use with OpenBTS.
|
||||
CommonLib Common-use libraries, mostly C++ wrappers for basic facilities.
|
||||
Control Control-layer functions for the protocols of GSM 04.08 and SIP.
|
||||
GSM The GSM stack.
|
||||
SIP Components of the SIP state machines ued by the control layer.
|
||||
SMS The SMS stack.
|
||||
SR The subscriber registry.
|
||||
TRXManager The interface between the GSM stack and the radio.
|
||||
Transceiver The software transceiver and specific installation tests.
|
||||
apps OpenBTS application binaries.
|
||||
doc Project documentation.
|
||||
tests Test fixtures for subsets of OpenBTS components.
|
||||
smqueue RFC-3428 store-and-forward server for SMS
|
||||
Each TRX Manager UDP socket interface represents a single ARFCN.
|
||||
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
|
||||
Give a base port B (5700), the master clock interface is at port P=B.
|
||||
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
|
||||
The corresponding core-side interface for every socket is at P+100.
|
||||
For any given build, the number of ARFCN interfaces can be fixed.
|
||||
|
||||
|
||||
|
||||
By default, OpenBTS assumes the following UDP port assignments:
|
||||
Indications on the Master Clock Interface
|
||||
|
||||
5060 -- Asterisk SIP interface
|
||||
5061 -- local SIP softphone
|
||||
5062 -- OpenBTS SIP interface
|
||||
5063 -- smqueue SIP interface
|
||||
5064 -- subscriber registry SIP interface
|
||||
5700-range -- OpenBTS-transceiver interface
|
||||
The master clock interface is output only (from the radio).
|
||||
Messages are "indications".
|
||||
|
||||
These can be controlled in the CONFIG table in /etc/OpenBTS.db.
|
||||
CLOCK gives the current value of the transceiver clock to be used by the core.
|
||||
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
|
||||
IND CLOCK <totalFrames>
|
||||
|
||||
Standrd paths:
|
||||
/OpenBTS -- Binary installation.
|
||||
/etc/OpenBTS -- Configuration databases.
|
||||
/var/run/OpenBTS -- Real-time reporting databases.
|
||||
|
||||
The script apps/setUpFiles.sh will create these directories and install the
|
||||
correct files in them.
|
||||
|
||||
Commands on the Per-ARFCN Control Interface
|
||||
|
||||
The per-ARFCN control interface uses a command-reponse protocol.
|
||||
Commands are NULL-terminated ASCII strings, one per UDP socket.
|
||||
Each command has a corresponding response.
|
||||
Every command is of the form:
|
||||
|
||||
CMD <cmdtype> [params]
|
||||
|
||||
The <cmdtype> is the actual command.
|
||||
Parameters are optional depending on the commands type.
|
||||
Every response is of the form:
|
||||
|
||||
RSP <cmdtype> <status> [result]
|
||||
|
||||
The <status> is 0 for success and a non-zero error code for failure.
|
||||
Successful responses may include results, depending on the command type.
|
||||
|
||||
|
||||
Power Control
|
||||
|
||||
POWEROFF shuts off transmitter power and stops the demodulator.
|
||||
CMD POWEROFF
|
||||
RSP POWEROFF <status>
|
||||
|
||||
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
|
||||
This command fails if the transmitter and receiver are not yet tuned.
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
If the transceiver is already on, it response with success to this command.
|
||||
CMD POWERON
|
||||
RSP POWERON <status>
|
||||
|
||||
SETPOWER sets output power in dB wrt full scale.
|
||||
This command fails if the transmitter and receiver are not running.
|
||||
CMD SETPOWER <dB>
|
||||
RSP SETPOWER <status> <dB>
|
||||
|
||||
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
|
||||
This command fails if the transmitter and receiver are not running.
|
||||
CMD ADJPOWER <dBStep>
|
||||
RSP ADJPOWER <status> <dBLevel>
|
||||
|
||||
|
||||
Tuning Control
|
||||
|
||||
RXTUNE tunes the receiver to a given frequency in kHz.
|
||||
This command fails if the receiver is already running.
|
||||
(To re-tune you stop the radio, re-tune, and restart.)
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
CMD RXTUNE <kHz>
|
||||
RSP RXTUNE <status> <kHz>
|
||||
|
||||
TXTUNE tunes the transmitter to a given frequency in kHz.
|
||||
This command fails if the transmitter is already running.
|
||||
(To re-tune you stop the radio, re-tune, and restart.)
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
CMD TXTUNE <kHz>
|
||||
RSP TXTUNE <status> <kHz>
|
||||
|
||||
|
||||
Timeslot Control
|
||||
|
||||
SETSLOT sets the format of the uplink timeslots in the ARFCN.
|
||||
The <timeslot> indicates the timeslot of interest.
|
||||
The <chantype> indicates the type of channel that occupies the timeslot.
|
||||
A chantype of zero indicates the timeslot is off.
|
||||
CMD SETSLOT <timeslot> <chantype>
|
||||
RSP SETSLOT <status> <timeslot> <chantype>
|
||||
|
||||
|
||||
Messages on the per-ARFCN Data Interface
|
||||
|
||||
Messages on the data interface carry one radio burst per UDP message.
|
||||
|
||||
|
||||
Received Data Burst
|
||||
|
||||
1 byte timeslot index
|
||||
4 bytes GSM frame number, big endian
|
||||
1 byte RSSI in -dBm
|
||||
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
|
||||
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
|
||||
|
||||
|
||||
Transmit Data Burst
|
||||
|
||||
1 byte timeslot index
|
||||
4 bytes GSM frame number, big endian
|
||||
1 byte transmit level wrt ARFCN max, -dB (attenuation)
|
||||
148 bytes output symbol values, 0 & 1
|
||||
|
||||
|
||||
|
||||
|
||||
Release history:
|
||||
|
||||
Release Name SVN Reposiory SVN Rev Comments
|
||||
|
||||
1.0 (none) SF.net ?? completed L1, L2
|
||||
|
||||
1.1 Arnaudville GNU Radio r10019 (trunk)
|
||||
|
||||
1.2 Breaux Bridge GNU Radio r10088 (trunk) GNU Build, very early assignment
|
||||
|
||||
1.3 Carencro KSP r1 (trunk) first post-injunction release
|
||||
|
||||
1.4 Donaldsonville KSP r23 (trunk) fixed Ubuntu build error
|
||||
|
||||
1.5 Eunice KSP r39 (trunk) fixed L2 bugs related to segmentation
|
||||
removed incomplete SMS directory
|
||||
moved "abort" calls into L3 subclasses
|
||||
|
||||
1.6 New Iberia KSP r130 (trunk) import of all 2.2 improvements to non-SMS release
|
||||
|
||||
|
||||
2.0 St. Francisville KSP r54 (smswork) SMS support
|
||||
file-based configuration
|
||||
|
||||
2.1 Grand Coteau KSP r70 (smswork) DTMF support
|
||||
fixed more Linux-related build errors
|
||||
-lpthread
|
||||
TLMessage constructor
|
||||
expanded stack to prevent overflows in Linux
|
||||
moved gSIPInterface to main app
|
||||
fixed iterator bug in Pager
|
||||
|
||||
2.2 Houma KSP r122 (smswork) added LEGAL notice
|
||||
removed Assert classes
|
||||
stop paging on page response
|
||||
fixed Pager-spin bug
|
||||
fixed Transceiver spin bugs
|
||||
fixed 2^32 microsecond rollover bug
|
||||
reduced stack footprints in Transceiver
|
||||
fixed SMS timestamps
|
||||
check LAI before using TMSI in LUR
|
||||
reduced memory requirement by 75%
|
||||
removed PagerTest
|
||||
fixed stale-transaction bug in paging handler
|
||||
fixed USRP clock rollover bug
|
||||
faster call connection
|
||||
new USRPDevice design
|
||||
|
||||
2.3 Jean Lafitte KSP r190? (trunk) check for out-of-date RACH bursts
|
||||
better TRX-GSM clock sync
|
||||
formal logging system
|
||||
command line interface
|
||||
emergency call setup
|
||||
|
||||
2.4 Kinder KSP r208? (trunk) fixed BCCH neighbor list bug
|
||||
support for neighbor lists
|
||||
fixed support for non-local Asterisk servers
|
||||
cleaner configuration management
|
||||
more realtime control of BCCH parameters
|
||||
proper rejection of Hold messages
|
||||
fixed L3 hanging bug in MTDCheckBYE
|
||||
|
||||
2.4.1 Kinder KSP r462 fixed lots of valgrind errors
|
||||
|
||||
2.4.2 Kinder KSP r482 zero-length calling party number bug
|
||||
g++ 4.4 #includes
|
||||
|
||||
2.5 Lacassine KSP r551 imported Joshua Lackey patches
|
||||
SIP fixes from Anne Kwong
|
||||
SIP fixes from testing with SMS server
|
||||
L3 TI handling fixes
|
||||
SMS server support
|
||||
GNU Radio 3.2 compatibility
|
||||
configurable max range and LU-reject cause
|
||||
"page" & "testcall" CLI features
|
||||
|
||||
2.5.1 Lacassine KSP r595 fixed some build bugs for some Linux distros
|
||||
|
||||
2.5.2 Lacassine KSP r630 fixed channel assignment bug for Nokia DCT4+ handsets
|
||||
|
||||
2.5.3 Lacassine KSP r756 merged fix for transceiver startup crash
|
||||
due to use of uninitialized variables (r646)
|
||||
merged fix for fusb bug from trunk (r582)
|
||||
|
||||
2.5.4 Lacassine KSP r812 merged fixes to build under latest Fedora and
|
||||
to build with git GnuRadio (r814)
|
||||
|
||||
2.6 Mamou KSP r886 fixed infamous fusb bug (r582)
|
||||
fixed idle-filling table size bug
|
||||
smoother uplink power control
|
||||
load-limiting downlink power control
|
||||
new "config" features (optional, static)
|
||||
IMEI interrogation
|
||||
fixed MOD "missing FIFO" bug
|
||||
configurable short code features
|
||||
fixed transceiver startup crash (r646)
|
||||
readline support is back
|
||||
fixed timing advance bug (r844)
|
||||
added CLI "chans" command
|
||||
track time-of-use in TMSI table (r844)
|
||||
added CLI "noise" command (r844)
|
||||
added CLI "rxpower" command (r844)
|
||||
added CLI "unconfig" command
|
||||
|
||||
2.7 Natchitoches Range rxxx (never released publicly)
|
||||
converted TMSITable to sqlite3 (r902)
|
||||
sqlite3-based configuration (r???)
|
||||
converted Logger to syslogd (r903)
|
||||
added support for rest octets (r1022)
|
||||
external database for transaction reporting (r1184)
|
||||
external database for channel status reporting (r1203)
|
||||
in-call delivery and submission of text messages (r1231)
|
||||
RFC-2833 DMTF (r1249)
|
||||
|
||||
2.8 Opelousas Range rxxx move databases to /etc and /var
|
||||
RRLP aiding support
|
||||
|
||||
|
||||
|
||||
107
Transceiver52M/Channelizer.cpp
Normal file
107
Transceiver52M/Channelizer.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Polyphase channelizer
|
||||
*
|
||||
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "Channelizer.h"
|
||||
|
||||
extern "C" {
|
||||
#include "fft.h"
|
||||
#include "convolve.h"
|
||||
}
|
||||
|
||||
static void deinterleave(const float *in, size_t ilen,
|
||||
float **out, size_t olen, size_t m)
|
||||
{
|
||||
size_t i, n;
|
||||
|
||||
for (i = 0; i < olen; i++) {
|
||||
for (n = 0; n < m; n++) {
|
||||
out[m - 1 - n][2 * i + 0] = in[2 * (i * m + n) + 0];
|
||||
out[m - 1 - n][2 * i + 1] = in[2 * (i * m + n) + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Channelizer::inputLen() const
|
||||
{
|
||||
return blockLen * m;
|
||||
}
|
||||
|
||||
size_t Channelizer::outputLen() const
|
||||
{
|
||||
return blockLen;
|
||||
}
|
||||
|
||||
float *Channelizer::outputBuffer(size_t chan) const
|
||||
{
|
||||
if (chan >= m)
|
||||
return NULL;
|
||||
|
||||
return hInputs[chan];
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation based on material found in:
|
||||
*
|
||||
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
|
||||
* Prentice Hall, 2006."
|
||||
*/
|
||||
bool Channelizer::rotate(const float *in, size_t len)
|
||||
{
|
||||
size_t hSize = 2 * hLen * sizeof(float);
|
||||
|
||||
if (!checkLen(blockLen, len))
|
||||
return false;
|
||||
|
||||
deinterleave(in, len, hInputs, blockLen, m);
|
||||
|
||||
/*
|
||||
* Convolve through filterbank while applying and saving sample history
|
||||
*/
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
|
||||
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
|
||||
|
||||
convolve_real(hInputs[i], blockLen,
|
||||
subFilters[i], hLen,
|
||||
hOutputs[i], blockLen,
|
||||
0, blockLen, 1, 0);
|
||||
}
|
||||
|
||||
cxvec_fft(fftHandle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Setup channelizer paramaters */
|
||||
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
|
||||
: ChannelizerBase(m, blockLen, hLen)
|
||||
{
|
||||
}
|
||||
|
||||
Channelizer::~Channelizer()
|
||||
{
|
||||
}
|
||||
34
Transceiver52M/Channelizer.h
Normal file
34
Transceiver52M/Channelizer.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef _CHANNELIZER_RX_H_
|
||||
#define _CHANNELIZER_RX_H_
|
||||
|
||||
#include "ChannelizerBase.h"
|
||||
|
||||
class Channelizer : public ChannelizerBase {
|
||||
public:
|
||||
/** Constructor for channelizing filter bank
|
||||
@param m number of physical channels
|
||||
@param blockLen number of samples per output of each iteration
|
||||
@param hLen number of taps in each constituent filter path
|
||||
*/
|
||||
Channelizer(size_t m, size_t blockLen, size_t hLen = 16);
|
||||
~Channelizer();
|
||||
|
||||
/* Return required input and output buffer lengths */
|
||||
size_t inputLen() const;
|
||||
size_t outputLen() const;
|
||||
|
||||
/** Rotate "input commutator" and drive samples through filterbank
|
||||
@param in complex input vector
|
||||
@param iLen number of samples in buffer (must match block length)
|
||||
@return false on error and true otherwise
|
||||
*/
|
||||
bool rotate(const float *in, size_t iLen);
|
||||
|
||||
/** Get buffer for an output path
|
||||
@param chan channel number of filterbank
|
||||
@return NULL on error and pointer to buffer otherwise
|
||||
*/
|
||||
float *outputBuffer(size_t chan) const;
|
||||
};
|
||||
|
||||
#endif /* _CHANNELIZER_RX_H_ */
|
||||
251
Transceiver52M/ChannelizerBase.cpp
Normal file
251
Transceiver52M/ChannelizerBase.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Polyphase channelizer
|
||||
*
|
||||
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ChannelizerBase.h"
|
||||
|
||||
extern "C" {
|
||||
#include "fft.h"
|
||||
}
|
||||
|
||||
static float sinc(float x)
|
||||
{
|
||||
if (x == 0.0f)
|
||||
return 0.999999999999f;
|
||||
|
||||
return sin(M_PI * x) / (M_PI * x);
|
||||
}
|
||||
|
||||
/*
|
||||
* There are more efficient reversal algorithms, but we only reverse at
|
||||
* initialization so we don't care.
|
||||
*/
|
||||
static void reverse(float *buf, size_t len)
|
||||
{
|
||||
float tmp[2 * len];
|
||||
memcpy(tmp, buf, 2 * len * sizeof(float));
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
buf[2 * i + 0] = tmp[2 * (len - 1 - i) + 0];
|
||||
buf[2 * i + 1] = tmp[2 * (len - 1 - i) + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create polyphase filterbank
|
||||
*
|
||||
* Implementation based material found in,
|
||||
*
|
||||
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
|
||||
* Prentice Hall, 2006."
|
||||
*/
|
||||
bool ChannelizerBase::initFilters()
|
||||
{
|
||||
size_t protoLen = m * hLen;
|
||||
float *proto;
|
||||
float sum = 0.0f, scale = 0.0f;
|
||||
float midpt = (float) (protoLen - 1.0) / 2.0;
|
||||
|
||||
/*
|
||||
* Allocate 'M' partition filters and the temporary prototype
|
||||
* filter. Coefficients are real only and must be 16-byte memory
|
||||
* aligned for SSE usage.
|
||||
*/
|
||||
proto = new float[protoLen];
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
subFilters = (float **) malloc(sizeof(float *) * m);
|
||||
if (!subFilters) {
|
||||
delete[] proto;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
subFilters[i] = (float *)
|
||||
memalign(16, hLen * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the prototype filter with a Blackman-harris window.
|
||||
* Scale coefficients with DC filter gain set to unity divided
|
||||
* by the number of channels.
|
||||
*/
|
||||
float a0 = 0.35875;
|
||||
float a1 = 0.48829;
|
||||
float a2 = 0.14128;
|
||||
float a3 = 0.01168;
|
||||
|
||||
for (size_t i = 0; i < protoLen; i++) {
|
||||
proto[i] = sinc(((float) i - midpt) / (float) m);
|
||||
proto[i] *= a0 -
|
||||
a1 * cos(2 * M_PI * i / (protoLen - 1)) +
|
||||
a2 * cos(4 * M_PI * i / (protoLen - 1)) -
|
||||
a3 * cos(6 * M_PI * i / (protoLen - 1));
|
||||
sum += proto[i];
|
||||
}
|
||||
scale = (float) m / sum;
|
||||
|
||||
/*
|
||||
* Populate partition filters and reverse the coefficients per
|
||||
* convolution requirements.
|
||||
*/
|
||||
for (size_t i = 0; i < hLen; i++) {
|
||||
for (size_t n = 0; n < m; n++) {
|
||||
subFilters[n][2 * i + 0] = proto[i * m + n] * scale;
|
||||
subFilters[n][2 * i + 1] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m; i++)
|
||||
reverse(subFilters[i], hLen);
|
||||
|
||||
delete[] proto;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChannelizerBase::initFFT()
|
||||
{
|
||||
size_t size;
|
||||
|
||||
if (fftInput || fftOutput || fftHandle)
|
||||
return false;
|
||||
|
||||
size = blockLen * m * 2 * sizeof(float);
|
||||
fftInput = (float *) fft_malloc(size);
|
||||
memset(fftInput, 0, size);
|
||||
|
||||
size = (blockLen + hLen) * m * 2 * sizeof(float);
|
||||
fftOutput = (float *) fft_malloc(size);
|
||||
memset(fftOutput, 0, size);
|
||||
|
||||
if (!fftInput | !fftOutput) {
|
||||
LOG(ALERT) << "Memory allocation error";
|
||||
return false;
|
||||
}
|
||||
|
||||
fftHandle = init_fft(0, m, blockLen, blockLen + hLen,
|
||||
fftInput, fftOutput, hLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChannelizerBase::mapBuffers()
|
||||
{
|
||||
if (!fftHandle) {
|
||||
LOG(ALERT) << "FFT buffers not initialized";
|
||||
return false;
|
||||
}
|
||||
|
||||
hInputs = (float **) malloc(sizeof(float *) * m);
|
||||
hOutputs = (float **) malloc(sizeof(float *) * m);
|
||||
if (!hInputs | !hOutputs)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
hInputs[i] = &fftOutput[2 * (i * (blockLen + hLen) + hLen)];
|
||||
hOutputs[i] = &fftInput[2 * (i * blockLen)];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup filterbank internals
|
||||
*/
|
||||
bool ChannelizerBase::init()
|
||||
{
|
||||
/*
|
||||
* Filterbank coefficients, fft plan, history, and output sample
|
||||
* rate conversion blocks
|
||||
*/
|
||||
if (!initFilters()) {
|
||||
LOG(ALERT) << "Failed to initialize channelizing filter";
|
||||
return false;
|
||||
}
|
||||
|
||||
hist = (float **) malloc(sizeof(float *) * m);
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
hist[i] = new float[2 * hLen];
|
||||
memset(hist[i], 0, 2 * hLen * sizeof(float));
|
||||
}
|
||||
|
||||
if (!initFFT()) {
|
||||
LOG(ALERT) << "Failed to initialize FFT";
|
||||
return false;
|
||||
}
|
||||
|
||||
mapBuffers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check vector length validity */
|
||||
bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
|
||||
{
|
||||
if (outerLen != innerLen * m) {
|
||||
LOG(ALERT) << "Invalid outer length " << innerLen
|
||||
<< " is not multiple of " << blockLen;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (innerLen != blockLen) {
|
||||
LOG(ALERT) << "Invalid inner length " << outerLen
|
||||
<< " does not equal " << blockLen;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup channelizer paramaters
|
||||
*/
|
||||
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
|
||||
: fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
|
||||
{
|
||||
this->m = m;
|
||||
this->hLen = hLen;
|
||||
this->blockLen = blockLen;
|
||||
}
|
||||
|
||||
ChannelizerBase::~ChannelizerBase()
|
||||
{
|
||||
free_fft(fftHandle);
|
||||
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
free(subFilters[i]);
|
||||
delete hist[i];
|
||||
}
|
||||
|
||||
fft_free(fftInput);
|
||||
fft_free(fftOutput);
|
||||
|
||||
free(hInputs);
|
||||
free(hOutputs);
|
||||
free(hist);
|
||||
}
|
||||
39
Transceiver52M/ChannelizerBase.h
Normal file
39
Transceiver52M/ChannelizerBase.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef _CHANNELIZER_BASE_H_
|
||||
#define _CHANNELIZER_BASE_H_
|
||||
|
||||
class ChannelizerBase {
|
||||
protected:
|
||||
ChannelizerBase(size_t m, size_t blockLen, size_t hLen);
|
||||
~ChannelizerBase();
|
||||
|
||||
/* Channelizer parameters */
|
||||
size_t m;
|
||||
size_t hLen;
|
||||
size_t blockLen;
|
||||
|
||||
/* Channelizer filterbank sub-filters */
|
||||
float **subFilters;
|
||||
|
||||
/* Input/Output buffers */
|
||||
float **hInputs, **hOutputs, **hist;
|
||||
float *fftInput, *fftOutput;
|
||||
|
||||
/* Pointer to opaque FFT instance */
|
||||
struct fft_hdl *fftHandle;
|
||||
|
||||
/* Initializer internals */
|
||||
bool initFilters();
|
||||
bool initFFT();
|
||||
void releaseFilters();
|
||||
|
||||
/* Map overlapped FFT and filter I/O buffers */
|
||||
bool mapBuffers();
|
||||
|
||||
/* Buffer length validity checking */
|
||||
bool checkLen(size_t innerLen, size_t outerLen);
|
||||
public:
|
||||
/* Initilize channelizer/synthesis filter internals */
|
||||
bool init();
|
||||
};
|
||||
|
||||
#endif /* _CHANNELIZER_BASE_H_ */
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Compilation Flags
|
||||
|
||||
SWLOOPBACK compile for software loopback testing
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "Threads.h"
|
||||
#include "DummyLoad.h"
|
||||
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
int DummyLoad::loadBurst(short *wDummyBurst, int len) {
|
||||
dummyBurst = wDummyBurst;
|
||||
dummyBurstSz = len;
|
||||
}
|
||||
|
||||
|
||||
DummyLoad::DummyLoad (double _desiredSampleRate)
|
||||
{
|
||||
LOG(INFO) << "creating USRP device...";
|
||||
sampleRate = _desiredSampleRate;
|
||||
}
|
||||
|
||||
void DummyLoad::updateTime(void) {
|
||||
gettimeofday(&currTime,NULL);
|
||||
double timeElapsed = (currTime.tv_sec - startTime.tv_sec)*1.0e6 +
|
||||
(currTime.tv_usec - startTime.tv_usec);
|
||||
currstamp = (TIMESTAMP) floor(timeElapsed/(1.0e6/sampleRate));
|
||||
}
|
||||
|
||||
bool DummyLoad::make(bool wSkipRx)
|
||||
{
|
||||
|
||||
samplesRead = 0;
|
||||
samplesWritten = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyLoad::start()
|
||||
{
|
||||
LOG(INFO) << "starting USRP...";
|
||||
underrun = false;
|
||||
gettimeofday(&startTime,NULL);
|
||||
dummyBurstCursor = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyLoad::stop()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Assumes sequential reads
|
||||
int DummyLoad::readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp,
|
||||
bool *wUnderrun,
|
||||
unsigned *RSSI)
|
||||
{
|
||||
updateTime();
|
||||
underrunLock.lock();
|
||||
*wUnderrun = underrun;
|
||||
underrunLock.unlock();
|
||||
if (currstamp+len < timestamp) {
|
||||
usleep(100);
|
||||
return 0;
|
||||
}
|
||||
else if (currstamp < timestamp) {
|
||||
usleep(100);
|
||||
return 0;
|
||||
}
|
||||
else if (timestamp+len < currstamp) {
|
||||
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*(dummyBurstSz-dummyBurstCursor));
|
||||
int retVal = dummyBurstSz-dummyBurstCursor;
|
||||
dummyBurstCursor = 0;
|
||||
return retVal;
|
||||
}
|
||||
else if (timestamp + len > currstamp) {
|
||||
int amount = timestamp + len - currstamp;
|
||||
if (amount < dummyBurstSz-dummyBurstCursor) {
|
||||
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*amount);
|
||||
dummyBurstCursor += amount;
|
||||
return amount;
|
||||
}
|
||||
else {
|
||||
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*(dummyBurstSz-dummyBurstCursor));
|
||||
int retVal = dummyBurstSz-dummyBurstCursor;
|
||||
dummyBurstCursor = 0;
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DummyLoad::writeSamples(short *buf, int len, bool *wUnderrun,
|
||||
unsigned long long timestamp,
|
||||
bool isControl)
|
||||
{
|
||||
updateTime();
|
||||
underrunLock.lock();
|
||||
underrun |= (currstamp+len < timestamp);
|
||||
underrunLock.unlock();
|
||||
return len;
|
||||
}
|
||||
|
||||
bool DummyLoad::updateAlignment(TIMESTAMP timestamp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyLoad::setTxFreq(double wFreq) { return true;};
|
||||
bool DummyLoad::setRxFreq(double wFreq) { return true;};
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "radioDevice.h"
|
||||
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
|
||||
class DummyLoad: public RadioDevice {
|
||||
|
||||
private:
|
||||
|
||||
double sampleRate; ///< the desired sampling rate
|
||||
unsigned long long samplesRead; ///< number of samples read from USRP
|
||||
unsigned long long samplesWritten; ///< number of samples sent to USRP
|
||||
|
||||
Mutex underrunLock;
|
||||
|
||||
struct timeval startTime, currTime;
|
||||
|
||||
TIMESTAMP currstamp;
|
||||
short *dummyBurst;
|
||||
int dummyBurstSz;
|
||||
int dummyBurstCursor;
|
||||
bool underrun;
|
||||
|
||||
void updateTime(void);
|
||||
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
DummyLoad (double _desiredSampleRate);
|
||||
|
||||
int loadBurst(short *wDummyBurst, int len);
|
||||
|
||||
/** Instantiate the USRP */
|
||||
bool make(bool skipRx = false);
|
||||
|
||||
/** Start the USRP */
|
||||
bool start();
|
||||
|
||||
/** Stop the USRP */
|
||||
bool stop();
|
||||
|
||||
/**
|
||||
Read samples from the USRP.
|
||||
@param buf preallocated buf to contain read result
|
||||
@param len number of samples desired
|
||||
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
|
||||
@param timestamp The timestamp of the first samples to be read
|
||||
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
int readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL);
|
||||
/**
|
||||
Write samples to the USRP.
|
||||
@param buf Contains the data to be written.
|
||||
@param len number of samples to write.
|
||||
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param timestamp The timestamp of the first sample of the data buffer.
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
int writeSamples(short *buf, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool isControl = false);
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
|
||||
/** Set the transmitter frequency */
|
||||
bool setTxFreq(double wFreq);
|
||||
|
||||
/** Set the receiver frequency */
|
||||
bool setRxFreq(double wFreq);
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
TIMESTAMP initialWriteTimestamp(void) { return 20000;}
|
||||
|
||||
/** Returns the starting read Timestamp*/
|
||||
TIMESTAMP initialReadTimestamp(void) { return 20000;}
|
||||
|
||||
/** returns the full-scale transmit amplitude **/
|
||||
double fullScaleInputValue() {return 13500.0;}
|
||||
|
||||
/** returns the full-scale receive amplitude **/
|
||||
double fullScaleOutputValue() {return 9450.0;}
|
||||
|
||||
/** Return internal status values */
|
||||
inline double getTxFreq() { return 0;}
|
||||
inline double getRxFreq() { return 0;}
|
||||
inline double getSampleRate() {return sampleRate;}
|
||||
inline double numberRead() { return samplesRead; }
|
||||
inline double numberWritten() { return samplesWritten;}
|
||||
|
||||
};
|
||||
|
||||
@@ -21,19 +21,10 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
#UHD wins if both are defined
|
||||
if UHD
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(UHD_CFLAGS)
|
||||
else
|
||||
if USRP1
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USRP_CFLAGS)
|
||||
else
|
||||
#we should never be here, as this doesn't build if one of the above
|
||||
#doesn't exist
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
endif
|
||||
endif
|
||||
AM_CXXFLAGS = -ldl -lpthread
|
||||
SUBDIRS = arch device
|
||||
|
||||
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
|
||||
@@ -41,88 +32,70 @@ 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 \
|
||||
radioVector.cpp \
|
||||
radioClock.cpp \
|
||||
radioBuffer.cpp \
|
||||
sigProcLib.cpp \
|
||||
signalVector.cpp \
|
||||
Transceiver.cpp \
|
||||
DummyLoad.cpp
|
||||
ChannelizerBase.cpp \
|
||||
Channelizer.cpp \
|
||||
Synthesis.cpp
|
||||
|
||||
if RESAMPLE
|
||||
libtransceiver_la_SOURCES = \
|
||||
libtransceiver_common_la_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
radioIOResamp.cpp
|
||||
else
|
||||
libtransceiver_la_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
radioIO.cpp
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
USRPping \
|
||||
transceiver \
|
||||
sigProcLibTest
|
||||
Resampler.cpp \
|
||||
radioInterfaceResamp.cpp \
|
||||
radioInterfaceMulti.cpp
|
||||
|
||||
noinst_HEADERS = \
|
||||
Complex.h \
|
||||
radioInterface.h \
|
||||
radioVector.h \
|
||||
radioClock.h \
|
||||
radioDevice.h \
|
||||
radioBuffer.h \
|
||||
sigProcLib.h \
|
||||
signalVector.h \
|
||||
Transceiver.h \
|
||||
USRPDevice.h \
|
||||
DummyLoad.h \
|
||||
rcvLPF_651.h \
|
||||
sendLPF_961.h
|
||||
Resampler.h \
|
||||
ChannelizerBase.h \
|
||||
Channelizer.h \
|
||||
Synthesis.h
|
||||
|
||||
USRPping_SOURCES = USRPping.cpp
|
||||
USRPping_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
|
||||
transceiver_SOURCES = runTransceiver.cpp
|
||||
transceiver_LDADD = \
|
||||
libtransceiver.la \
|
||||
COMMON_LDADD = \
|
||||
libtransceiver_common.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
$(COMMON_LA) \
|
||||
$(FFTWF_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS)
|
||||
|
||||
sigProcLibTest_SOURCES = sigProcLibTest.cpp
|
||||
sigProcLibTest_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
bin_PROGRAMS =
|
||||
|
||||
#uhd wins
|
||||
if UHD
|
||||
libtransceiver_la_SOURCES += UHDDevice.cpp
|
||||
transceiver_LDADD += $(UHD_LIBS)
|
||||
USRPping_LDADD += $(UHD_LIBS)
|
||||
sigProcLibTest_LDADD += $(UHD_LIBS)
|
||||
else
|
||||
if USRP1
|
||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||
transceiver_LDADD += $(USRP_LIBS)
|
||||
USRPping_LDADD += $(USRP_LIBS)
|
||||
sigProcLibTest_LDADD += $(USRP_LIBS)
|
||||
else
|
||||
#we should never be here, as one of the above mustbe defined for us to build
|
||||
endif
|
||||
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
|
||||
|
||||
|
||||
MOSTLYCLEANFILES +=
|
||||
|
||||
#radioInterface.cpp
|
||||
#ComplexTest.cpp
|
||||
#sigProcLibTest.cpp
|
||||
#sweepGenerator.cpp
|
||||
#testRadio.cpp
|
||||
|
||||
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
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
Basic model:
|
||||
|
||||
Have channel H = {h_0, h_1, ..., h_{K-1}}.
|
||||
Have received sequence Y = {y_0, ..., y_{K+N}}.
|
||||
Have transmitted sequence X = {x_0, ..., x_{N-1}}.
|
||||
Denote state S_n = {x_n, x_{n-1}, ..., x_{n-L}}.
|
||||
|
||||
Define a bag as an unordered collection with two operations, add and take.
|
||||
We have three bags:
|
||||
S: a bag of survivors.
|
||||
F: a bag of available data structures.
|
||||
|
||||
At time n, start with a non-empty bag S of survivors from time n-1.
|
||||
Take a member out of S, and create all possible branches and their corresponding metrics. If metric ratio is above T, discard branch. Otherwise, check branch against entry in pruning table P. If branch metric is smaller than the existing entry's metric in P, then replace entry with branch. Otherwise, discard branch.
|
||||
Once all possible branches of S have been created and pruned, S should be empty.Empty pruning table back into S, thus P is now empty. Repeat.
|
||||
187
Transceiver52M/Resampler.cpp
Normal file
187
Transceiver52M/Resampler.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Rational Sample Rate Conversion
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Resampler.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convolve.h"
|
||||
}
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327f
|
||||
#endif
|
||||
|
||||
#define MAX_OUTPUT_LEN 4096
|
||||
|
||||
using namespace std;
|
||||
|
||||
static float sinc(float x)
|
||||
{
|
||||
if (x == 0.0)
|
||||
return 0.9999999999;
|
||||
|
||||
return sin(M_PI * x) / (M_PI * x);
|
||||
}
|
||||
|
||||
void Resampler::initFilters(float bw)
|
||||
{
|
||||
float cutoff;
|
||||
float sum = 0.0f, scale = 0.0f;
|
||||
|
||||
/*
|
||||
* Allocate partition filters and the temporary prototype filter
|
||||
* according to numerator of the rational rate. Coefficients are
|
||||
* real only and must be 16-byte memory aligned for SSE usage.
|
||||
*/
|
||||
auto proto = vector<float>(p * filt_len);
|
||||
for (auto &part : partitions)
|
||||
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
|
||||
|
||||
/*
|
||||
* Generate the prototype filter with a Blackman-harris window.
|
||||
* Scale coefficients with DC filter gain set to unity divided
|
||||
* by the number of filter partitions.
|
||||
*/
|
||||
float a0 = 0.35875;
|
||||
float a1 = 0.48829;
|
||||
float a2 = 0.14128;
|
||||
float a3 = 0.01168;
|
||||
|
||||
if (p > q)
|
||||
cutoff = (float) p;
|
||||
else
|
||||
cutoff = (float) q;
|
||||
|
||||
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.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][i] = complex<float>(proto[i * p + n] * scale);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
if (in_len % q) {
|
||||
std::cerr << "Invalid input length " << in_len
|
||||
<< " is not multiple of " << q << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out_len % p) {
|
||||
std::cerr << "Invalid output length " << out_len
|
||||
<< " is not multiple of " << p << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((in_len / q) != (out_len / p)) {
|
||||
std::cerr << "Input/output block length mismatch" << std::endl;
|
||||
std::cerr << "P = " << p << ", Q = " << q << std::endl;
|
||||
std::cerr << "Input len: " << in_len << std::endl;
|
||||
std::cerr << "Output len: " << out_len << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out_len > MAX_OUTPUT_LEN) {
|
||||
std::cerr << "Block length of " << out_len
|
||||
<< " exceeds max of " << MAX_OUTPUT_LEN << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
|
||||
{
|
||||
int n, path;
|
||||
|
||||
if (!check_vec_len(in_len, out_len, p, q))
|
||||
return -1;
|
||||
|
||||
/* Generate output from precomputed input/output paths */
|
||||
for (size_t i = 0; i < out_len; i++) {
|
||||
n = in_index[i];
|
||||
path = out_path[i];
|
||||
|
||||
convolve_real(in, in_len,
|
||||
reinterpret_cast<float *>(partitions[path]),
|
||||
filt_len, &out[2 * i], out_len - i,
|
||||
n, 1, 1, 0);
|
||||
}
|
||||
|
||||
return out_len;
|
||||
}
|
||||
|
||||
bool Resampler::init(float bw)
|
||||
{
|
||||
if (p == 0 || q == 0 || filt_len == 0) return false;
|
||||
|
||||
/* Filterbank filter internals */
|
||||
initFilters(bw);
|
||||
|
||||
/* Precompute filterbank paths */
|
||||
int i = 0;
|
||||
for (auto &index : in_index)
|
||||
index = (q * i++) / p;
|
||||
i = 0;
|
||||
for (auto &path : out_path)
|
||||
path = (q * i++) % p;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Resampler::len()
|
||||
{
|
||||
return filt_len;
|
||||
}
|
||||
|
||||
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
|
||||
{
|
||||
this->p = p;
|
||||
this->q = q;
|
||||
this->filt_len = filt_len;
|
||||
}
|
||||
|
||||
Resampler::~Resampler()
|
||||
{
|
||||
for (auto &part : partitions)
|
||||
free(part);
|
||||
}
|
||||
76
Transceiver52M/Resampler.h
Normal file
76
Transceiver52M/Resampler.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Rational Sample Rate Conversion
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _RESAMPLER_H_
|
||||
#define _RESAMPLER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
|
||||
class Resampler {
|
||||
public:
|
||||
/* Constructor for rational sample rate conversion
|
||||
* @param p numerator of resampling ratio
|
||||
* @param q denominator of resampling ratio
|
||||
* @param filt_len length of each polyphase subfilter
|
||||
*/
|
||||
Resampler(size_t p, size_t q, size_t filt_len = 16);
|
||||
~Resampler();
|
||||
|
||||
/* Initilize resampler filterbank.
|
||||
* @param bw bandwidth factor on filter generation (pre-window)
|
||||
* @return false on error, zero otherwise
|
||||
*
|
||||
* Automatic setting is to compute the filter to prevent aliasing with
|
||||
* a Blackman-Harris window. Adjustment is made through a bandwith
|
||||
* factor to shift the cutoff and/or the constituent filter lengths.
|
||||
* Calculation of specific rolloff factors or 3-dB cutoff points is
|
||||
* left as an excersize for the reader.
|
||||
*/
|
||||
bool init(float bw = 1.0f);
|
||||
|
||||
/* Rotate "commutator" and drive samples through filterbank
|
||||
* @param in continuous buffer of input complex float values
|
||||
* @param in_len input buffer length
|
||||
* @param out continuous buffer of output complex float values
|
||||
* @param out_len output buffer length
|
||||
* @return number of samples outputted, negative on error
|
||||
*
|
||||
* Input and output vector lengths must of be equal multiples of the
|
||||
* rational conversion rate denominator and numerator respectively.
|
||||
*/
|
||||
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
|
||||
|
||||
/* Get filter length
|
||||
* @return number of taps in each filter partition
|
||||
*/
|
||||
size_t len();
|
||||
|
||||
private:
|
||||
size_t p;
|
||||
size_t q;
|
||||
size_t filt_len;
|
||||
std::vector<size_t> in_index;
|
||||
std::vector<size_t> out_path;
|
||||
std::vector<std::complex<float> *> partitions;
|
||||
|
||||
void initFilters(float bw);
|
||||
};
|
||||
|
||||
#endif /* _RESAMPLER_H_ */
|
||||
121
Transceiver52M/Synthesis.cpp
Normal file
121
Transceiver52M/Synthesis.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Polyphase synthesis filter
|
||||
*
|
||||
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include "Synthesis.h"
|
||||
|
||||
extern "C" {
|
||||
#include "fft.h"
|
||||
#include "convolve.h"
|
||||
}
|
||||
|
||||
static void interleave(float **in, size_t ilen,
|
||||
float *out, size_t m)
|
||||
{
|
||||
size_t i, n;
|
||||
|
||||
for (i = 0; i < ilen; i++) {
|
||||
for (n = 0; n < m; n++) {
|
||||
out[2 * (i * m + n) + 0] = in[n][2 * i + 0];
|
||||
out[2 * (i * m + n) + 1] = in[n][2 * i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Synthesis::inputLen() const
|
||||
{
|
||||
return blockLen;
|
||||
}
|
||||
|
||||
size_t Synthesis::outputLen() const
|
||||
{
|
||||
return blockLen * m;
|
||||
}
|
||||
|
||||
float *Synthesis::inputBuffer(size_t chan) const
|
||||
{
|
||||
if (chan >= m)
|
||||
return NULL;
|
||||
|
||||
return hOutputs[chan];
|
||||
}
|
||||
|
||||
bool Synthesis::resetBuffer(size_t chan)
|
||||
{
|
||||
if (chan >= m)
|
||||
return false;
|
||||
|
||||
memset(hOutputs[chan], 0, blockLen * 2 * sizeof(float));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation based on material found in:
|
||||
*
|
||||
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
|
||||
* Prentice Hall, 2006."
|
||||
*/
|
||||
bool Synthesis::rotate(float *out, size_t len)
|
||||
{
|
||||
size_t hSize = 2 * hLen * sizeof(float);
|
||||
|
||||
if (!checkLen(blockLen, len)) {
|
||||
std::cout << "Length fail" << std::endl;
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
cxvec_fft(fftHandle);
|
||||
|
||||
/*
|
||||
* Convolve through filterbank while applying and saving sample history
|
||||
*/
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
|
||||
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
|
||||
|
||||
convolve_real(hInputs[i], blockLen,
|
||||
subFilters[i], hLen,
|
||||
hOutputs[i], blockLen,
|
||||
0, blockLen, 1, 0);
|
||||
}
|
||||
|
||||
/* Interleave into output vector */
|
||||
interleave(hOutputs, blockLen, out, m);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Synthesis::Synthesis(size_t m, size_t blockLen, size_t hLen)
|
||||
: ChannelizerBase(m, blockLen, hLen)
|
||||
{
|
||||
}
|
||||
|
||||
Synthesis::~Synthesis()
|
||||
{
|
||||
}
|
||||
35
Transceiver52M/Synthesis.h
Normal file
35
Transceiver52M/Synthesis.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef _SYNTHESIS_H_
|
||||
#define _SYNTHESIS_H_
|
||||
|
||||
#include "ChannelizerBase.h"
|
||||
|
||||
class Synthesis : public ChannelizerBase {
|
||||
public:
|
||||
/** Constructor for synthesis filterbank
|
||||
@param m number of physical channels
|
||||
@param blockLen number of samples per output of each iteration
|
||||
@param hLen number of taps in each constituent filter path
|
||||
*/
|
||||
Synthesis(size_t m, size_t blockLen, size_t hLen = 16);
|
||||
~Synthesis();
|
||||
|
||||
/* Return required input and output buffer lengths */
|
||||
size_t inputLen() const;
|
||||
size_t outputLen() const;
|
||||
|
||||
/** Rotate "output commutator" and drive samples through filterbank
|
||||
@param out complex output vector
|
||||
@param oLen number of samples in buffer (must match block length * m)
|
||||
@return false on error and true otherwise
|
||||
*/
|
||||
bool rotate(float *out, size_t oLen);
|
||||
|
||||
/** Get buffer for an input path
|
||||
@param chan channel number of filterbank
|
||||
@return NULL on error and pointer to buffer otherwise
|
||||
*/
|
||||
float *inputBuffer(size_t chan) const;
|
||||
bool resetBuffer(size_t chan);
|
||||
};
|
||||
|
||||
#endif /* _SYNTHESIS_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,13 +22,6 @@
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Compilation switches
|
||||
TRANSMIT_LOGGING write every burst on the given slot to a log
|
||||
*/
|
||||
|
||||
#include "radioInterface.h"
|
||||
#include "Interthread.h"
|
||||
#include "GSMCommon.h"
|
||||
@@ -37,44 +30,103 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/** Define this to be the slot number to be logged. */
|
||||
//#define TRANSMIT_LOGGING 1
|
||||
extern "C" {
|
||||
#include "config_defs.h"
|
||||
}
|
||||
|
||||
class Transceiver;
|
||||
|
||||
/** Channel descriptor for transceiver object and channel number pair */
|
||||
struct TransceiverChannel {
|
||||
TransceiverChannel(Transceiver *trx, int num)
|
||||
{
|
||||
this->trx = trx;
|
||||
this->num = num;
|
||||
}
|
||||
|
||||
~TransceiverChannel()
|
||||
{
|
||||
}
|
||||
|
||||
Transceiver *trx;
|
||||
size_t num;
|
||||
};
|
||||
|
||||
/** Internal transceiver state variables */
|
||||
struct TransceiverState {
|
||||
TransceiverState();
|
||||
~TransceiverState();
|
||||
|
||||
/* Initialize a multiframe slot in the filler table */
|
||||
bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
|
||||
|
||||
int chanType[8];
|
||||
|
||||
/* Last timestamp of each timeslot's channel estimate */
|
||||
GSM::Time chanEstimateTime[8];
|
||||
|
||||
/* The filler table */
|
||||
signalVector *fillerTable[102][8];
|
||||
int fillerModulus[8];
|
||||
bool mRetrans;
|
||||
|
||||
/* Most recent channel estimate of all timeslots */
|
||||
signalVector *chanResponse[8];
|
||||
|
||||
/* Most recent DFE feedback filter of all timeslots */
|
||||
signalVector *DFEForward[8];
|
||||
signalVector *DFEFeedback[8];
|
||||
|
||||
/* Most recent SNR, timing, and channel amplitude estimates */
|
||||
float SNRestimate[8];
|
||||
float chanRespOffset[8];
|
||||
complex chanRespAmplitude[8];
|
||||
|
||||
/* Received noise energy levels */
|
||||
float mNoiseLev;
|
||||
noiseVector mNoises;
|
||||
|
||||
/* Shadowed downlink attenuation */
|
||||
int mPower;
|
||||
};
|
||||
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
class Transceiver {
|
||||
|
||||
private:
|
||||
public:
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX, as a string
|
||||
@param GSMcoreAddress IP address of the GSM core, as a string
|
||||
@param 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,
|
||||
double wRssiOffset);
|
||||
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
/** Destructor */
|
||||
~Transceiver();
|
||||
|
||||
UDPSocket mDataSocket; ///< socket for writing to/reading from GSM core
|
||||
UDPSocket mControlSocket; ///< socket for writing/reading control commands from GSM core
|
||||
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
||||
/** Start the control loop */
|
||||
bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge);
|
||||
|
||||
VectorQueue mTransmitPriorityQueue; ///< priority queue of transmit bursts received from GSM core
|
||||
VectorFIFO* mTransmitFIFO; ///< radioInterface FIFO of transmit bursts
|
||||
VectorFIFO* mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||
/** attach the radioInterface receive FIFO */
|
||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||
{
|
||||
if (chan >= mReceiveFIFO.size())
|
||||
return false;
|
||||
|
||||
Thread *mFIFOServiceLoopThread; ///< thread to push/pull bursts into transmit/receive FIFO
|
||||
Thread *mControlServiceLoopThread; ///< thread to process control messages from GSM core
|
||||
Thread *mTransmitPriorityQueueServiceLoopThread;///< thread to process transmit bursts from GSM core
|
||||
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||
|
||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||
double txFullScale; ///< full scale input to radio
|
||||
double rxFullScale; ///< full scale output to radio
|
||||
|
||||
/** Codes for burst types of received bursts*/
|
||||
typedef enum {
|
||||
OFF, ///< timeslot is off
|
||||
TSC, ///< timeslot should contain a normal burst
|
||||
RACH, ///< timeslot should contain an access burst
|
||||
IDLE ///< timeslot is an idle (or dummy) burst
|
||||
} CorrType;
|
||||
mReceiveFIFO[chan] = wFIFO;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** accessor for number of channels */
|
||||
size_t numChans() const { return mChans; };
|
||||
|
||||
/** Codes for channel combinations */
|
||||
typedef enum {
|
||||
@@ -96,121 +148,130 @@ private:
|
||||
LOOPBACK ///< similar go VII, used in loopback testing
|
||||
} ChannelCombination;
|
||||
|
||||
private:
|
||||
int mBasePort;
|
||||
std::string mLocalAddr;
|
||||
std::string mRemoteAddr;
|
||||
|
||||
/** unmodulate a modulated burst */
|
||||
#ifdef TRANSMIT_LOGGING
|
||||
void unModulateVector(signalVector wVector);
|
||||
#endif
|
||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
||||
|
||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||
|
||||
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
||||
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
||||
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
|
||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||
|
||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||
double txFullScale; ///< full scale input to radio
|
||||
double rxFullScale; ///< full scale output to radio
|
||||
|
||||
double rssiOffset; ///< RSSI to dBm conversion offset
|
||||
|
||||
/** modulate and add a burst to the transmit queue */
|
||||
void addRadioVector(BitVector &burst,
|
||||
int RSSI,
|
||||
GSM::Time &wTime);
|
||||
void addRadioVector(size_t chan, BitVector &bits,
|
||||
int RSSI, GSM::Time &wTime);
|
||||
|
||||
/** Update filler table */
|
||||
void updateFillerTable(size_t chan, radioVector *burst);
|
||||
|
||||
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
|
||||
void pushRadioVector(GSM::Time &nowTime);
|
||||
|
||||
/** Pull and demodulate a burst from the receive FIFO */
|
||||
SoftVector *pullRadioVector(GSM::Time &wTime,
|
||||
int &RSSI,
|
||||
int &timingOffset);
|
||||
|
||||
/** Pull and demodulate a burst from the receive FIFO */
|
||||
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
||||
double &timingOffset, double &noise,
|
||||
size_t chan = 0);
|
||||
|
||||
/** Set modulus for specific timeslot */
|
||||
void setModulus(int timeslot);
|
||||
void setModulus(size_t timeslot, size_t chan);
|
||||
|
||||
/** return the expected burst type for the specified timestamp */
|
||||
CorrType expectedCorrType(GSM::Time currTime);
|
||||
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
|
||||
|
||||
/** send messages over the clock socket */
|
||||
void writeClockInterface(void);
|
||||
|
||||
signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
|
||||
int mSPSTx; ///< number of samples per Tx symbol
|
||||
int mSPSRx; ///< number of samples per Rx symbol
|
||||
size_t mChans;
|
||||
|
||||
int mSamplesPerSymbol; ///< number of samples per GSM symbol
|
||||
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
ChannelCombination mChanType[8]; ///< channel types for all timeslots
|
||||
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
|
||||
int mPower; ///< the transmit power in dB
|
||||
unsigned mTSC; ///< the midamble sequence code
|
||||
double mEnergyThreshold; ///< threshold to determine if received data is potentially a GSM burst
|
||||
GSM::Time prevFalseDetectionTime; ///< last timestamp of a false energy detection
|
||||
int fillerModulus[8]; ///< modulus values of all timeslots, in frames
|
||||
signalVector *fillerTable[102][8]; ///< table of modulated filler waveforms for all timeslots
|
||||
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
||||
unsigned mMaxExpectedDelayAB; ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH)
|
||||
unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
|
||||
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
|
||||
|
||||
GSM::Time channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
|
||||
signalVector *channelResponse[8]; ///< most recent channel estimate of all timeslots
|
||||
float SNRestimate[8]; ///< most recent SNR estimate of all timeslots
|
||||
signalVector *DFEForward[8]; ///< most recent DFE feedforward filter of all timeslots
|
||||
signalVector *DFEFeedback[8]; ///< most recent DFE feedback filter of all timeslots
|
||||
float chanRespOffset[8]; ///< most recent timing offset, e.g. TOA, of all timeslots
|
||||
complex chanRespAmplitude[8]; ///< most recent channel amplitude of all timeslots
|
||||
std::vector<TransceiverState> mStates;
|
||||
|
||||
public:
|
||||
/** Start and stop I/O threads through the control socket API */
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX manager, as a string
|
||||
@param wSamplesPerSymbol number of samples per GSM symbol
|
||||
@param wTransmitLatency initial setting of transmit latency
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
int wSamplesPerSymbol,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface);
|
||||
|
||||
/** Destructor */
|
||||
~Transceiver();
|
||||
|
||||
/** start the Transceiver */
|
||||
void start();
|
||||
|
||||
/** attach the radioInterface receive FIFO */
|
||||
void receiveFIFO(VectorFIFO *wFIFO) { mReceiveFIFO = wFIFO;}
|
||||
|
||||
/** attach the radioInterface transmit FIFO */
|
||||
void transmitFIFO(VectorFIFO *wFIFO) { mTransmitFIFO = wFIFO;}
|
||||
/** Protect destructor accessable stop call */
|
||||
Mutex mLock;
|
||||
|
||||
protected:
|
||||
/** drive lower receive I/O and burst generation */
|
||||
void driveReceiveRadio();
|
||||
|
||||
/** drive reception and demodulation of GSM bursts */
|
||||
void driveReceiveFIFO();
|
||||
/** drive demodulation of GSM bursts */
|
||||
void driveReceiveFIFO(size_t chan);
|
||||
|
||||
/** drive transmission of GSM bursts */
|
||||
void driveTransmitFIFO();
|
||||
void driveTxFIFO();
|
||||
|
||||
/** drive handling of control messages from GSM core */
|
||||
void driveControl();
|
||||
void driveControl(size_t chan);
|
||||
|
||||
/**
|
||||
drive modulation and sorting of GSM bursts from GSM core
|
||||
@return true if a burst was transferred successfully
|
||||
*/
|
||||
bool driveTransmitPriorityQueue();
|
||||
bool driveTxPriorityQueue(size_t chan);
|
||||
|
||||
friend void *FIFOServiceLoopAdapter(Transceiver *);
|
||||
friend void *RxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
friend void *ControlServiceLoopAdapter(Transceiver *);
|
||||
friend void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
friend void *RxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *TxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
friend void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *);
|
||||
|
||||
void reset();
|
||||
|
||||
/** set priority on current thread */
|
||||
void setPriority() { mRadioInterface->setPriority(); }
|
||||
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
|
||||
|
||||
void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
|
||||
double rssi, double noise, double toa);
|
||||
};
|
||||
|
||||
/** FIFO thread loop */
|
||||
void *FIFOServiceLoopAdapter(Transceiver *);
|
||||
void *RxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** Main drive threads */
|
||||
void *RxLowerLoopAdapter(Transceiver *);
|
||||
void *TxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
/** control message handler thread loop */
|
||||
void *ControlServiceLoopAdapter(Transceiver *);
|
||||
void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** transmit queueing thread loop */
|
||||
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *);
|
||||
|
||||
void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
@@ -1,992 +0,0 @@
|
||||
/*
|
||||
* Device support for Ettus Research UHD driver
|
||||
* Written by Thomas Tsou <ttsou@vt.edu>
|
||||
*
|
||||
* Copyright 2010,2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include "radioDevice.h"
|
||||
#include "Threads.h"
|
||||
#include "Logger.h"
|
||||
#include <uhd/version.hpp>
|
||||
#include <uhd/property_tree.hpp>
|
||||
#include <uhd/usrp/multi_usrp.hpp>
|
||||
#include <uhd/utils/thread_priority.hpp>
|
||||
#include <uhd/utils/msg.hpp>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#define U1_DEFAULT_CLK_RT 64e6
|
||||
#define U2_DEFAULT_CLK_RT 100e6
|
||||
|
||||
/*
|
||||
master_clk_rt - Master clock frequency - ignored if host resampling is
|
||||
enabled
|
||||
|
||||
rx_smpl_offset - Timing correction in seconds between receive and
|
||||
transmit timestamps. This value corrects for delays on
|
||||
on the RF side of the timestamping point of the device.
|
||||
This value is generally empirically measured.
|
||||
|
||||
smpl_buf_sz - The receive sample buffer size in bytes.
|
||||
|
||||
tx_ampl - Transmit amplitude must be between 0 and 1.0
|
||||
*/
|
||||
const double master_clk_rt = 52e6;
|
||||
const size_t smpl_buf_sz = (1 << 20);
|
||||
const float tx_ampl = .3;
|
||||
|
||||
#ifdef RESAMPLE
|
||||
const double rx_smpl_offset = .00005;
|
||||
#else
|
||||
const double rx_smpl_offset = .0000869;
|
||||
#endif
|
||||
|
||||
/** Timestamp conversion
|
||||
@param timestamp a UHD or OpenBTS timestamp
|
||||
@param rate sample rate
|
||||
@return the converted timestamp
|
||||
*/
|
||||
uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
|
||||
{
|
||||
double secs = (double) ticks / rate;
|
||||
return uhd::time_spec_t(secs);
|
||||
}
|
||||
|
||||
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
|
||||
{
|
||||
TIMESTAMP ticks = ts.get_full_secs() * rate;
|
||||
return ts.get_tick_count(rate) + ticks;
|
||||
}
|
||||
|
||||
/*
|
||||
Sample Buffer - Allows reading and writing of timed samples using OpenBTS
|
||||
or UHD style timestamps. Time conversions are handled
|
||||
internally or accessable through the static convert calls.
|
||||
*/
|
||||
class smpl_buf {
|
||||
public:
|
||||
/** Sample buffer constructor
|
||||
@param len number of 32-bit samples the buffer should hold
|
||||
@param rate sample clockrate
|
||||
@param timestamp
|
||||
*/
|
||||
smpl_buf(size_t len, double rate);
|
||||
~smpl_buf();
|
||||
|
||||
/** Query number of samples available for reading
|
||||
@param timestamp time of first sample
|
||||
@return number of available samples or error
|
||||
*/
|
||||
ssize_t avail_smpls(TIMESTAMP timestamp) const;
|
||||
ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
|
||||
|
||||
/** Read and write
|
||||
@param buf pointer to buffer
|
||||
@param len number of samples desired to read or write
|
||||
@param timestamp time of first stample
|
||||
@return number of actual samples read or written or error
|
||||
*/
|
||||
ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
|
||||
ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
|
||||
ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
|
||||
ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
|
||||
|
||||
/** Buffer status string
|
||||
@return a formatted string describing internal buffer state
|
||||
*/
|
||||
std::string str_status() const;
|
||||
|
||||
/** Formatted error string
|
||||
@param code an error code
|
||||
@return a formatted error string
|
||||
*/
|
||||
static std::string str_code(ssize_t code);
|
||||
|
||||
enum err_code {
|
||||
ERROR_TIMESTAMP = -1,
|
||||
ERROR_READ = -2,
|
||||
ERROR_WRITE = -3,
|
||||
ERROR_OVERFLOW = -4
|
||||
};
|
||||
|
||||
private:
|
||||
uint32_t *data;
|
||||
size_t buf_len;
|
||||
|
||||
double clk_rt;
|
||||
|
||||
TIMESTAMP time_start;
|
||||
TIMESTAMP time_end;
|
||||
|
||||
size_t data_start;
|
||||
size_t data_end;
|
||||
};
|
||||
|
||||
/*
|
||||
uhd_device - UHD implementation of the Device interface. Timestamped samples
|
||||
are sent to and received from the device. An intermediate buffer
|
||||
on the receive side collects and aligns packets of samples.
|
||||
Events and errors such as underruns are reported asynchronously
|
||||
by the device and received in a separate thread.
|
||||
*/
|
||||
class uhd_device : public RadioDevice {
|
||||
public:
|
||||
uhd_device(double rate, bool skip_rx);
|
||||
~uhd_device();
|
||||
|
||||
bool open(const std::string &args);
|
||||
bool start();
|
||||
bool stop();
|
||||
void restart(uhd::time_spec_t ts);
|
||||
void setPriority();
|
||||
enum busType getBus() { return bus; }
|
||||
|
||||
int readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
|
||||
|
||||
int writeSamples(short *buf, int len, bool *underrun,
|
||||
TIMESTAMP timestamp, bool isControl);
|
||||
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
|
||||
bool setTxFreq(double wFreq);
|
||||
bool setRxFreq(double wFreq);
|
||||
|
||||
inline TIMESTAMP initialWriteTimestamp() { return 0; }
|
||||
inline TIMESTAMP initialReadTimestamp() { return 0; }
|
||||
|
||||
inline double fullScaleInputValue() { return 32000 * tx_ampl; }
|
||||
inline double fullScaleOutputValue() { return 32000; }
|
||||
|
||||
double setRxGain(double db);
|
||||
double getRxGain(void) { return rx_gain; }
|
||||
double maxRxGain(void) { return rx_gain_max; }
|
||||
double minRxGain(void) { return rx_gain_min; }
|
||||
|
||||
double setTxGain(double db);
|
||||
double maxTxGain(void) { return tx_gain_max; }
|
||||
double minTxGain(void) { return tx_gain_min; }
|
||||
|
||||
double getTxFreq() { return tx_freq; }
|
||||
double getRxFreq() { return rx_freq; }
|
||||
|
||||
inline double getSampleRate() { return actual_smpl_rt; }
|
||||
inline double numberRead() { return rx_pkt_cnt; }
|
||||
inline double numberWritten() { return 0; }
|
||||
|
||||
/** Receive and process asynchronous message
|
||||
@return true if message received or false on timeout or error
|
||||
*/
|
||||
bool recv_async_msg();
|
||||
|
||||
enum err_code {
|
||||
ERROR_TIMING = -1,
|
||||
ERROR_UNRECOVERABLE = -2,
|
||||
ERROR_UNHANDLED = -3,
|
||||
};
|
||||
|
||||
private:
|
||||
uhd::usrp::multi_usrp::sptr usrp_dev;
|
||||
enum busType bus;
|
||||
|
||||
double desired_smpl_rt, actual_smpl_rt;
|
||||
|
||||
double tx_gain, tx_gain_min, tx_gain_max;
|
||||
double rx_gain, rx_gain_min, rx_gain_max;
|
||||
|
||||
double tx_freq, rx_freq;
|
||||
size_t tx_spp, rx_spp;
|
||||
|
||||
bool started;
|
||||
bool aligned;
|
||||
bool skip_rx;
|
||||
|
||||
size_t rx_pkt_cnt;
|
||||
size_t drop_cnt;
|
||||
uhd::time_spec_t prev_ts;
|
||||
|
||||
TIMESTAMP ts_offset;
|
||||
smpl_buf *rx_smpl_buf;
|
||||
|
||||
void init_gains();
|
||||
void set_ref_clk(bool ext_clk);
|
||||
double set_rates(double rate);
|
||||
bool parse_dev_type();
|
||||
bool flush_recv(size_t num_pkts);
|
||||
int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
|
||||
|
||||
std::string str_code(uhd::rx_metadata_t metadata);
|
||||
std::string str_code(uhd::async_metadata_t metadata);
|
||||
|
||||
Thread async_event_thrd;
|
||||
};
|
||||
|
||||
void *async_event_loop(uhd_device *dev)
|
||||
{
|
||||
while (1) {
|
||||
dev->recv_async_msg();
|
||||
pthread_testcancel();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Catch and drop underrun 'U' and overrun 'O' messages from stdout
|
||||
since we already report using the logging facility. Direct
|
||||
everything else appropriately.
|
||||
*/
|
||||
void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
|
||||
{
|
||||
switch (type) {
|
||||
case uhd::msg::status:
|
||||
LOG(INFO) << msg;
|
||||
break;
|
||||
case uhd::msg::warning:
|
||||
LOG(WARNING) << msg;
|
||||
break;
|
||||
case uhd::msg::error:
|
||||
LOG(ERR) << msg;
|
||||
break;
|
||||
case uhd::msg::fastpath:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uhd_device::uhd_device(double rate, bool skip_rx)
|
||||
: desired_smpl_rt(rate), actual_smpl_rt(0),
|
||||
tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
|
||||
rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
|
||||
tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
|
||||
started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
|
||||
prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
|
||||
{
|
||||
this->skip_rx = skip_rx;
|
||||
}
|
||||
|
||||
uhd_device::~uhd_device()
|
||||
{
|
||||
stop();
|
||||
|
||||
if (rx_smpl_buf)
|
||||
delete rx_smpl_buf;
|
||||
}
|
||||
|
||||
void uhd_device::init_gains()
|
||||
{
|
||||
uhd::gain_range_t range;
|
||||
|
||||
range = usrp_dev->get_tx_gain_range();
|
||||
tx_gain_min = range.start();
|
||||
tx_gain_max = range.stop();
|
||||
|
||||
range = usrp_dev->get_rx_gain_range();
|
||||
rx_gain_min = range.start();
|
||||
rx_gain_max = range.stop();
|
||||
|
||||
usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
|
||||
usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
|
||||
|
||||
tx_gain = usrp_dev->get_tx_gain();
|
||||
rx_gain = usrp_dev->get_rx_gain();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void uhd_device::set_ref_clk(bool ext_clk)
|
||||
{
|
||||
uhd::clock_config_t clk_cfg;
|
||||
|
||||
clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
|
||||
|
||||
if (ext_clk)
|
||||
clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
|
||||
else
|
||||
clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
|
||||
|
||||
usrp_dev->set_clock_config(clk_cfg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
double uhd_device::set_rates(double rate)
|
||||
{
|
||||
double actual_rt, actual_clk_rt;
|
||||
|
||||
#ifndef RESAMPLE
|
||||
// Make sure we can set the master clock rate on this device
|
||||
actual_clk_rt = usrp_dev->get_master_clock_rate();
|
||||
if (actual_clk_rt > U1_DEFAULT_CLK_RT) {
|
||||
LOG(ALERT) << "Cannot set clock rate on this device";
|
||||
LOG(ALERT) << "Please compile with host resampling support";
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
// Set master clock rate
|
||||
usrp_dev->set_master_clock_rate(master_clk_rt);
|
||||
actual_clk_rt = usrp_dev->get_master_clock_rate();
|
||||
|
||||
if (actual_clk_rt != master_clk_rt) {
|
||||
LOG(ALERT) << "Failed to set master clock rate";
|
||||
LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
|
||||
return -1.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set sample rates
|
||||
usrp_dev->set_tx_rate(rate);
|
||||
usrp_dev->set_rx_rate(rate);
|
||||
actual_rt = usrp_dev->get_tx_rate();
|
||||
|
||||
if (actual_rt != rate) {
|
||||
LOG(ALERT) << "Actual sample rate differs from desired rate";
|
||||
return -1.0;
|
||||
}
|
||||
if (usrp_dev->get_rx_rate() != actual_rt) {
|
||||
LOG(ALERT) << "Transmit and receive sample rates do not match";
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
return actual_rt;
|
||||
}
|
||||
|
||||
double uhd_device::setTxGain(double db)
|
||||
{
|
||||
usrp_dev->set_tx_gain(db);
|
||||
tx_gain = usrp_dev->get_tx_gain();
|
||||
|
||||
LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
|
||||
|
||||
return tx_gain;
|
||||
}
|
||||
|
||||
double uhd_device::setRxGain(double db)
|
||||
{
|
||||
usrp_dev->set_rx_gain(db);
|
||||
rx_gain = usrp_dev->get_rx_gain();
|
||||
|
||||
LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
|
||||
|
||||
return rx_gain;
|
||||
}
|
||||
|
||||
/*
|
||||
Parse the UHD device tree and mboard name to find out what device we're
|
||||
dealing with. We need the bus type so that the transceiver knows how to
|
||||
deal with the transport latency. Reject the USRP1 because UHD doesn't
|
||||
support timestamped samples with it.
|
||||
*/
|
||||
bool uhd_device::parse_dev_type()
|
||||
{
|
||||
std::string mboard_str, dev_str;
|
||||
uhd::property_tree::sptr prop_tree;
|
||||
size_t usrp1_str, usrp2_str, b100_str1, b100_str2;
|
||||
|
||||
prop_tree = usrp_dev->get_device()->get_tree();
|
||||
dev_str = prop_tree->access<std::string>("/name").get();
|
||||
mboard_str = usrp_dev->get_mboard_name();
|
||||
|
||||
usrp1_str = dev_str.find("USRP1");
|
||||
b100_str1 = dev_str.find("B-Series");
|
||||
b100_str2 = mboard_str.find("B100");
|
||||
|
||||
if (usrp1_str != std::string::npos) {
|
||||
LOG(ALERT) << "USRP1 is not supported using the UHD driver";
|
||||
LOG(ALERT) << "Please compile with GNU Radio libusrp support";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((b100_str1 != std::string::npos) || (b100_str2 != std::string::npos)) {
|
||||
bus = USB;
|
||||
LOG(INFO) << "Using USB bus for " << dev_str;
|
||||
} else {
|
||||
bus = NET;
|
||||
LOG(INFO) << "Using network bus for " << dev_str;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhd_device::open(const std::string &args)
|
||||
{
|
||||
// Register msg handler
|
||||
uhd::msg::register_handler(&uhd_msg_handler);
|
||||
|
||||
// Find UHD devices
|
||||
uhd::device_addr_t addr(args);
|
||||
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
|
||||
if (dev_addrs.size() == 0) {
|
||||
LOG(ALERT) << "No UHD devices found with address '" << args << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the first found device
|
||||
LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
|
||||
try {
|
||||
usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
|
||||
} catch(...) {
|
||||
LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for a valid device type and set bus type
|
||||
if (!parse_dev_type())
|
||||
return false;
|
||||
|
||||
#ifdef EXTREF
|
||||
set_ref_clk(true);
|
||||
#endif
|
||||
|
||||
// Number of samples per over-the-wire packet
|
||||
tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
|
||||
rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
|
||||
|
||||
// Set rates
|
||||
actual_smpl_rt = set_rates(desired_smpl_rt);
|
||||
if (actual_smpl_rt < 0)
|
||||
return false;
|
||||
|
||||
// Create receive buffer
|
||||
size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
|
||||
rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
|
||||
|
||||
// Set receive chain sample offset
|
||||
ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
|
||||
|
||||
// Initialize and shadow gain values
|
||||
init_gains();
|
||||
|
||||
// Print configuration
|
||||
LOG(INFO) << "\n" << usrp_dev->get_pp_string();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhd_device::flush_recv(size_t num_pkts)
|
||||
{
|
||||
uhd::rx_metadata_t md;
|
||||
size_t num_smpls;
|
||||
uint32_t buff[rx_spp];
|
||||
float timeout;
|
||||
|
||||
// Use .01 sec instead of the default .1 sec
|
||||
timeout = .01;
|
||||
|
||||
for (size_t i = 0; i < num_pkts; i++) {
|
||||
num_smpls = usrp_dev->get_device()->recv(
|
||||
buff,
|
||||
rx_spp,
|
||||
md,
|
||||
uhd::io_type_t::COMPLEX_INT16,
|
||||
uhd::device::RECV_MODE_ONE_PACKET,
|
||||
timeout);
|
||||
|
||||
if (!num_smpls) {
|
||||
switch (md.error_code) {
|
||||
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
||||
return true;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void uhd_device::restart(uhd::time_spec_t ts)
|
||||
{
|
||||
uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
|
||||
usrp_dev->issue_stream_cmd(cmd);
|
||||
|
||||
flush_recv(50);
|
||||
|
||||
usrp_dev->set_time_now(ts);
|
||||
aligned = false;
|
||||
|
||||
cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
|
||||
cmd.stream_now = true;
|
||||
usrp_dev->issue_stream_cmd(cmd);
|
||||
}
|
||||
|
||||
bool uhd_device::start()
|
||||
{
|
||||
LOG(INFO) << "Starting USRP...";
|
||||
|
||||
if (started) {
|
||||
LOG(ERR) << "Device already started";
|
||||
return false;
|
||||
}
|
||||
|
||||
setPriority();
|
||||
|
||||
// Start asynchronous event (underrun check) loop
|
||||
async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
|
||||
|
||||
// Start streaming
|
||||
restart(uhd::time_spec_t(0.0));
|
||||
|
||||
// Display usrp time
|
||||
double time_now = usrp_dev->get_time_now().get_real_secs();
|
||||
LOG(INFO) << "The current time is " << time_now << " seconds";
|
||||
|
||||
started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhd_device::stop()
|
||||
{
|
||||
uhd::stream_cmd_t stream_cmd =
|
||||
uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
|
||||
|
||||
usrp_dev->issue_stream_cmd(stream_cmd);
|
||||
|
||||
started = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void uhd_device::setPriority()
|
||||
{
|
||||
uhd::set_thread_priority_safe();
|
||||
return;
|
||||
}
|
||||
|
||||
int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
|
||||
{
|
||||
uhd::time_spec_t ts;
|
||||
|
||||
if (!num_smpls) {
|
||||
LOG(ERR) << str_code(md);
|
||||
|
||||
switch (md.error_code) {
|
||||
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
||||
LOG(ALERT) << "UHD: Receive timed out";
|
||||
return ERROR_UNRECOVERABLE;
|
||||
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
|
||||
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
|
||||
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
|
||||
case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
|
||||
default:
|
||||
return ERROR_UNHANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
// Missing timestamp
|
||||
if (!md.has_time_spec) {
|
||||
LOG(ALERT) << "UHD: Received packet missing timestamp";
|
||||
return ERROR_UNRECOVERABLE;
|
||||
}
|
||||
|
||||
ts = md.time_spec;
|
||||
|
||||
// Monotonicity check
|
||||
if (ts < prev_ts) {
|
||||
LOG(ALERT) << "UHD: Loss of monotonic time";
|
||||
LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
|
||||
<< "Previous time: " << prev_ts.get_real_secs();
|
||||
return ERROR_TIMING;
|
||||
} else {
|
||||
prev_ts = ts;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uhd_device::readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
||||
{
|
||||
ssize_t rc;
|
||||
uhd::time_spec_t ts;
|
||||
uhd::rx_metadata_t metadata;
|
||||
uint32_t pkt_buf[rx_spp];
|
||||
|
||||
if (skip_rx)
|
||||
return 0;
|
||||
|
||||
// Shift read time with respect to transmit clock
|
||||
timestamp += ts_offset;
|
||||
|
||||
ts = convert_time(timestamp, actual_smpl_rt);
|
||||
LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
|
||||
|
||||
// Check that timestamp is valid
|
||||
rc = rx_smpl_buf->avail_smpls(timestamp);
|
||||
if (rc < 0) {
|
||||
LOG(ERR) << rx_smpl_buf->str_code(rc);
|
||||
LOG(ERR) << rx_smpl_buf->str_status();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Receive samples from the usrp until we have enough
|
||||
while (rx_smpl_buf->avail_smpls(timestamp) < len) {
|
||||
size_t num_smpls = usrp_dev->get_device()->recv(
|
||||
(void*)pkt_buf,
|
||||
rx_spp,
|
||||
metadata,
|
||||
uhd::io_type_t::COMPLEX_INT16,
|
||||
uhd::device::RECV_MODE_ONE_PACKET);
|
||||
|
||||
rx_pkt_cnt++;
|
||||
|
||||
// Check for errors
|
||||
rc = check_rx_md_err(metadata, num_smpls);
|
||||
switch (rc) {
|
||||
case ERROR_UNRECOVERABLE:
|
||||
LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
|
||||
LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
|
||||
exit(-1);
|
||||
case ERROR_TIMING:
|
||||
restart(prev_ts);
|
||||
case ERROR_UNHANDLED:
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
ts = metadata.time_spec;
|
||||
LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
|
||||
|
||||
rc = rx_smpl_buf->write(pkt_buf,
|
||||
num_smpls,
|
||||
metadata.time_spec);
|
||||
|
||||
// Continue on local overrun, exit on other errors
|
||||
if ((rc < 0)) {
|
||||
LOG(ERR) << rx_smpl_buf->str_code(rc);
|
||||
LOG(ERR) << rx_smpl_buf->str_status();
|
||||
if (rc != smpl_buf::ERROR_OVERFLOW)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// We have enough samples
|
||||
rc = rx_smpl_buf->read(buf, len, timestamp);
|
||||
if ((rc < 0) || (rc != len)) {
|
||||
LOG(ERR) << rx_smpl_buf->str_code(rc);
|
||||
LOG(ERR) << rx_smpl_buf->str_status();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int uhd_device::writeSamples(short *buf, int len, bool *underrun,
|
||||
unsigned long long timestamp,bool isControl)
|
||||
{
|
||||
uhd::tx_metadata_t metadata;
|
||||
metadata.has_time_spec = true;
|
||||
metadata.start_of_burst = false;
|
||||
metadata.end_of_burst = false;
|
||||
metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
|
||||
|
||||
// No control packets
|
||||
if (isControl) {
|
||||
LOG(ERR) << "Control packets not supported";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Drop a fixed number of packets (magic value)
|
||||
if (!aligned) {
|
||||
drop_cnt++;
|
||||
|
||||
if (drop_cnt == 1) {
|
||||
LOG(DEBUG) << "Aligning transmitter: stop burst";
|
||||
*underrun = true;
|
||||
metadata.end_of_burst = true;
|
||||
} else if (drop_cnt < 30) {
|
||||
LOG(DEBUG) << "Aligning transmitter: packet advance";
|
||||
return len;
|
||||
} else {
|
||||
LOG(DEBUG) << "Aligning transmitter: start burst";
|
||||
metadata.start_of_burst = true;
|
||||
aligned = true;
|
||||
drop_cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t num_smpls = usrp_dev->get_device()->send(buf,
|
||||
len,
|
||||
metadata,
|
||||
uhd::io_type_t::COMPLEX_INT16,
|
||||
uhd::device::SEND_MODE_FULL_BUFF);
|
||||
|
||||
if (num_smpls != (unsigned) len) {
|
||||
LOG(ALERT) << "UHD: Device send timed out";
|
||||
LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
|
||||
LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return num_smpls;
|
||||
}
|
||||
|
||||
bool uhd_device::updateAlignment(TIMESTAMP timestamp)
|
||||
{
|
||||
aligned = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhd_device::setTxFreq(double wFreq)
|
||||
{
|
||||
uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
|
||||
LOG(INFO) << "\n" << tr.to_pp_string();
|
||||
tx_freq = usrp_dev->get_tx_freq();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhd_device::setRxFreq(double wFreq)
|
||||
{
|
||||
uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
|
||||
LOG(INFO) << "\n" << tr.to_pp_string();
|
||||
rx_freq = usrp_dev->get_rx_freq();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhd_device::recv_async_msg()
|
||||
{
|
||||
uhd::async_metadata_t md;
|
||||
if (!usrp_dev->get_device()->recv_async_msg(md))
|
||||
return false;
|
||||
|
||||
// Assume that any error requires resynchronization
|
||||
if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
|
||||
aligned = false;
|
||||
|
||||
if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
|
||||
(md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
|
||||
LOG(ERR) << str_code(md);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
|
||||
{
|
||||
std::ostringstream ost("UHD: ");
|
||||
|
||||
switch (metadata.error_code) {
|
||||
case uhd::rx_metadata_t::ERROR_CODE_NONE:
|
||||
ost << "No error";
|
||||
break;
|
||||
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
||||
ost << "No packet received, implementation timed-out";
|
||||
break;
|
||||
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
|
||||
ost << "A stream command was issued in the past";
|
||||
break;
|
||||
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
|
||||
ost << "Expected another stream command";
|
||||
break;
|
||||
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
|
||||
ost << "An internal receive buffer has filled";
|
||||
break;
|
||||
case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
|
||||
ost << "The packet could not be parsed";
|
||||
break;
|
||||
default:
|
||||
ost << "Unknown error " << metadata.error_code;
|
||||
}
|
||||
|
||||
if (metadata.has_time_spec)
|
||||
ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
|
||||
|
||||
return ost.str();
|
||||
}
|
||||
|
||||
std::string uhd_device::str_code(uhd::async_metadata_t metadata)
|
||||
{
|
||||
std::ostringstream ost("UHD: ");
|
||||
|
||||
switch (metadata.event_code) {
|
||||
case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
|
||||
ost << "A packet was successfully transmitted";
|
||||
break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
|
||||
ost << "An internal send buffer has emptied";
|
||||
break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
|
||||
ost << "Packet loss between host and device";
|
||||
break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
|
||||
ost << "Packet time was too late or too early";
|
||||
break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
|
||||
ost << "Underflow occurred inside a packet";
|
||||
break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
|
||||
ost << "Packet loss within a burst";
|
||||
break;
|
||||
default:
|
||||
ost << "Unknown error " << metadata.event_code;
|
||||
}
|
||||
|
||||
if (metadata.has_time_spec)
|
||||
ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
|
||||
|
||||
return ost.str();
|
||||
}
|
||||
|
||||
smpl_buf::smpl_buf(size_t len, double rate)
|
||||
: buf_len(len), clk_rt(rate),
|
||||
time_start(0), time_end(0), data_start(0), data_end(0)
|
||||
{
|
||||
data = new uint32_t[len];
|
||||
}
|
||||
|
||||
smpl_buf::~smpl_buf()
|
||||
{
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
|
||||
{
|
||||
if (timestamp < time_start)
|
||||
return ERROR_TIMESTAMP;
|
||||
else if (timestamp >= time_end)
|
||||
return 0;
|
||||
else
|
||||
return time_end - timestamp;
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
|
||||
{
|
||||
return avail_smpls(convert_time(timespec, clk_rt));
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
|
||||
{
|
||||
int type_sz = 2 * sizeof(short);
|
||||
|
||||
// Check for valid read
|
||||
if (timestamp < time_start)
|
||||
return ERROR_TIMESTAMP;
|
||||
if (timestamp >= time_end)
|
||||
return 0;
|
||||
if (len >= buf_len)
|
||||
return ERROR_READ;
|
||||
|
||||
// How many samples should be copied
|
||||
size_t num_smpls = time_end - timestamp;
|
||||
if (num_smpls > len)
|
||||
num_smpls = len;
|
||||
|
||||
// Starting index
|
||||
size_t read_start = data_start + (timestamp - time_start);
|
||||
|
||||
// Read it
|
||||
if (read_start + num_smpls < buf_len) {
|
||||
size_t numBytes = len * type_sz;
|
||||
memcpy(buf, data + read_start, numBytes);
|
||||
} else {
|
||||
size_t first_cp = (buf_len - read_start) * type_sz;
|
||||
size_t second_cp = len * type_sz - first_cp;
|
||||
|
||||
memcpy(buf, data + read_start, first_cp);
|
||||
memcpy((char*) buf + first_cp, data, second_cp);
|
||||
}
|
||||
|
||||
data_start = (read_start + len) % buf_len;
|
||||
time_start = timestamp + len;
|
||||
|
||||
if (time_start > time_end)
|
||||
return ERROR_READ;
|
||||
else
|
||||
return num_smpls;
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
|
||||
{
|
||||
return read(buf, len, convert_time(ts, clk_rt));
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
|
||||
{
|
||||
int type_sz = 2 * sizeof(short);
|
||||
|
||||
// Check for valid write
|
||||
if ((len == 0) || (len >= buf_len))
|
||||
return ERROR_WRITE;
|
||||
if ((timestamp + len) <= time_end)
|
||||
return ERROR_TIMESTAMP;
|
||||
|
||||
// Starting index
|
||||
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
|
||||
|
||||
// Write it
|
||||
if ((write_start + len) < buf_len) {
|
||||
size_t numBytes = len * type_sz;
|
||||
memcpy(data + write_start, buf, numBytes);
|
||||
} else {
|
||||
size_t first_cp = (buf_len - write_start) * type_sz;
|
||||
size_t second_cp = len * type_sz - first_cp;
|
||||
|
||||
memcpy(data + write_start, buf, first_cp);
|
||||
memcpy(data, (char*) buf + first_cp, second_cp);
|
||||
}
|
||||
|
||||
data_end = (write_start + len) % buf_len;
|
||||
time_end = timestamp + len;
|
||||
|
||||
if (((write_start + len) > buf_len) && (data_end > data_start))
|
||||
return ERROR_OVERFLOW;
|
||||
else if (time_end <= time_start)
|
||||
return ERROR_WRITE;
|
||||
else
|
||||
return len;
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
|
||||
{
|
||||
return write(buf, len, convert_time(ts, clk_rt));
|
||||
}
|
||||
|
||||
std::string smpl_buf::str_status() const
|
||||
{
|
||||
std::ostringstream ost("Sample buffer: ");
|
||||
|
||||
ost << "length = " << buf_len;
|
||||
ost << ", time_start = " << time_start;
|
||||
ost << ", time_end = " << time_end;
|
||||
ost << ", data_start = " << data_start;
|
||||
ost << ", data_end = " << data_end;
|
||||
|
||||
return ost.str();
|
||||
}
|
||||
|
||||
std::string smpl_buf::str_code(ssize_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case ERROR_TIMESTAMP:
|
||||
return "Sample buffer: Requested timestamp is not valid";
|
||||
case ERROR_READ:
|
||||
return "Sample buffer: Read error";
|
||||
case ERROR_WRITE:
|
||||
return "Sample buffer: Write error";
|
||||
case ERROR_OVERFLOW:
|
||||
return "Sample buffer: Overrun";
|
||||
default:
|
||||
return "Sample buffer: Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
|
||||
{
|
||||
return new uhd_device(smpl_rt, skip_rx);
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <Logger.h>
|
||||
#include <Configuration.h>
|
||||
#include "radioDevice.h"
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// Configure logger.
|
||||
if (argc>1) gLogInit(argv[1]);
|
||||
else gLogInit("DEBUG");
|
||||
//if (argc>2) gSetLogFile(argv[2]);
|
||||
|
||||
RadioDevice *usrp = RadioDevice::make(52.0e6/192.0);
|
||||
|
||||
usrp->open("");
|
||||
|
||||
TIMESTAMP timestamp;
|
||||
|
||||
usrp->setTxFreq(825.4e6);
|
||||
usrp->setRxFreq(825.4e6);
|
||||
|
||||
usrp->start();
|
||||
|
||||
usrp->setRxGain(57);
|
||||
|
||||
LOG(INFO) << "Looping...";
|
||||
bool underrun;
|
||||
|
||||
short data[]={0x00,0x02};
|
||||
|
||||
usrp->updateAlignment(20000);
|
||||
usrp->updateAlignment(21000);
|
||||
|
||||
int numpkts = 1;
|
||||
short data2[512*2*numpkts];
|
||||
for (int i = 0; i < 512*numpkts; i++) {
|
||||
data2[i<<1] = 10000;//4096*cos(2*3.14159*(i % 126)/126);
|
||||
data2[(i<<1) + 1] = 10000;//4096*sin(2*3.14159*(i % 126)/126);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1; i++)
|
||||
usrp->writeSamples((short*) data2,512*numpkts,&underrun,102000+i*1000);
|
||||
|
||||
timestamp = 19000;
|
||||
double sum = 0.0;
|
||||
unsigned long num = 0;
|
||||
while (1) {
|
||||
short readBuf[512*2];
|
||||
int rd = usrp->readSamples(readBuf,512,&underrun,timestamp);
|
||||
if (rd) {
|
||||
LOG(INFO) << "rcvd. data@:" << timestamp;
|
||||
for (int i = 0; i < 512; i++) {
|
||||
uint32_t *wordPtr = (uint32_t *) &readBuf[2*i];
|
||||
printf ("%llu: %d %d\n", timestamp+i,readBuf[2*i],readBuf[2*i+1]);
|
||||
sum += (readBuf[2*i+1]*readBuf[2*i+1] + readBuf[2*i]*readBuf[2*i]);
|
||||
num++;
|
||||
//if (num % 10000 == 0) printf("avg pwr: %f\n",sum/num);
|
||||
}
|
||||
timestamp += rd;
|
||||
//usrp->writeSamples((short*) data2,512*numpkts,&underrun,timestamp+1000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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
|
||||
22
Transceiver52M/arch/arm/Makefile.am
Normal file
22
Transceiver52M/arch/arm/Makefile.am
Normal file
@@ -0,0 +1,22 @@
|
||||
if ARCH_ARM_A15
|
||||
ARCH_FLAGS = -mfpu=neon-vfpv4
|
||||
else
|
||||
ARCH_FLAGS = -mfpu=neon
|
||||
endif
|
||||
|
||||
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common
|
||||
AM_CCASFLAGS = $(ARCH_FLAGS)
|
||||
|
||||
noinst_LTLIBRARIES = libarch.la
|
||||
|
||||
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
|
||||
|
||||
libarch_la_SOURCES = \
|
||||
convert.c \
|
||||
convert_neon.S \
|
||||
convolve.c \
|
||||
convolve_neon.S \
|
||||
scale.c \
|
||||
scale_neon.S \
|
||||
mult.c \
|
||||
mult_neon.S
|
||||
85
Transceiver52M/arch/arm/convert.c
Normal file
85
Transceiver52M/arch/arm/convert.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* NEON type conversions
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "convert.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
|
||||
void neon_convert_si16_ps_4n(float *, const short *, int);
|
||||
|
||||
void convert_init(void) {
|
||||
}
|
||||
|
||||
/* 4*N 16-bit signed integer conversion with remainder */
|
||||
static void neon_convert_si16_ps(float *out,
|
||||
const short *in,
|
||||
int len)
|
||||
{
|
||||
int start = len / 4 * 4;
|
||||
|
||||
neon_convert_si16_ps_4n(out, in, len >> 2);
|
||||
|
||||
for (int i = 0; i < len % 4; i++)
|
||||
out[start + i] = (float) in[start + i];
|
||||
}
|
||||
|
||||
/* 4*N 16-bit signed integer conversion with remainder */
|
||||
static void neon_convert_ps_si16(short *out,
|
||||
const float *in,
|
||||
const float *scale,
|
||||
int len)
|
||||
{
|
||||
int start = len / 4 * 4;
|
||||
|
||||
neon_convert_ps_si16_4n(out, in, scale, len >> 2);
|
||||
|
||||
for (int i = 0; i < len % 4; i++)
|
||||
out[start + i] = (short) (in[start + i] * (*scale));
|
||||
}
|
||||
|
||||
void convert_float_short(short *out, const float *in, float scale, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
float q[4] = { scale, scale, scale, scale };
|
||||
|
||||
if (len % 4)
|
||||
neon_convert_ps_si16(out, in, q, len);
|
||||
else
|
||||
neon_convert_ps_si16_4n(out, in, q, len >> 2);
|
||||
#else
|
||||
base_convert_float_short(out, in, scale, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void convert_short_float(float *out, const short *in, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if (len % 4)
|
||||
neon_convert_si16_ps(out, in, len);
|
||||
else
|
||||
neon_convert_si16_ps_4n(out, in, len >> 2);
|
||||
#else
|
||||
base_convert_short_float(out, in, len);
|
||||
#endif
|
||||
}
|
||||
51
Transceiver52M/arch/arm/convert_neon.S
Normal file
51
Transceiver52M/arch/arm/convert_neon.S
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* NEON type conversions
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.align 2
|
||||
.global neon_convert_ps_si16_4n
|
||||
.type neon_convert_ps_si16_4n, %function
|
||||
neon_convert_ps_si16_4n:
|
||||
vld1.32 {q1}, [r2]
|
||||
.loop_fltint:
|
||||
vld1.64 {d0-d1}, [r1]!
|
||||
vmul.f32 q0, q1
|
||||
vcvt.s32.f32 q2, q0
|
||||
vqmovn.s32 d0, q2
|
||||
vst1.64 {d0}, [r0]!
|
||||
subs r3, #1
|
||||
bne .loop_fltint
|
||||
bx lr
|
||||
.size neon_convert_ps_si16_4n, .-neon_convert_ps_si16_4n
|
||||
.text
|
||||
.align 2
|
||||
.global neon_convert_si16_ps_4n
|
||||
.type neon_convert_si16_ps_4n, %function
|
||||
neon_convert_si16_ps_4n:
|
||||
.loop_intflt:
|
||||
vld1.64 {d0}, [r1]!
|
||||
vmovl.s16 q1, d0
|
||||
vcvt.f32.s32 q0, q1
|
||||
vst1.64 {q0}, [r0]!
|
||||
subs r2, #1
|
||||
bne .loop_intflt
|
||||
bx lr
|
||||
.size neon_convert_si16_ps_4n, .-neon_convert_si16_ps_4n
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
146
Transceiver52M/arch/arm/convolve.c
Normal file
146
Transceiver52M/arch/arm/convolve.c
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* NEON Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Forward declarations from base implementation */
|
||||
int _base_convolve_real(float *x, int x_len,
|
||||
float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int _base_convolve_complex(float *x, int x_len,
|
||||
float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int bounds_check(int x_len, int h_len, int y_len,
|
||||
int start, int len, int step);
|
||||
|
||||
#ifdef HAVE_NEON
|
||||
/* Calls into NEON assembler */
|
||||
void neon_conv_real4(float *x, float *h, float *y, int len);
|
||||
void neon_conv_real8(float *x, float *h, float *y, int len);
|
||||
void neon_conv_real12(float *x, float *h, float *y, int len);
|
||||
void neon_conv_real16(float *x, float *h, float *y, int len);
|
||||
void neon_conv_real20(float *x, float *h, float *y, int len);
|
||||
void mac_cx_neon4(float *x, float *h, float *y, int len);
|
||||
|
||||
/* Complex-complex convolution */
|
||||
static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
mac_cx_neon4(&x[2 * i], h, &y[2 * i], h_len >> 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* API: Initalize convolve module */
|
||||
void convolve_init(void)
|
||||
{
|
||||
/* Stub */
|
||||
return;
|
||||
}
|
||||
|
||||
/* API: Aligned complex-real */
|
||||
int convolve_real(float *x, int x_len,
|
||||
float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
void (*conv_func)(float *, float *, float *, int) = NULL;
|
||||
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
#ifdef HAVE_NEON
|
||||
if (step <= 4) {
|
||||
switch (h_len) {
|
||||
case 4:
|
||||
conv_func = neon_conv_real4;
|
||||
break;
|
||||
case 8:
|
||||
conv_func = neon_conv_real8;
|
||||
break;
|
||||
case 12:
|
||||
conv_func = neon_conv_real12;
|
||||
break;
|
||||
case 16:
|
||||
conv_func = neon_conv_real16;
|
||||
break;
|
||||
case 20:
|
||||
conv_func = neon_conv_real20;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (conv_func) {
|
||||
conv_func(&x[2 * (-(h_len - 1) + start)],
|
||||
h, y, len);
|
||||
} else {
|
||||
_base_convolve_real(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* API: Aligned complex-complex */
|
||||
int convolve_complex(float *x, int x_len,
|
||||
float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
void (*conv_func)(float *, float *, float *, int, int) = NULL;
|
||||
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
#ifdef HAVE_NEON
|
||||
if (step <= 4 && !(h_len % 4))
|
||||
conv_func = neon_conv_cmplx_4n;
|
||||
#endif
|
||||
if (conv_func) {
|
||||
conv_func(&x[2 * (-(h_len - 1) + start)],
|
||||
h, y, h_len, len);
|
||||
} else {
|
||||
_base_convolve_complex(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
277
Transceiver52M/arch/arm/convolve_neon.S
Normal file
277
Transceiver52M/arch/arm/convolve_neon.S
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* NEON Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.align 2
|
||||
.global neon_conv_real4
|
||||
.type neon_conv_real4, %function
|
||||
neon_conv_real4:
|
||||
push {r4, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]
|
||||
ldr r4, =8
|
||||
.neon_conv_loop4:
|
||||
vld2.32 {q2-q3}, [r0], r4
|
||||
vmul.f32 q4, q2, q0
|
||||
vmul.f32 q5, q3, q0
|
||||
vpadd.f32 d12, d8, d9
|
||||
vpadd.f32 d13, d10, d11
|
||||
vpadd.f32 d14, d12, d13
|
||||
vst1.64 {d14}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop4
|
||||
vpop {q4-q7}
|
||||
pop {r4, pc}
|
||||
.size neon_conv_real4, .-neon_conv_real4
|
||||
.align 2
|
||||
.p2align 4,,15
|
||||
.global neon_conv_real8
|
||||
.type neon_conv_real8, %function
|
||||
neon_conv_real8:
|
||||
push {r4-r5, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r1]
|
||||
add r4, r0, #32
|
||||
ldr r5, =8
|
||||
.neon_conv_loop8:
|
||||
vld2.32 {q4-q5}, [r0], r5
|
||||
vld2.32 {q6-q7}, [r4], r5
|
||||
vmul.f32 q8, q4, q0
|
||||
vmul.f32 q9, q5, q0
|
||||
vmul.f32 q10, q6, q2
|
||||
vmul.f32 q11, q7, q2
|
||||
|
||||
vadd.f32 q12, q8, q10
|
||||
vadd.f32 q13, q9, q11
|
||||
|
||||
vpadd.f32 d22, d24, d25
|
||||
vpadd.f32 d23, d26, d27
|
||||
vpadd.f32 d24, d22, d23
|
||||
vst1.64 {d24}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop8
|
||||
vpop {q4-q7}
|
||||
pop {r4-r5, pc}
|
||||
.size neon_conv_real8, .-neon_conv_real8
|
||||
.align 2
|
||||
.global neon_conv_real12
|
||||
.type neon_conv_real12, %function
|
||||
neon_conv_real12:
|
||||
push {r4-r6, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r1]!
|
||||
vld2.32 {q4-q5}, [r1]!
|
||||
add r4, r0, #32
|
||||
add r5, r0, #64
|
||||
ldr r6, =8
|
||||
.neon_conv_loop12:
|
||||
vld2.32 {q6-q7}, [r0], r6
|
||||
vld2.32 {q8-q9}, [r4], r6
|
||||
vld2.32 {q10-q11}, [r5], r6
|
||||
#ifdef HAVE_NEON_FMA
|
||||
vfma.f32 q1, q6, q0
|
||||
vfma.f32 q3, q7, q0
|
||||
vfma.f32 q1, q8, q2
|
||||
vfma.f32 q3, q9, q2
|
||||
vfma.f32 q1, q10, q4
|
||||
vfma.f32 q3, q11, q4
|
||||
#else
|
||||
vmul.f32 q12, q6, q0
|
||||
vmul.f32 q13, q7, q0
|
||||
vmul.f32 q14, q8, q2
|
||||
vmul.f32 q15, q9, q2
|
||||
vmul.f32 q1, q10, q4
|
||||
vmul.f32 q3, q11, q4
|
||||
|
||||
vadd.f32 q5, q12, q14
|
||||
vadd.f32 q6, q13, q15
|
||||
vadd.f32 q1, q5, q1
|
||||
vadd.f32 q3, q6, q3
|
||||
#endif
|
||||
vpadd.f32 d2, d2, d3
|
||||
vpadd.f32 d3, d6, d7
|
||||
vpadd.f32 d6, d2, d3
|
||||
vst1.64 {d6}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop12
|
||||
vpop {q4-q7}
|
||||
pop {r4-r6, pc}
|
||||
.size neon_conv_real12, .-neon_conv_real12
|
||||
.align 2
|
||||
.global neon_conv_real16
|
||||
.type neon_conv_real16, %function
|
||||
neon_conv_real16:
|
||||
push {r4-r7, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r1]!
|
||||
vld2.32 {q4-q5}, [r1]!
|
||||
vld2.32 {q6-q7}, [r1]
|
||||
add r4, r0, #32
|
||||
add r5, r0, #64
|
||||
add r6, r0, #96
|
||||
ldr r7, =8
|
||||
.neon_conv_loop16:
|
||||
vld2.32 {q8-q9}, [r0], r7
|
||||
vld2.32 {q10-q11}, [r4], r7
|
||||
vld2.32 {q12-q13}, [r5], r7
|
||||
vld2.32 {q14-q15}, [r6], r7
|
||||
#ifdef HAVE_NEON_FMA
|
||||
vmul.f32 q1, q8, q0
|
||||
vmul.f32 q3, q9, q0
|
||||
vfma.f32 q1, q10, q2
|
||||
vfma.f32 q3, q11, q2
|
||||
vfma.f32 q1, q12, q4
|
||||
vfma.f32 q3, q13, q4
|
||||
vfma.f32 q1, q14, q6
|
||||
vfma.f32 q3, q15, q6
|
||||
#else
|
||||
vmul.f32 q1, q8, q0
|
||||
vmul.f32 q3, q9, q0
|
||||
vmul.f32 q5, q10, q2
|
||||
vmul.f32 q7, q11, q2
|
||||
vmul.f32 q8, q12, q4
|
||||
vmul.f32 q9, q13, q4
|
||||
vmul.f32 q10, q14, q6
|
||||
vmul.f32 q11, q15, q6
|
||||
|
||||
vadd.f32 q1, q1, q5
|
||||
vadd.f32 q3, q3, q7
|
||||
vadd.f32 q5, q8, q10
|
||||
vadd.f32 q7, q9, q11
|
||||
vadd.f32 q1, q1, q5
|
||||
vadd.f32 q3, q3, q7
|
||||
#endif
|
||||
vpadd.f32 d2, d2, d3
|
||||
vpadd.f32 d3, d6, d7
|
||||
vpadd.f32 d6, d2, d3
|
||||
vst1.64 {d6}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop16
|
||||
vpop {q4-q7}
|
||||
pop {r4-r7, pc}
|
||||
.size neon_conv_real16, .-neon_conv_real16
|
||||
.align 2
|
||||
.global neon_conv_real20
|
||||
.type neon_conv_real20, %function
|
||||
neon_conv_real20:
|
||||
push {r4-r8, lr}
|
||||
vpush {q4-q7}
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r1]!
|
||||
vld2.32 {q4-q5}, [r1]!
|
||||
vld2.32 {q6-q7}, [r1]!
|
||||
vld2.32 {q8-q9}, [r1]
|
||||
add r4, r0, #32
|
||||
add r5, r0, #64
|
||||
add r6, r0, #96
|
||||
add r7, r0, #128
|
||||
ldr r8, =8
|
||||
.neon_conv_loop20:
|
||||
vld2.32 {q10-q11}, [r0], r8
|
||||
vld2.32 {q12-q13}, [r4], r8
|
||||
vld2.32 {q14-q15}, [r5], r8
|
||||
#ifdef HAVE_NEON_FMA
|
||||
vmul.f32 q1, q10, q0
|
||||
vfma.f32 q1, q12, q2
|
||||
vfma.f32 q1, q14, q4
|
||||
vmul.f32 q3, q11, q0
|
||||
vfma.f32 q3, q13, q2
|
||||
vfma.f32 q3, q15, q4
|
||||
|
||||
vld2.32 {q12-q13}, [r6], r8
|
||||
vld2.32 {q14-q15}, [r7], r8
|
||||
|
||||
vfma.f32 q1, q12, q6
|
||||
vfma.f32 q3, q13, q6
|
||||
vfma.f32 q1, q14, q8
|
||||
vfma.f32 q3, q15, q8
|
||||
#else
|
||||
vmul.f32 q1, q10, q0
|
||||
vmul.f32 q3, q12, q2
|
||||
vmul.f32 q5, q14, q4
|
||||
vmul.f32 q7, q11, q0
|
||||
vmul.f32 q9, q13, q2
|
||||
vmul.f32 q10, q15, q4
|
||||
vadd.f32 q1, q1, q3
|
||||
vadd.f32 q3, q7, q9
|
||||
vadd.f32 q9, q1, q5
|
||||
vadd.f32 q10, q3, q10
|
||||
|
||||
vld2.32 {q12-q13}, [r6], r8
|
||||
vld2.32 {q14-q15}, [r7], r8
|
||||
|
||||
vmul.f32 q1, q12, q6
|
||||
vmul.f32 q3, q13, q6
|
||||
vmul.f32 q5, q14, q8
|
||||
vmul.f32 q7, q15, q8
|
||||
vadd.f32 q12, q1, q9
|
||||
vadd.f32 q14, q3, q10
|
||||
vadd.f32 q1, q12, q5
|
||||
vadd.f32 q3, q14, q7
|
||||
#endif
|
||||
vpadd.f32 d2, d2, d3
|
||||
vpadd.f32 d3, d6, d7
|
||||
vpadd.f32 d6, d2, d3
|
||||
vst1.64 {d6}, [r2]!
|
||||
subs r3, r3, #1
|
||||
bne .neon_conv_loop20
|
||||
vpop {q4-q7}
|
||||
pop {r4-r8, pc}
|
||||
.size neon_conv_real20, .-neon_conv_real20
|
||||
.align 2
|
||||
.global mac_cx_neon4
|
||||
.type mac_cx_neon4, %function
|
||||
mac_cx_neon4:
|
||||
push {r4, lr}
|
||||
ldr r4, =32
|
||||
veor q14, q14
|
||||
veor q15, q15
|
||||
.neon_conv_loop_mac4:
|
||||
vld2.32 {q0-q1}, [r0], r4
|
||||
vld2.32 {q2-q3}, [r1]!
|
||||
|
||||
vmul.f32 q10, q0, q2
|
||||
vmul.f32 q11, q1, q3
|
||||
vmul.f32 q12, q0, q3
|
||||
vmul.f32 q13, q2, q1
|
||||
vsub.f32 q8, q10, q11
|
||||
vadd.f32 q9, q12, q13
|
||||
|
||||
vadd.f32 q14, q8
|
||||
vadd.f32 q15, q9
|
||||
subs r3, #1
|
||||
bne .neon_conv_loop_mac4
|
||||
|
||||
vld1.64 d0, [r2]
|
||||
vpadd.f32 d28, d28, d29
|
||||
vpadd.f32 d30, d30, d31
|
||||
vpadd.f32 d1, d28, d30
|
||||
vadd.f32 d1, d0
|
||||
vst1.64 d1, [r2]
|
||||
pop {r4, pc}
|
||||
.size mac_cx_neon4, .-mac_cx_neon4
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
56
Transceiver52M/arch/arm/mult.c
Normal file
56
Transceiver52M/arch/arm/mult.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* NEON scaling
|
||||
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <mult.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
void neon_cmplx_mul_4n(float *, float *, float *, int);
|
||||
|
||||
static void cmplx_mul_ps(float *out, float *a, float *b, int len)
|
||||
{
|
||||
float ai, aq, bi, bq;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
ai = a[2 * i + 0];
|
||||
aq = a[2 * i + 1];
|
||||
|
||||
bi = b[2 * i + 0];
|
||||
bq = b[2 * i + 1];
|
||||
|
||||
out[2 * i + 0] = ai * bi - aq * bq;
|
||||
out[2 * i + 1] = ai * bq + aq * bi;
|
||||
}
|
||||
}
|
||||
|
||||
void mul_complex(float *out, float *a, float *b, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if (len % 4)
|
||||
cmplx_mul_ps(out, a, b, len);
|
||||
else
|
||||
neon_cmplx_mul_4n(out, a, b, len >> 2);
|
||||
#else
|
||||
cmplx_mul_ps(out, a, b, len);
|
||||
#endif
|
||||
}
|
||||
42
Transceiver52M/arch/arm/mult_neon.S
Normal file
42
Transceiver52M/arch/arm/mult_neon.S
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* NEON complex multiplication
|
||||
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.align 2
|
||||
.global neon_cmplx_mul_4n
|
||||
.type neon_cmplx_mul_4n, %function
|
||||
neon_cmplx_mul_4n:
|
||||
vpush {q4-q7}
|
||||
.loop_mul:
|
||||
vld2.32 {q0-q1}, [r1]!
|
||||
vld2.32 {q2-q3}, [r2]!
|
||||
vmul.f32 q4, q0, q2
|
||||
vmul.f32 q5, q1, q3
|
||||
vmul.f32 q6, q0, q3
|
||||
vmul.f32 q7, q2, q1
|
||||
vsub.f32 q8, q4, q5
|
||||
vadd.f32 q9, q6, q7
|
||||
vst2.32 {q8-q9}, [r0]!
|
||||
subs r3, #1
|
||||
bne .loop_mul
|
||||
vpop {q4-q7}
|
||||
bx lr
|
||||
.size neon_cmplx_mul_4n, .-neon_cmplx_mul_4n
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
56
Transceiver52M/arch/arm/scale.c
Normal file
56
Transceiver52M/arch/arm/scale.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* NEON scaling
|
||||
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <scale.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
void neon_scale_4n(float *, float *, float *, int);
|
||||
|
||||
static void scale_ps(float *out, float *in, float *scale, int len)
|
||||
{
|
||||
float ai, aq, bi, bq;
|
||||
|
||||
bi = scale[0];
|
||||
bq = scale[1];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
ai = in[2 * i + 0];
|
||||
aq = in[2 * i + 1];
|
||||
|
||||
out[2 * i + 0] = ai * bi - aq * bq;
|
||||
out[2 * i + 1] = ai * bq + aq * bi;
|
||||
}
|
||||
}
|
||||
|
||||
void scale_complex(float *out, float *in, float* scale, int len)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if (len % 4)
|
||||
scale_ps(out, in, scale, len);
|
||||
else
|
||||
neon_scale_4n(in, scale, out, len >> 2);
|
||||
#else
|
||||
scale_ps(out, in, scale, len);
|
||||
#endif
|
||||
}
|
||||
50
Transceiver52M/arch/arm/scale_neon.S
Normal file
50
Transceiver52M/arch/arm/scale_neon.S
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* ARM NEON Scaling
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.align 2
|
||||
.global neon_scale_4n
|
||||
.type neon_scale_4n, %function
|
||||
neon_scale_4n:
|
||||
push {r4, lr}
|
||||
ldr r4, =32
|
||||
|
||||
vld1.64 d0, [r1]
|
||||
vmov.32 s4, s1
|
||||
vmov.32 s1, s0
|
||||
vmov.64 d1, d0
|
||||
vmov.32 s5, s4
|
||||
vmov.64 d3, d2
|
||||
.loop_mul_const:
|
||||
vld2.32 {q2-q3}, [r0], r4
|
||||
|
||||
vmul.f32 q8, q0, q2
|
||||
vmul.f32 q9, q1, q3
|
||||
vmul.f32 q10, q0, q3
|
||||
vmul.f32 q11, q1, q2
|
||||
vsub.f32 q8, q8, q9
|
||||
vadd.f32 q9, q10, q11
|
||||
|
||||
vst2.32 {q8-q9}, [r2]!
|
||||
subs r3, #1
|
||||
bne .loop_mul_const
|
||||
pop {r4, pc}
|
||||
.size neon_scale_4n, .-neon_scale_4n
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
15
Transceiver52M/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
|
||||
15
Transceiver52M/arch/common/convert.h
Normal file
15
Transceiver52M/arch/common/convert.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _CONVERT_H_
|
||||
#define _CONVERT_H_
|
||||
|
||||
void convert_float_short(short *out, const float *in, float scale, int len);
|
||||
|
||||
void convert_short_float(float *out, const short *in, int len);
|
||||
|
||||
void base_convert_float_short(short *out, const float *in,
|
||||
float scale, int len);
|
||||
|
||||
void base_convert_short_float(float *out, const short *in, int len);
|
||||
|
||||
void convert_init(void);
|
||||
|
||||
#endif /* _CONVERT_H_ */
|
||||
34
Transceiver52M/arch/common/convert_base.c
Normal file
34
Transceiver52M/arch/common/convert_base.c
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Conversion
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "convert.h"
|
||||
|
||||
void base_convert_float_short(short *out, const float *in,
|
||||
float scale, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
out[i] = in[i] * scale;
|
||||
}
|
||||
|
||||
void base_convert_short_float(float *out, const short *in, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
out[i] = in[i];
|
||||
}
|
||||
|
||||
32
Transceiver52M/arch/common/convolve.h
Normal file
32
Transceiver52M/arch/common/convolve.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _CONVOLVE_H_
|
||||
#define _CONVOLVE_H_
|
||||
|
||||
void *convolve_h_alloc(int num);
|
||||
|
||||
int convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int base_convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int base_convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
void convolve_init(void);
|
||||
|
||||
#endif /* _CONVOLVE_H_ */
|
||||
156
Transceiver52M/arch/common/convolve_base.c
Normal file
156
Transceiver52M/arch/common/convolve_base.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Base multiply and accumulate complex-real */
|
||||
static void mac_real(const float *x, const float *h, float *y)
|
||||
{
|
||||
y[0] += x[0] * h[0];
|
||||
y[1] += x[1] * h[0];
|
||||
}
|
||||
|
||||
/* Base multiply and accumulate complex-complex */
|
||||
static void mac_cmplx(const float *x, const float *h, float *y)
|
||||
{
|
||||
y[0] += x[0] * h[0] - x[1] * h[1];
|
||||
y[1] += x[0] * h[1] + x[1] * h[0];
|
||||
}
|
||||
|
||||
/* Base vector complex-complex multiply and accumulate */
|
||||
static void mac_real_vec_n(const float *x, const float *h, float *y,
|
||||
int len, int step, int offset)
|
||||
{
|
||||
for (int i = offset; i < len; i += step)
|
||||
mac_real(&x[2 * i], &h[2 * i], y);
|
||||
}
|
||||
|
||||
/* Base vector complex-complex multiply and accumulate */
|
||||
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
|
||||
int len, int step, int offset)
|
||||
{
|
||||
for (int i = offset; i < len; i += step)
|
||||
mac_cmplx(&x[2 * i], &h[2 * i], y);
|
||||
}
|
||||
|
||||
/* Base complex-real convolution */
|
||||
int _base_convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
||||
h,
|
||||
&y[2 * i], h_len,
|
||||
step, offset);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Base complex-complex convolution */
|
||||
int _base_convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
||||
h,
|
||||
&y[2 * i],
|
||||
h_len, step, offset);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Buffer validity checks */
|
||||
int bounds_check(int x_len, int h_len, int y_len,
|
||||
int start, int len, int step)
|
||||
{
|
||||
if ((x_len < 1) || (h_len < 1) ||
|
||||
(y_len < 1) || (len < 1) || (step < 1)) {
|
||||
fprintf(stderr, "Convolve: Invalid input\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((start + len > x_len) || (len > y_len) || (x_len < h_len)) {
|
||||
fprintf(stderr, "Convolve: Boundary exception\n");
|
||||
fprintf(stderr, "start: %i, len: %i, x: %i, h: %i, y: %i\n",
|
||||
start, len, x_len, h_len, y_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API: Non-aligned (no SSE) complex-real */
|
||||
int base_convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
return _base_convolve_real(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
|
||||
/* API: Non-aligned (no SSE) complex-complex */
|
||||
int base_convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset)
|
||||
{
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
return _base_convolve_complex(x, x_len,
|
||||
h, h_len,
|
||||
y, y_len,
|
||||
start, len, step, offset);
|
||||
}
|
||||
|
||||
/* Aligned filter tap allocation */
|
||||
void *convolve_h_alloc(int len)
|
||||
{
|
||||
#ifdef HAVE_SSE3
|
||||
return memalign(16, len * 2 * sizeof(float));
|
||||
#else
|
||||
return malloc(len * 2 * sizeof(float));
|
||||
#endif
|
||||
}
|
||||
112
Transceiver52M/arch/common/fft.c
Normal file
112
Transceiver52M/arch/common/fft.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Fast Fourier transform
|
||||
*
|
||||
* Copyright (C) 2012 Tom Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <fftw3.h>
|
||||
|
||||
#include "fft.h"
|
||||
|
||||
struct fft_hdl {
|
||||
float *fft_in;
|
||||
float *fft_out;
|
||||
int len;
|
||||
fftwf_plan fft_plan;
|
||||
};
|
||||
|
||||
/*! \brief Initialize FFT backend
|
||||
* \param[in] reverse FFT direction
|
||||
* \param[in] m FFT length
|
||||
* \param[in] istride input stride count
|
||||
* \param[in] ostride output stride count
|
||||
* \param[in] in input buffer (FFTW aligned)
|
||||
* \param[in] out output buffer (FFTW aligned)
|
||||
* \param[in] ooffset initial offset into output buffer
|
||||
*
|
||||
* If the reverse is non-NULL, then an inverse FFT will be used. This is a
|
||||
* wrapper for advanced non-contiguous FFTW usage. See FFTW documentation for
|
||||
* further details.
|
||||
*
|
||||
* http://www.fftw.org/doc/Advanced-Complex-DFTs.html
|
||||
*
|
||||
* It is currently unknown how the offset of the output buffer affects FFTW
|
||||
* memory alignment.
|
||||
*/
|
||||
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
|
||||
float *in, float *out, int ooffset)
|
||||
{
|
||||
int rank = 1;
|
||||
int n[] = { m };
|
||||
int howmany = istride;
|
||||
int idist = 1;
|
||||
int odist = 1;
|
||||
int *inembed = n;
|
||||
int *onembed = n;
|
||||
fftwf_complex *obuffer, *ibuffer;
|
||||
|
||||
struct fft_hdl *hdl = (struct fft_hdl *) malloc(sizeof(struct fft_hdl));
|
||||
if (!hdl)
|
||||
return NULL;
|
||||
|
||||
int direction = FFTW_FORWARD;
|
||||
if (reverse)
|
||||
direction = FFTW_BACKWARD;
|
||||
|
||||
ibuffer = (fftwf_complex *) in;
|
||||
obuffer = (fftwf_complex *) out + ooffset;
|
||||
|
||||
hdl->fft_in = in;
|
||||
hdl->fft_out = out;
|
||||
hdl->fft_plan = fftwf_plan_many_dft(rank, n, howmany,
|
||||
ibuffer, inembed, istride, idist,
|
||||
obuffer, onembed, ostride, odist,
|
||||
direction, FFTW_MEASURE);
|
||||
return hdl;
|
||||
}
|
||||
|
||||
void *fft_malloc(size_t size)
|
||||
{
|
||||
return fftwf_malloc(size);
|
||||
}
|
||||
|
||||
void fft_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/*! \brief Free FFT backend resources
|
||||
*/
|
||||
void free_fft(struct fft_hdl *hdl)
|
||||
{
|
||||
fftwf_destroy_plan(hdl->fft_plan);
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
/*! \brief Run multiple DFT operations with the initialized plan
|
||||
* \param[in] hdl handle to an intitialized fft struct
|
||||
*
|
||||
* Input and output buffers are configured with init_fft().
|
||||
*/
|
||||
int cxvec_fft(struct fft_hdl *hdl)
|
||||
{
|
||||
fftwf_execute(hdl->fft_plan);
|
||||
return 0;
|
||||
}
|
||||
13
Transceiver52M/arch/common/fft.h
Normal file
13
Transceiver52M/arch/common/fft.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef _FFT_H_
|
||||
#define _FFT_H_
|
||||
|
||||
struct fft_hdl;
|
||||
|
||||
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
|
||||
float *in, float *out, int ooffset);
|
||||
void *fft_malloc(size_t size);
|
||||
void fft_free(void *ptr);
|
||||
void free_fft(struct fft_hdl *hdl);
|
||||
int cxvec_fft(struct fft_hdl *hdl);
|
||||
|
||||
#endif /* _FFT_H_ */
|
||||
6
Transceiver52M/arch/common/mult.h
Normal file
6
Transceiver52M/arch/common/mult.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _MULT_H_
|
||||
#define _MULT_H_
|
||||
|
||||
void mul_complex(float *out, float *a, float *b, int len);
|
||||
|
||||
#endif /* _MULT_H_ */
|
||||
6
Transceiver52M/arch/common/scale.h
Normal file
6
Transceiver52M/arch/common/scale.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _SCALE_H_
|
||||
#define _SCALE_H_
|
||||
|
||||
void scale_complex(float *out, float *in, float *scale, int len);
|
||||
|
||||
#endif /* _SCALE_H_ */
|
||||
33
Transceiver52M/arch/x86/Makefile.am
Normal file
33
Transceiver52M/arch/x86/Makefile.am
Normal file
@@ -0,0 +1,33 @@
|
||||
AM_CFLAGS = -Wall -std=gnu99 -I${srcdir}/../common
|
||||
|
||||
noinst_LTLIBRARIES = libarch.la
|
||||
noinst_LTLIBRARIES += libarch_sse_3.la
|
||||
noinst_LTLIBRARIES += libarch_sse_4_1.la
|
||||
|
||||
noinst_HEADERS = \
|
||||
convert_sse_3.h \
|
||||
convert_sse_4_1.h \
|
||||
convolve_sse_3.h
|
||||
|
||||
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
|
||||
|
||||
# SSE 3 specific code
|
||||
if HAVE_SSE3
|
||||
libarch_sse_3_la_SOURCES = \
|
||||
convert_sse_3.c \
|
||||
convolve_sse_3.c
|
||||
libarch_sse_3_la_CFLAGS = $(AM_CFLAGS) -msse3
|
||||
libarch_la_LIBADD += libarch_sse_3.la
|
||||
endif
|
||||
|
||||
# SSE 4.1 specific code
|
||||
if HAVE_SSE4_1
|
||||
libarch_sse_4_1_la_SOURCES = \
|
||||
convert_sse_4_1.c
|
||||
libarch_sse_4_1_la_CFLAGS = $(AM_CFLAGS) -msse4.1
|
||||
libarch_la_LIBADD += libarch_sse_4_1.la
|
||||
endif
|
||||
|
||||
libarch_la_SOURCES = \
|
||||
convert.c \
|
||||
convolve.c
|
||||
83
Transceiver52M/arch/x86/convert.c
Normal file
83
Transceiver52M/arch/x86/convert.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "convert.h"
|
||||
#include "convert_sse_3.h"
|
||||
#include "convert_sse_4_1.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Architecture dependant function pointers */
|
||||
struct convert_cpu_context {
|
||||
void (*convert_si16_ps_16n) (float *, const short *, int);
|
||||
void (*convert_si16_ps) (float *, const short *, int);
|
||||
void (*convert_scale_ps_si16_16n)(short *, const float *, float, int);
|
||||
void (*convert_scale_ps_si16_8n)(short *, const float *, float, int);
|
||||
void (*convert_scale_ps_si16)(short *, const float *, float, int);
|
||||
};
|
||||
|
||||
static struct convert_cpu_context c;
|
||||
|
||||
void convert_init(void)
|
||||
{
|
||||
c.convert_scale_ps_si16_16n = base_convert_float_short;
|
||||
c.convert_scale_ps_si16_8n = base_convert_float_short;
|
||||
c.convert_scale_ps_si16 = base_convert_float_short;
|
||||
c.convert_si16_ps_16n = base_convert_short_float;
|
||||
c.convert_si16_ps = base_convert_short_float;
|
||||
|
||||
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||
#ifdef HAVE_SSE4_1
|
||||
if (__builtin_cpu_supports("sse4.1")) {
|
||||
c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n;
|
||||
c.convert_si16_ps = &_sse_convert_si16_ps;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
if (__builtin_cpu_supports("sse3")) {
|
||||
c.convert_scale_ps_si16_16n = _sse_convert_scale_ps_si16_16n;
|
||||
c.convert_scale_ps_si16_8n = _sse_convert_scale_ps_si16_8n;
|
||||
c.convert_scale_ps_si16 = _sse_convert_scale_ps_si16;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void convert_float_short(short *out, const float *in, float scale, int len)
|
||||
{
|
||||
if (!(len % 16))
|
||||
c.convert_scale_ps_si16_16n(out, in, scale, len);
|
||||
else if (!(len % 8))
|
||||
c.convert_scale_ps_si16_8n(out, in, scale, len);
|
||||
else
|
||||
c.convert_scale_ps_si16(out, in, scale, len);
|
||||
}
|
||||
|
||||
void convert_short_float(float *out, const short *in, int len)
|
||||
{
|
||||
if (!(len % 16))
|
||||
c.convert_si16_ps_16n(out, in, len);
|
||||
else
|
||||
c.convert_si16_ps(out, in, len);
|
||||
}
|
||||
107
Transceiver52M/arch/x86/convert_sse_3.c
Normal file
107
Transceiver52M/arch/x86/convert_sse_3.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "convert_sse_3.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
void _sse_convert_scale_ps_si16_8n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len)
|
||||
{
|
||||
__m128 m0, m1, m2;
|
||||
__m128i m4, m5;
|
||||
|
||||
for (int i = 0; i < len / 8; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_ps(&in[8 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&in[8 * i + 4]);
|
||||
m2 = _mm_load1_ps(&scale);
|
||||
|
||||
/* Scale */
|
||||
m0 = _mm_mul_ps(m0, m2);
|
||||
m1 = _mm_mul_ps(m1, m2);
|
||||
|
||||
/* Convert */
|
||||
m4 = _mm_cvtps_epi32(m0);
|
||||
m5 = _mm_cvtps_epi32(m1);
|
||||
|
||||
/* Pack and store */
|
||||
m5 = _mm_packs_epi32(m4, m5);
|
||||
_mm_storeu_si128((__m128i *) & out[8 * i], m5);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8*N single precision floats scaled and converted with remainder */
|
||||
void _sse_convert_scale_ps_si16(short *restrict out,
|
||||
const float *restrict in, float scale, int len)
|
||||
{
|
||||
int start = len / 8 * 8;
|
||||
|
||||
_sse_convert_scale_ps_si16_8n(out, in, scale, len);
|
||||
|
||||
for (int i = 0; i < len % 8; i++)
|
||||
out[start + i] = in[start + i] * scale;
|
||||
}
|
||||
|
||||
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
void _sse_convert_scale_ps_si16_16n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len)
|
||||
{
|
||||
__m128 m0, m1, m2, m3, m4;
|
||||
__m128i m5, m6, m7, m8;
|
||||
|
||||
for (int i = 0; i < len / 16; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_ps(&in[16 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&in[16 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&in[16 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&in[16 * i + 12]);
|
||||
m4 = _mm_load1_ps(&scale);
|
||||
|
||||
/* Scale */
|
||||
m0 = _mm_mul_ps(m0, m4);
|
||||
m1 = _mm_mul_ps(m1, m4);
|
||||
m2 = _mm_mul_ps(m2, m4);
|
||||
m3 = _mm_mul_ps(m3, m4);
|
||||
|
||||
/* Convert */
|
||||
m5 = _mm_cvtps_epi32(m0);
|
||||
m6 = _mm_cvtps_epi32(m1);
|
||||
m7 = _mm_cvtps_epi32(m2);
|
||||
m8 = _mm_cvtps_epi32(m3);
|
||||
|
||||
/* Pack and store */
|
||||
m5 = _mm_packs_epi32(m5, m6);
|
||||
m7 = _mm_packs_epi32(m7, m8);
|
||||
_mm_storeu_si128((__m128i *) & out[16 * i + 0], m5);
|
||||
_mm_storeu_si128((__m128i *) & out[16 * i + 8], m7);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
34
Transceiver52M/arch/x86/convert_sse_3.h
Normal file
34
Transceiver52M/arch/x86/convert_sse_3.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
void _sse_convert_scale_ps_si16_8n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len);
|
||||
|
||||
/* 8*N single precision floats scaled and converted with remainder */
|
||||
void _sse_convert_scale_ps_si16(short *restrict out,
|
||||
const float *restrict in, float scale, int len);
|
||||
|
||||
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
|
||||
void _sse_convert_scale_ps_si16_16n(short *restrict out,
|
||||
const float *restrict in,
|
||||
float scale, int len);
|
||||
77
Transceiver52M/arch/x86/convert_sse_4_1.c
Normal file
77
Transceiver52M/arch/x86/convert_sse_4_1.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "convert_sse_4_1.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE4_1
|
||||
#include <smmintrin.h>
|
||||
|
||||
/* 16*N 16-bit signed integer converted to single precision floats */
|
||||
void _sse_convert_si16_ps_16n(float *restrict out,
|
||||
const short *restrict in, int len)
|
||||
{
|
||||
__m128i m0, m1, m2, m3, m4, m5;
|
||||
__m128 m6, m7, m8, m9;
|
||||
|
||||
for (int i = 0; i < len / 16; i++) {
|
||||
/* Load (unaligned) packed floats */
|
||||
m0 = _mm_loadu_si128((__m128i *) & in[16 * i + 0]);
|
||||
m1 = _mm_loadu_si128((__m128i *) & in[16 * i + 8]);
|
||||
|
||||
/* Unpack */
|
||||
m2 = _mm_cvtepi16_epi32(m0);
|
||||
m4 = _mm_cvtepi16_epi32(m1);
|
||||
m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
m3 = _mm_cvtepi16_epi32(m0);
|
||||
m5 = _mm_cvtepi16_epi32(m1);
|
||||
|
||||
/* Convert */
|
||||
m6 = _mm_cvtepi32_ps(m2);
|
||||
m7 = _mm_cvtepi32_ps(m3);
|
||||
m8 = _mm_cvtepi32_ps(m4);
|
||||
m9 = _mm_cvtepi32_ps(m5);
|
||||
|
||||
/* Store */
|
||||
_mm_storeu_ps(&out[16 * i + 0], m6);
|
||||
_mm_storeu_ps(&out[16 * i + 4], m7);
|
||||
_mm_storeu_ps(&out[16 * i + 8], m8);
|
||||
_mm_storeu_ps(&out[16 * i + 12], m9);
|
||||
}
|
||||
}
|
||||
|
||||
/* 16*N 16-bit signed integer conversion with remainder */
|
||||
void _sse_convert_si16_ps(float *restrict out,
|
||||
const short *restrict in, int len)
|
||||
{
|
||||
int start = len / 16 * 16;
|
||||
|
||||
_sse_convert_si16_ps_16n(out, in, len);
|
||||
|
||||
for (int i = 0; i < len % 16; i++)
|
||||
out[start + i] = in[start + i];
|
||||
}
|
||||
|
||||
#endif
|
||||
28
Transceiver52M/arch/x86/convert_sse_4_1.h
Normal file
28
Transceiver52M/arch/x86/convert_sse_4_1.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SSE type conversions
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* 16*N 16-bit signed integer converted to single precision floats */
|
||||
void _sse_convert_si16_ps_16n(float *restrict out,
|
||||
const short *restrict in, int len);
|
||||
|
||||
/* 16*N 16-bit signed integer conversion with remainder */
|
||||
void _sse_convert_si16_ps(float *restrict out,
|
||||
const short *restrict in, int len);
|
||||
172
Transceiver52M/arch/x86/convolve.c
Normal file
172
Transceiver52M/arch/x86/convolve.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* SSE Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "convolve.h"
|
||||
#include "convolve_sse_3.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Architecture dependant function pointers */
|
||||
struct convolve_cpu_context {
|
||||
void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_cmplx_8n) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_cmplx) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real4) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real8) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real12) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real16) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real20) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real4n) (const float *, int, const float *, int, float *,
|
||||
int, int, int, int, int);
|
||||
void (*conv_real) (const float *, int, const float *, int, float *, int,
|
||||
int, int, int, int);
|
||||
};
|
||||
static struct convolve_cpu_context c;
|
||||
|
||||
/* Forward declarations from base implementation */
|
||||
int _base_convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int _base_convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len,
|
||||
int step, int offset);
|
||||
|
||||
int bounds_check(int x_len, int h_len, int y_len,
|
||||
int start, int len, int step);
|
||||
|
||||
/* API: Initalize convolve module */
|
||||
void convolve_init(void)
|
||||
{
|
||||
c.conv_cmplx_4n = (void *)_base_convolve_complex;
|
||||
c.conv_cmplx_8n = (void *)_base_convolve_complex;
|
||||
c.conv_cmplx = (void *)_base_convolve_complex;
|
||||
c.conv_real4 = (void *)_base_convolve_real;
|
||||
c.conv_real8 = (void *)_base_convolve_real;
|
||||
c.conv_real12 = (void *)_base_convolve_real;
|
||||
c.conv_real16 = (void *)_base_convolve_real;
|
||||
c.conv_real20 = (void *)_base_convolve_real;
|
||||
c.conv_real4n = (void *)_base_convolve_real;
|
||||
c.conv_real = (void *)_base_convolve_real;
|
||||
|
||||
#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
|
||||
if (__builtin_cpu_supports("sse3")) {
|
||||
c.conv_cmplx_4n = sse_conv_cmplx_4n;
|
||||
c.conv_cmplx_8n = sse_conv_cmplx_8n;
|
||||
c.conv_real4 = sse_conv_real4;
|
||||
c.conv_real8 = sse_conv_real8;
|
||||
c.conv_real12 = sse_conv_real12;
|
||||
c.conv_real16 = sse_conv_real16;
|
||||
c.conv_real20 = sse_conv_real20;
|
||||
c.conv_real4n = sse_conv_real4n;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* API: Aligned complex-real */
|
||||
int convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len, int start, int len, int step, int offset)
|
||||
{
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
if (step <= 4) {
|
||||
switch (h_len) {
|
||||
case 4:
|
||||
c.conv_real4(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
case 8:
|
||||
c.conv_real8(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
case 12:
|
||||
c.conv_real12(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
case 16:
|
||||
c.conv_real16(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
case 20:
|
||||
c.conv_real20(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
break;
|
||||
default:
|
||||
if (!(h_len % 4))
|
||||
c.conv_real4n(x, x_len, h, h_len, y, y_len,
|
||||
start, len, step, offset);
|
||||
else
|
||||
c.conv_real(x, x_len, h, h_len, y, y_len, start,
|
||||
len, step, offset);
|
||||
}
|
||||
} else
|
||||
c.conv_real(x, x_len, h, h_len, y, y_len, start, len, step,
|
||||
offset);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* API: Aligned complex-complex */
|
||||
int convolve_complex(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
||||
return -1;
|
||||
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
if (step <= 4) {
|
||||
if (!(h_len % 8))
|
||||
c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start,
|
||||
len, step, offset);
|
||||
else if (!(h_len % 4))
|
||||
c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start,
|
||||
len, step, offset);
|
||||
else
|
||||
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len,
|
||||
step, offset);
|
||||
} else
|
||||
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len, step,
|
||||
offset);
|
||||
|
||||
return len;
|
||||
}
|
||||
542
Transceiver52M/arch/x86/convolve_sse_3.c
Normal file
542
Transceiver52M/arch/x86/convolve_sse_3.c
Normal file
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* SSE Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "convolve_sse_3.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
#include <xmmintrin.h>
|
||||
#include <pmmintrin.h>
|
||||
|
||||
/* 4-tap SSE complex-real convolution */
|
||||
void sse_conv_real4(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
/* NOTE: The parameter list of this function has to match the parameter
|
||||
* list of _base_convolve_real() in convolve_base.c. This specific
|
||||
* implementation, ignores some of the parameters of
|
||||
* _base_convolve_complex(), which are: x_len, y_len, offset, step */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m4 = _mm_mul_ps(m2, m7);
|
||||
m5 = _mm_mul_ps(m3, m7);
|
||||
|
||||
/* Sum and store */
|
||||
m6 = _mm_hadd_ps(m4, m5);
|
||||
m0 = _mm_hadd_ps(m6, m6);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8-tap SSE complex-real convolution */
|
||||
void sse_conv_real8(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||
|
||||
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m6 = _mm_mul_ps(m6, m4);
|
||||
m7 = _mm_mul_ps(m7, m4);
|
||||
m8 = _mm_mul_ps(m8, m5);
|
||||
m9 = _mm_mul_ps(m9, m5);
|
||||
|
||||
/* Sum and store */
|
||||
m6 = _mm_add_ps(m6, m8);
|
||||
m7 = _mm_add_ps(m7, m9);
|
||||
m6 = _mm_hadd_ps(m6, m7);
|
||||
m6 = _mm_hadd_ps(m6, m6);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m6);
|
||||
m6 = _mm_shuffle_ps(m6, m6, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m6);
|
||||
}
|
||||
}
|
||||
|
||||
/* 12-tap SSE complex-real convolution */
|
||||
void sse_conv_real12(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
|
||||
m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m12);
|
||||
m1 = _mm_mul_ps(m5, m12);
|
||||
m2 = _mm_mul_ps(m6, m13);
|
||||
m3 = _mm_mul_ps(m7, m13);
|
||||
m4 = _mm_mul_ps(m8, m14);
|
||||
m5 = _mm_mul_ps(m9, m14);
|
||||
|
||||
/* Sum and store */
|
||||
m8 = _mm_add_ps(m0, m2);
|
||||
m9 = _mm_add_ps(m1, m3);
|
||||
m10 = _mm_add_ps(m8, m4);
|
||||
m11 = _mm_add_ps(m9, m5);
|
||||
|
||||
m2 = _mm_hadd_ps(m10, m11);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 16-tap SSE complex-real convolution */
|
||||
void sse_conv_real16(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
m6 = _mm_load_ps(&h[24]);
|
||||
m7 = _mm_load_ps(&h[28]);
|
||||
|
||||
m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m15 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 24]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 28]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m12);
|
||||
m1 = _mm_mul_ps(m5, m12);
|
||||
m2 = _mm_mul_ps(m6, m13);
|
||||
m3 = _mm_mul_ps(m7, m13);
|
||||
|
||||
m4 = _mm_mul_ps(m8, m14);
|
||||
m5 = _mm_mul_ps(m9, m14);
|
||||
m6 = _mm_mul_ps(m10, m15);
|
||||
m7 = _mm_mul_ps(m11, m15);
|
||||
|
||||
/* Sum and store */
|
||||
m8 = _mm_add_ps(m0, m2);
|
||||
m9 = _mm_add_ps(m1, m3);
|
||||
m10 = _mm_add_ps(m4, m6);
|
||||
m11 = _mm_add_ps(m5, m7);
|
||||
|
||||
m0 = _mm_add_ps(m8, m10);
|
||||
m1 = _mm_add_ps(m9, m11);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 20-tap SSE complex-real convolution */
|
||||
void sse_conv_real20(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m11, m12, m13, m14, m15;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[0]);
|
||||
m1 = _mm_load_ps(&h[4]);
|
||||
m2 = _mm_load_ps(&h[8]);
|
||||
m3 = _mm_load_ps(&h[12]);
|
||||
m4 = _mm_load_ps(&h[16]);
|
||||
m5 = _mm_load_ps(&h[20]);
|
||||
m6 = _mm_load_ps(&h[24]);
|
||||
m7 = _mm_load_ps(&h[28]);
|
||||
m8 = _mm_load_ps(&h[32]);
|
||||
m9 = _mm_load_ps(&h[36]);
|
||||
|
||||
m11 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m12 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m13 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m14 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m15 = _mm_shuffle_ps(m8, m9, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Multiply-accumulate first 12 taps */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||
m4 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||
m5 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||
|
||||
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m2 = _mm_mul_ps(m6, m11);
|
||||
m3 = _mm_mul_ps(m7, m11);
|
||||
m4 = _mm_mul_ps(m8, m12);
|
||||
m5 = _mm_mul_ps(m9, m12);
|
||||
m6 = _mm_mul_ps(m0, m13);
|
||||
m7 = _mm_mul_ps(m1, m13);
|
||||
|
||||
m0 = _mm_add_ps(m2, m4);
|
||||
m1 = _mm_add_ps(m3, m5);
|
||||
m8 = _mm_add_ps(m0, m6);
|
||||
m9 = _mm_add_ps(m1, m7);
|
||||
|
||||
/* Multiply-accumulate last 8 taps */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 24]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 28]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 32]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 36]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
m0 = _mm_mul_ps(m4, m14);
|
||||
m1 = _mm_mul_ps(m5, m14);
|
||||
m2 = _mm_mul_ps(m6, m15);
|
||||
m3 = _mm_mul_ps(m7, m15);
|
||||
|
||||
m4 = _mm_add_ps(m0, m2);
|
||||
m5 = _mm_add_ps(m1, m3);
|
||||
|
||||
/* Final sum and store */
|
||||
m0 = _mm_add_ps(m8, m4);
|
||||
m1 = _mm_add_ps(m9, m5);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m3 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m3);
|
||||
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m3);
|
||||
}
|
||||
}
|
||||
|
||||
/* 4*N-tap SSE complex-real convolution */
|
||||
void sse_conv_real4n(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
/* See NOTE in sse_conv_real4() */
|
||||
|
||||
__m128 m0, m1, m2, m4, m5, m6, m7;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m6 = _mm_setzero_ps();
|
||||
m7 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 4; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[8 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[8 * n + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]);
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m2, m4);
|
||||
m1 = _mm_mul_ps(m2, m5);
|
||||
|
||||
/* Accumulate */
|
||||
m6 = _mm_add_ps(m6, m0);
|
||||
m7 = _mm_add_ps(m7, m1);
|
||||
}
|
||||
|
||||
m0 = _mm_hadd_ps(m6, m7);
|
||||
m0 = _mm_hadd_ps(m0, m0);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 4*N-tap SSE complex-complex convolution */
|
||||
void sse_conv_cmplx_4n(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
/* NOTE: The parameter list of this function has to match the parameter
|
||||
* list of _base_convolve_complex() in convolve_base.c. This specific
|
||||
* implementation, ignores some of the parameters of
|
||||
* _base_convolve_complex(), which are: x_len, y_len, offset, step. */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m6 = _mm_setzero_ps();
|
||||
m7 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 4; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[8 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[8 * n + 4]);
|
||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]);
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m2, m4);
|
||||
m1 = _mm_mul_ps(m3, m5);
|
||||
|
||||
m2 = _mm_mul_ps(m2, m5);
|
||||
m3 = _mm_mul_ps(m3, m4);
|
||||
|
||||
/* Sum */
|
||||
m0 = _mm_sub_ps(m0, m1);
|
||||
m2 = _mm_add_ps(m2, m3);
|
||||
|
||||
/* Accumulate */
|
||||
m6 = _mm_add_ps(m6, m0);
|
||||
m7 = _mm_add_ps(m7, m2);
|
||||
}
|
||||
|
||||
m0 = _mm_hadd_ps(m6, m7);
|
||||
m0 = _mm_hadd_ps(m0, m0);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m0);
|
||||
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8*N-tap SSE complex-complex convolution */
|
||||
void sse_conv_cmplx_8n(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset)
|
||||
{
|
||||
/* See NOTE in sse_conv_cmplx_4n() */
|
||||
|
||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
||||
|
||||
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
/* Zero */
|
||||
m12 = _mm_setzero_ps();
|
||||
m13 = _mm_setzero_ps();
|
||||
m14 = _mm_setzero_ps();
|
||||
m15 = _mm_setzero_ps();
|
||||
|
||||
for (int n = 0; n < h_len / 8; n++) {
|
||||
/* Load (aligned) filter taps */
|
||||
m0 = _mm_load_ps(&h[16 * n + 0]);
|
||||
m1 = _mm_load_ps(&h[16 * n + 4]);
|
||||
m2 = _mm_load_ps(&h[16 * n + 8]);
|
||||
m3 = _mm_load_ps(&h[16 * n + 12]);
|
||||
|
||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Load (unaligned) input data */
|
||||
m0 = _mm_loadu_ps(&_x[2 * i + 16 * n + 0]);
|
||||
m1 = _mm_loadu_ps(&_x[2 * i + 16 * n + 4]);
|
||||
m2 = _mm_loadu_ps(&_x[2 * i + 16 * n + 8]);
|
||||
m3 = _mm_loadu_ps(&_x[2 * i + 16 * n + 12]);
|
||||
|
||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||
|
||||
/* Quad multiply */
|
||||
m0 = _mm_mul_ps(m4, m8);
|
||||
m1 = _mm_mul_ps(m5, m9);
|
||||
m2 = _mm_mul_ps(m6, m10);
|
||||
m3 = _mm_mul_ps(m7, m11);
|
||||
|
||||
m4 = _mm_mul_ps(m4, m9);
|
||||
m5 = _mm_mul_ps(m5, m8);
|
||||
m6 = _mm_mul_ps(m6, m11);
|
||||
m7 = _mm_mul_ps(m7, m10);
|
||||
|
||||
/* Sum */
|
||||
m0 = _mm_sub_ps(m0, m1);
|
||||
m2 = _mm_sub_ps(m2, m3);
|
||||
m4 = _mm_add_ps(m4, m5);
|
||||
m6 = _mm_add_ps(m6, m7);
|
||||
|
||||
/* Accumulate */
|
||||
m12 = _mm_add_ps(m12, m0);
|
||||
m13 = _mm_add_ps(m13, m2);
|
||||
m14 = _mm_add_ps(m14, m4);
|
||||
m15 = _mm_add_ps(m15, m6);
|
||||
}
|
||||
|
||||
m0 = _mm_add_ps(m12, m13);
|
||||
m1 = _mm_add_ps(m14, m15);
|
||||
m2 = _mm_hadd_ps(m0, m1);
|
||||
m2 = _mm_hadd_ps(m2, m2);
|
||||
|
||||
_mm_store_ss(&y[2 * i + 0], m2);
|
||||
m2 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
_mm_store_ss(&y[2 * i + 1], m2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
68
Transceiver52M/arch/x86/convolve_sse_3.h
Normal file
68
Transceiver52M/arch/x86/convolve_sse_3.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SSE Convolution
|
||||
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* 4-tap SSE complex-real convolution */
|
||||
void sse_conv_real4(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset);
|
||||
|
||||
/* 8-tap SSE complex-real convolution */
|
||||
void sse_conv_real8(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset);
|
||||
|
||||
/* 12-tap SSE complex-real convolution */
|
||||
void sse_conv_real12(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset);
|
||||
|
||||
/* 16-tap SSE complex-real convolution */
|
||||
void sse_conv_real16(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset);
|
||||
|
||||
/* 20-tap SSE complex-real convolution */
|
||||
void sse_conv_real20(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset);
|
||||
|
||||
/* 4*N-tap SSE complex-real convolution */
|
||||
void sse_conv_real4n(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset);
|
||||
|
||||
/* 4*N-tap SSE complex-complex convolution */
|
||||
void sse_conv_cmplx_4n(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset);
|
||||
|
||||
/* 8*N-tap SSE complex-complex convolution */
|
||||
void sse_conv_cmplx_8n(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len,
|
||||
int start, int len, int step, int offset);
|
||||
13
Transceiver52M/device/Makefile.am
Normal file
13
Transceiver52M/device/Makefile.am
Normal file
@@ -0,0 +1,13 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
noinst_HEADERS = radioDevice.h
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
if DEVICE_USRP1
|
||||
SUBDIRS += usrp1
|
||||
endif
|
||||
|
||||
if DEVICE_UHD
|
||||
SUBDIRS += uhd
|
||||
endif
|
||||
@@ -16,11 +16,21 @@
|
||||
#define __RADIO_DEVICE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GSMCommon.h"
|
||||
|
||||
extern "C" {
|
||||
#include "config_defs.h"
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#define GSMRATE (1625e3/6)
|
||||
#define MCBTS_SPACING 800000.0
|
||||
|
||||
/** a 64-bit virtual timestamp for radio data */
|
||||
typedef unsigned long long TIMESTAMP;
|
||||
|
||||
@@ -29,12 +39,25 @@ class RadioDevice {
|
||||
|
||||
public:
|
||||
/* Available transport bus types */
|
||||
enum busType { USB, NET };
|
||||
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
|
||||
|
||||
static RadioDevice *make(double desiredSampleRate, bool skipRx = false);
|
||||
/* Radio interface types */
|
||||
enum InterfaceType {
|
||||
NORMAL,
|
||||
RESAMP_64M,
|
||||
RESAMP_100M,
|
||||
MULTI_ARFCN,
|
||||
};
|
||||
|
||||
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||
size_t chans = 1, double offset = 0.0,
|
||||
const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
|
||||
const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
|
||||
|
||||
/** Initialize the USRP */
|
||||
virtual bool open(const std::string &args)=0;
|
||||
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
|
||||
|
||||
virtual ~RadioDevice() { }
|
||||
|
||||
/** Start the USRP */
|
||||
virtual bool start()=0;
|
||||
@@ -42,11 +65,11 @@ class RadioDevice {
|
||||
/** Stop the USRP */
|
||||
virtual bool stop()=0;
|
||||
|
||||
/** Get the bus type */
|
||||
virtual enum busType getBus()=0;
|
||||
/** Get the Tx window type */
|
||||
virtual enum TxWindowType getWindowType()=0;
|
||||
|
||||
/** Enable thread priority */
|
||||
virtual void setPriority()=0;
|
||||
virtual void setPriority(float prio = 0.5) = 0;
|
||||
|
||||
/**
|
||||
Read samples from the radio.
|
||||
@@ -58,10 +81,9 @@ class RadioDevice {
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
virtual int readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool *underrun = 0,
|
||||
unsigned *RSSI = 0)=0;
|
||||
virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
|
||||
unsigned *RSSI = 0) = 0;
|
||||
/**
|
||||
Write samples to the radio.
|
||||
@param buf Contains the data to be written.
|
||||
@@ -71,18 +93,17 @@ class RadioDevice {
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
virtual int writeSamples(short *buf, int len, bool *underrun,
|
||||
TIMESTAMP timestamp,
|
||||
bool isControl=false)=0;
|
||||
|
||||
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp, bool isControl = false) = 0;
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
virtual bool updateAlignment(TIMESTAMP timestamp)=0;
|
||||
|
||||
|
||||
/** Set the transmitter frequency */
|
||||
virtual bool setTxFreq(double wFreq)=0;
|
||||
virtual bool setTxFreq(double wFreq, size_t chan = 0) = 0;
|
||||
|
||||
/** Set the receiver frequency */
|
||||
virtual bool setRxFreq(double wFreq)=0;
|
||||
virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
virtual TIMESTAMP initialWriteTimestamp(void)=0;
|
||||
@@ -97,10 +118,10 @@ class RadioDevice {
|
||||
virtual double fullScaleOutputValue()=0;
|
||||
|
||||
/** sets the receive chan gain, returns the gain setting **/
|
||||
virtual double setRxGain(double dB)=0;
|
||||
virtual double setRxGain(double dB, size_t chan = 0) = 0;
|
||||
|
||||
/** gets the current receive gain **/
|
||||
virtual double getRxGain(void)=0;
|
||||
virtual double getRxGain(size_t chan = 0) = 0;
|
||||
|
||||
/** return maximum Rx Gain **/
|
||||
virtual double maxRxGain(void) = 0;
|
||||
@@ -109,7 +130,7 @@ class RadioDevice {
|
||||
virtual double minRxGain(void) = 0;
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
virtual double setTxGain(double dB)=0;
|
||||
virtual double setTxGain(double dB, size_t chan = 0) = 0;
|
||||
|
||||
/** return maximum Tx Gain **/
|
||||
virtual double maxTxGain(void) = 0;
|
||||
@@ -117,9 +138,27 @@ 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()=0;
|
||||
virtual double getRxFreq()=0;
|
||||
virtual double getTxFreq(size_t chan = 0) = 0;
|
||||
virtual double getRxFreq(size_t chan = 0) = 0;
|
||||
virtual double getSampleRate()=0;
|
||||
virtual double numberRead()=0;
|
||||
virtual double numberWritten()=0;
|
||||
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
|
||||
1593
Transceiver52M/device/uhd/UHDDevice.cpp
Normal file
1593
Transceiver52M/device/uhd/UHDDevice.cpp
Normal file
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,15 +58,29 @@ const dboardConfigType dboardConfig = TXA_RXB;
|
||||
|
||||
const double USRPDevice::masterClockRate = 52.0e6;
|
||||
|
||||
USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx)
|
||||
: skipRx(skipRx)
|
||||
USRPDevice::USRPDevice(size_t sps)
|
||||
{
|
||||
LOG(INFO) << "creating USRP device...";
|
||||
decimRate = (unsigned int) round(masterClockRate/_desiredSampleRate);
|
||||
|
||||
this->sps = sps;
|
||||
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
|
||||
actualSampleRate = masterClockRate/decimRate;
|
||||
rxGain = 0;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
/*
|
||||
* Undetermined delay b/w ping response timestamp and true
|
||||
* receive timestamp. Values are empirically measured. With
|
||||
* split sample rate Tx/Rx - 4/1 sps we need to need to
|
||||
* compensate for advance rather than delay.
|
||||
*/
|
||||
if (sps == 1)
|
||||
pingOffset = 272;
|
||||
else if (sps == 4)
|
||||
pingOffset = 269 - 7500;
|
||||
else
|
||||
pingOffset = 0;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
samplePeriod = 1.0e6/actualSampleRate;
|
||||
loopbackBufferSize = 0;
|
||||
gettimeofday(&lastReadTime,NULL);
|
||||
@@ -75,29 +88,28 @@ USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool USRPDevice::open(const std::string &)
|
||||
int USRPDevice::open(const std::string &, int, bool)
|
||||
{
|
||||
writeLock.unlock();
|
||||
|
||||
LOG(INFO) << "opening USRP device..";
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
string rbf = "std_inband.rbf";
|
||||
//string rbf = "inband_1rxhb_1tx.rbf";
|
||||
//string rbf = "inband_1rxhb_1tx.rbf";
|
||||
m_uRx.reset();
|
||||
if (!skipRx) {
|
||||
try {
|
||||
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(0,decimRate,1,-1,
|
||||
usrp_standard_rx::FPGA_MODE_NORMAL,
|
||||
1024,16*8,rbf));
|
||||
#ifdef HAVE_LIBUSRP_3_2
|
||||
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
|
||||
0, decimRate * sps, 1, -1,
|
||||
usrp_standard_rx::FPGA_MODE_NORMAL,
|
||||
1024, 16 * 8, rbf));
|
||||
m_uRx->set_fpga_master_clock_freq(masterClockRate);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
catch(...) {
|
||||
LOG(ALERT) << "make failed on Rx";
|
||||
m_uRx.reset();
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (m_uRx->fpga_master_clock_freq() != masterClockRate)
|
||||
@@ -105,22 +117,21 @@ bool USRPDevice::open(const std::string &)
|
||||
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
|
||||
<< ", desired clock freq = " << masterClockRate;
|
||||
m_uRx.reset();
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(0,decimRate*2,1,-1,
|
||||
1024,16*8,rbf));
|
||||
#ifdef HAVE_LIBUSRP_3_2
|
||||
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
|
||||
0, decimRate * 2, 1, -1,
|
||||
1024, 16 * 8, rbf));
|
||||
m_uTx->set_fpga_master_clock_freq(masterClockRate);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
catch(...) {
|
||||
LOG(ALERT) << "make failed on Tx";
|
||||
m_uTx.reset();
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (m_uTx->fpga_master_clock_freq() != masterClockRate)
|
||||
@@ -128,12 +139,12 @@ bool USRPDevice::open(const std::string &)
|
||||
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
|
||||
<< ", desired clock freq = " << masterClockRate;
|
||||
m_uTx.reset();
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!skipRx) m_uRx->stop();
|
||||
m_uTx->stop();
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
switch (dboardConfig) {
|
||||
@@ -164,19 +175,19 @@ bool USRPDevice::open(const std::string &)
|
||||
samplesRead = 0;
|
||||
samplesWritten = 0;
|
||||
started = false;
|
||||
|
||||
return true;
|
||||
|
||||
return NORMAL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool USRPDevice::start()
|
||||
bool USRPDevice::start()
|
||||
{
|
||||
LOG(INFO) << "starting USRP...";
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx && !skipRx) return false;
|
||||
if (!m_uTx) return false;
|
||||
|
||||
|
||||
if (!skipRx) m_uRx->stop();
|
||||
m_uTx->stop();
|
||||
|
||||
@@ -206,8 +217,8 @@ bool USRPDevice::start()
|
||||
hi32Timestamp = 0;
|
||||
isAligned = false;
|
||||
|
||||
|
||||
if (!skipRx)
|
||||
|
||||
if (!skipRx)
|
||||
started = (m_uRx->start() && m_uTx->start());
|
||||
else
|
||||
started = m_uTx->start();
|
||||
@@ -218,14 +229,14 @@ bool USRPDevice::start()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool USRPDevice::stop()
|
||||
bool USRPDevice::stop()
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx) return false;
|
||||
if (!m_uTx) return false;
|
||||
|
||||
|
||||
delete[] currData;
|
||||
|
||||
|
||||
started = !(m_uRx->stop() && m_uTx->stop());
|
||||
return !started;
|
||||
#else
|
||||
@@ -246,67 +257,132 @@ double USRPDevice::minTxGain()
|
||||
double USRPDevice::maxRxGain()
|
||||
{
|
||||
return m_dbRx->gain_max();
|
||||
}
|
||||
}
|
||||
|
||||
double USRPDevice::minRxGain()
|
||||
{
|
||||
return m_dbRx->gain_min();
|
||||
}
|
||||
|
||||
double USRPDevice::setTxGain(double dB) {
|
||||
|
||||
writeLock.lock();
|
||||
if (dB > maxTxGain()) dB = maxTxGain();
|
||||
if (dB < minTxGain()) dB = minTxGain();
|
||||
double USRPDevice::setTxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||
writeLock.lock();
|
||||
if (dB > maxTxGain())
|
||||
dB = maxTxGain();
|
||||
if (dB < minTxGain())
|
||||
dB = minTxGain();
|
||||
|
||||
if (!m_dbTx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting TX gain";
|
||||
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
return dB;
|
||||
if (!m_dbTx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting TX gain";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
return dB;
|
||||
}
|
||||
|
||||
|
||||
double USRPDevice::setRxGain(double dB) {
|
||||
double USRPDevice::setRxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
writeLock.lock();
|
||||
if (dB > maxRxGain()) dB = maxRxGain();
|
||||
if (dB < minRxGain()) dB = minRxGain();
|
||||
|
||||
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
|
||||
dB = 47.0;
|
||||
|
||||
if (!m_dbRx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting RX gain";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
return dB;
|
||||
writeLock.lock();
|
||||
if (dB > maxRxGain())
|
||||
dB = maxRxGain();
|
||||
if (dB < minRxGain())
|
||||
dB = minRxGain();
|
||||
|
||||
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
|
||||
|
||||
if (!m_dbRx->set_gain(dB))
|
||||
LOG(ERR) << "Error setting RX gain";
|
||||
|
||||
writeLock.unlock();
|
||||
|
||||
return dB;
|
||||
}
|
||||
|
||||
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
|
||||
{
|
||||
if (chan >= rx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
LOG(ALERT) << "Not implemented";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string USRPDevice::getRxAntenna(size_t chan)
|
||||
{
|
||||
if (chan >= rx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return "";
|
||||
}
|
||||
LOG(ALERT) << "Not implemented";
|
||||
return "";
|
||||
}
|
||||
|
||||
bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
|
||||
{
|
||||
if (chan >= tx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
LOG(ALERT) << "Not implemented";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string USRPDevice::getTxAntenna(size_t chan)
|
||||
{
|
||||
if (chan >= tx_paths.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return "";
|
||||
}
|
||||
LOG(ALERT) << "Not implemented";
|
||||
return "";
|
||||
}
|
||||
|
||||
bool USRPDevice::requiresRadioAlign()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GSM::Time USRPDevice::minLatency() {
|
||||
return GSM::Time(1,1);
|
||||
}
|
||||
|
||||
// NOTE: Assumes sequential reads
|
||||
int USRPDevice::readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp,
|
||||
bool *underrun,
|
||||
unsigned *RSSI)
|
||||
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx) return 0;
|
||||
|
||||
#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;
|
||||
@@ -316,7 +392,7 @@ int USRPDevice::readSamples(short *buf, 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++) {
|
||||
@@ -341,7 +417,7 @@ int USRPDevice::readSamples(short *buf, int len, bool *overrun,
|
||||
uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
|
||||
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
|
||||
timestamp -= timestampOffset;
|
||||
timestampOffset = pktTimestamp - pingTimestamp + PINGOFFSET;
|
||||
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
|
||||
LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
|
||||
timestamp += timestampOffset;
|
||||
isAligned = true;
|
||||
@@ -353,13 +429,13 @@ int USRPDevice::readSamples(short *buf, int len, bool *overrun,
|
||||
continue;
|
||||
}
|
||||
if ((word0 >> 28) & 0x04) {
|
||||
if (underrun) *underrun = true;
|
||||
if (underrun) *underrun = true;
|
||||
LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
|
||||
}
|
||||
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
|
||||
|
||||
|
||||
if (!isAligned) continue;
|
||||
|
||||
|
||||
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
|
||||
while (cursorStart*2 > currDataSize) {
|
||||
cursorStart -= currDataSize/2;
|
||||
@@ -372,17 +448,17 @@ int USRPDevice::readSamples(short *buf, int len, bool *overrun,
|
||||
else {
|
||||
memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
|
||||
}
|
||||
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
|
||||
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
|
||||
timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
|
||||
|
||||
LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// copy desired data to buf
|
||||
unsigned bufStart = dataStart+(timestamp-timeStart);
|
||||
if (bufStart + len < currDataSize/2) {
|
||||
if (bufStart + len < currDataSize/2) {
|
||||
LOG(DEBUG) << "bufStart: " << bufStart;
|
||||
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
|
||||
memset(data+bufStart*2,0,len*2*sizeof(short));
|
||||
@@ -400,21 +476,21 @@ int USRPDevice::readSamples(short *buf, 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);
|
||||
@@ -432,22 +508,25 @@ int USRPDevice::readSamples(short *buf, int len, bool *overrun,
|
||||
firstRead = true;
|
||||
}
|
||||
samplesRead += numSamples;
|
||||
|
||||
|
||||
return numSamples;
|
||||
#endif
|
||||
}
|
||||
|
||||
int USRPDevice::writeSamples(short *buf, int len, bool *underrun,
|
||||
unsigned long long timestamp,
|
||||
bool isControl)
|
||||
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
bool *underrun, unsigned long long timestamp,
|
||||
bool isControl)
|
||||
{
|
||||
writeLock.lock();
|
||||
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uTx) return 0;
|
||||
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uTx)
|
||||
return 0;
|
||||
|
||||
short *buf = bufs[0];
|
||||
|
||||
static uint32_t outData[128*20];
|
||||
|
||||
|
||||
for (int i = 0; i < len*2; i++) {
|
||||
buf[i] = host_to_usrp_short(buf[i]);
|
||||
}
|
||||
@@ -487,19 +566,21 @@ int USRPDevice::writeSamples(short *buf, int len, bool *underrun,
|
||||
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);
|
||||
bool tmpUnderrun;
|
||||
if (writeSamples((short *) data,1,&tmpUnderrun,timestamp & 0x0ffffffffll,true)) {
|
||||
|
||||
std::vector<short *> buf(1, data);
|
||||
if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
|
||||
pingTimestamp = timestamp;
|
||||
return true;
|
||||
}
|
||||
@@ -509,11 +590,16 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SWLOOPBACK
|
||||
bool USRPDevice::setTxFreq(double wFreq)
|
||||
#ifndef SWLOOPBACK
|
||||
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
usrp_tune_result result;
|
||||
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
|
||||
LOG(INFO) << "set TX: " << wFreq << std::endl
|
||||
<< " baseband freq: " << result.baseband_freq << std::endl
|
||||
@@ -530,10 +616,15 @@ bool USRPDevice::setTxFreq(double wFreq)
|
||||
}
|
||||
}
|
||||
|
||||
bool USRPDevice::setRxFreq(double wFreq)
|
||||
bool USRPDevice::setRxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
usrp_tune_result result;
|
||||
|
||||
if (chan) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
|
||||
LOG(INFO) << "set RX: " << wFreq << std::endl
|
||||
<< " baseband freq: " << result.baseband_freq << std::endl
|
||||
@@ -556,7 +647,10 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
|
||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
||||
#endif
|
||||
|
||||
RadioDevice *RadioDevice::make(double desiredSampleRate, bool skipRx)
|
||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||
InterfaceType iface, size_t chans, double offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths)
|
||||
{
|
||||
return new USRPDevice(desiredSampleRate, skipRx);
|
||||
return new USRPDevice(tx_sps);
|
||||
}
|
||||
@@ -21,29 +21,17 @@
|
||||
|
||||
#include "radioDevice.h"
|
||||
|
||||
#ifdef HAVE_LIBUSRP_3_3 // [
|
||||
# include <usrp/usrp_standard.h>
|
||||
# include <usrp/usrp_bytesex.h>
|
||||
# include <usrp/usrp_prims.h>
|
||||
#else // HAVE_LIBUSRP_3_3 ][
|
||||
# include "usrp_standard.h"
|
||||
# include "usrp_bytesex.h"
|
||||
# include "usrp_prims.h"
|
||||
#endif // !HAVE_LIBUSRP_3_3 ]
|
||||
#include <usrp/usrp_standard.h>
|
||||
#include <usrp/usrp_bytesex.h>
|
||||
#include <usrp/usrp_prims.h>
|
||||
#include <sys/time.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/** Define types which are not defined in libusrp-3.1 */
|
||||
#ifndef HAVE_LIBUSRP_3_2
|
||||
#include <boost/shared_ptr.hpp>
|
||||
typedef boost::shared_ptr<usrp_standard_tx> usrp_standard_tx_sptr;
|
||||
typedef boost::shared_ptr<usrp_standard_rx> usrp_standard_rx_sptr;
|
||||
#endif // HAVE_LIBUSRP_3_2
|
||||
|
||||
|
||||
|
||||
/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
|
||||
class USRPDevice: public RadioDevice {
|
||||
@@ -60,6 +48,7 @@ private:
|
||||
usrp_subdev_spec rxSubdevSpec;
|
||||
usrp_subdev_spec txSubdevSpec;
|
||||
|
||||
int sps;
|
||||
double actualSampleRate; ///< the actual USRP sampling rate
|
||||
unsigned int decimRate; ///< the USRP decimation rate
|
||||
|
||||
@@ -87,35 +76,30 @@ private:
|
||||
TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks
|
||||
TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command
|
||||
TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response
|
||||
static const TIMESTAMP PINGOFFSET = 272; ///< undetermined delay b/w ping response timestamp and true receive timestamp
|
||||
|
||||
long long pingOffset;
|
||||
unsigned long hi32Timestamp;
|
||||
unsigned long lastPktTimestamp;
|
||||
|
||||
double rxGain;
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
#ifdef SWLOOPBACK
|
||||
short loopbackBuffer[1000000];
|
||||
int loopbackBufferSize;
|
||||
double samplePeriod;
|
||||
double samplePeriod;
|
||||
|
||||
struct timeval startTime;
|
||||
struct timeval lastReadTime;
|
||||
bool firstRead;
|
||||
#endif
|
||||
|
||||
/** Set the transmission frequency */
|
||||
bool tx_setFreq(double freq, double *actual_freq);
|
||||
|
||||
/** Set the receiver frequency */
|
||||
bool rx_setFreq(double freq, double *actual_freq);
|
||||
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
USRPDevice (double _desiredSampleRate, bool skipRx);
|
||||
USRPDevice(size_t sps);
|
||||
|
||||
/** Instantiate the USRP */
|
||||
bool open(const std::string &);
|
||||
int open(const std::string &, int, bool);
|
||||
|
||||
/** Start the USRP */
|
||||
bool start();
|
||||
@@ -124,10 +108,9 @@ private:
|
||||
bool stop();
|
||||
|
||||
/** Set priority not supported */
|
||||
void setPriority() { return; }
|
||||
void setPriority(float prio = 0.5) { }
|
||||
|
||||
/** Only USB bus supported */
|
||||
busType getBus() { return USB; }
|
||||
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
|
||||
|
||||
/**
|
||||
Read samples from the USRP.
|
||||
@@ -139,10 +122,9 @@ private:
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
int readSamples(short *buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL);
|
||||
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL);
|
||||
/**
|
||||
Write samples to the USRP.
|
||||
@param buf Contains the data to be written.
|
||||
@@ -152,18 +134,17 @@ private:
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
int writeSamples(short *buf, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff,
|
||||
bool isControl = false);
|
||||
|
||||
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
|
||||
|
||||
/** Set the transmitter frequency */
|
||||
bool setTxFreq(double wFreq);
|
||||
bool setTxFreq(double wFreq, size_t chan = 0);
|
||||
|
||||
/** Set the receiver frequency */
|
||||
bool setRxFreq(double wFreq);
|
||||
bool setRxFreq(double wFreq, size_t chan = 0);
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
TIMESTAMP initialWriteTimestamp(void) { return 20000;}
|
||||
@@ -178,10 +159,10 @@ private:
|
||||
double fullScaleOutputValue() {return 9450.0;}
|
||||
|
||||
/** sets the receive chan gain, returns the gain setting **/
|
||||
double setRxGain(double dB);
|
||||
double setRxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** get the current receive gain */
|
||||
double getRxGain(void) {return rxGain;}
|
||||
double getRxGain(size_t chan = 0) { return rxGain; }
|
||||
|
||||
/** return maximum Rx Gain **/
|
||||
double maxRxGain(void);
|
||||
@@ -190,7 +171,7 @@ private:
|
||||
double minRxGain(void);
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
double setTxGain(double dB);
|
||||
double setTxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** return maximum Tx Gain **/
|
||||
double maxTxGain(void);
|
||||
@@ -198,15 +179,32 @@ 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() { return 0;}
|
||||
inline double getRxFreq() { return 0;}
|
||||
inline double getSampleRate() {return actualSampleRate;}
|
||||
inline double getTxFreq(size_t chan = 0) { return 0; }
|
||||
inline double getRxFreq(size_t chan = 0) { return 0; }
|
||||
inline double getSampleRate() { return actualSampleRate; }
|
||||
inline double numberRead() { return samplesRead; }
|
||||
inline double numberWritten() { return samplesWritten;}
|
||||
inline double numberWritten() { return samplesWritten; }
|
||||
|
||||
std::vector<std::string> tx_paths, rx_paths;
|
||||
};
|
||||
|
||||
#endif // _USRP_DEVICE_H_
|
||||
|
||||
83
Transceiver52M/laurent.m
Normal file
83
Transceiver52M/laurent.m
Normal file
@@ -0,0 +1,83 @@
|
||||
%
|
||||
% Laurent decomposition of GMSK signals
|
||||
% Generates C0, C1, and C2 pulse shapes
|
||||
%
|
||||
% Pierre Laurent, "Exact and Approximate Construction of Digital Phase
|
||||
% Modulations by Superposition of Amplitude Modulated Pulses", IEEE
|
||||
% Transactions of Communications, Vol. 34, No. 2, Feb 1986.
|
||||
%
|
||||
% Author: Thomas Tsou <tom@tsou.cc>
|
||||
%
|
||||
|
||||
% Modulation parameters
|
||||
oversamp = 16;
|
||||
L = 3;
|
||||
f = 270.83333e3;
|
||||
T = 1/f;
|
||||
h = 0.5;
|
||||
BT = 0.30;
|
||||
B = BT / T;
|
||||
|
||||
% Generate sampling points for L symbol periods
|
||||
t = -(L*T/2):T/oversamp:(L*T/2);
|
||||
t = t(1:end-1) + (T/oversamp/2);
|
||||
|
||||
% Generate Gaussian pulse
|
||||
g = qfunc(2*pi*B*(t - T/2)/(log(2)^.5)) - qfunc(2*pi*B*(t + T/2)/(log(2)^.5));
|
||||
g = g / sum(g) * pi/2;
|
||||
g = [0 g];
|
||||
|
||||
% Integrate phase
|
||||
q = 0;
|
||||
for i = 1:size(g,2);
|
||||
q(i) = sum(g(1:i));
|
||||
end
|
||||
|
||||
% Compute two sided "generalized phase pulse" function
|
||||
s = 0;
|
||||
for i = 1:size(g,2);
|
||||
s(i) = sin(q(i)) / sin(pi*h);
|
||||
end
|
||||
for i = (size(g,2) + 1):(2 * size(g,2) - 1);
|
||||
s(i) = sin(pi*h - q(i - (size(g,2) - 1))) / sin(pi*h);
|
||||
end
|
||||
|
||||
% Compute C0 pulse: valid for all L values
|
||||
c0 = s(1:end-(oversamp*(L-1)));
|
||||
for i = 1:L-1;
|
||||
c0 = c0 .* s((1 + i*oversamp):end-(oversamp*(L - 1 - i)));
|
||||
end
|
||||
|
||||
% Compute C1 pulse: valid for L = 3 only!
|
||||
% C1 = S0 * S4 * S2
|
||||
c1 = s(1:end-(oversamp*(4)));
|
||||
c1 = c1 .* s((1 + 4*oversamp):end-(oversamp*(4 - 1 - 3)));
|
||||
c1 = c1 .* s((1 + 2*oversamp):end-(oversamp*(4 - 1 - 1)));
|
||||
|
||||
% Compute C2 pulse: valid for L = 3 only!
|
||||
% C2 = S0 * S1 * S5
|
||||
c2 = s(1:end-(oversamp*(5)));
|
||||
c2 = c2 .* s((1 + 1*oversamp):end-(oversamp*(5 - 1 - 0)));
|
||||
c2 = c2 .* s((1 + 5*oversamp):end-(oversamp*(5 - 1 - 4)));
|
||||
|
||||
% Plot C0, C1, C2 Laurent pulse series
|
||||
figure(1);
|
||||
hold off;
|
||||
plot((0:size(c0,2)-1)/oversamp - 2,c0, 'b');
|
||||
hold on;
|
||||
plot((0:size(c1,2)-1)/oversamp - 2,c1, 'r');
|
||||
plot((0:size(c2,2)-1)/oversamp - 2,c2, 'g');
|
||||
|
||||
% Generate OpenBTS pulse
|
||||
numSamples = size(c0,2);
|
||||
centerPoint = (numSamples - 1)/2;
|
||||
i = ((0:numSamples) - centerPoint) / oversamp;
|
||||
xP = .96*exp(-1.1380*i.^2 - 0.527*i.^4);
|
||||
xP = xP / max(xP) * max(c0);
|
||||
|
||||
% Plot C0 pulse compared to OpenBTS pulse
|
||||
figure(2);
|
||||
hold off;
|
||||
plot((0:size(c0,2)-1)/oversamp, c0, 'b');
|
||||
hold on;
|
||||
plot((0:size(xP,2)-1)/oversamp, xP, 'r');
|
||||
573
Transceiver52M/osmo-trx.cpp
Normal file
573
Transceiver52M/osmo-trx.cpp
Normal file
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "Transceiver.h"
|
||||
#include "radioDevice.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <GSMCommon.h>
|
||||
#include <Logger.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 "convolve.h"
|
||||
#include "convert.h"
|
||||
#include "trx_vty.h"
|
||||
#include "debug.h"
|
||||
}
|
||||
|
||||
#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
|
||||
|
||||
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
|
||||
|
||||
static char* config_file = (char*)DEFAULT_CONFIG_FILE;
|
||||
|
||||
volatile bool gshutdown = false;
|
||||
|
||||
static void *tall_trx_ctx;
|
||||
static struct trx_ctx *g_trx_ctx;
|
||||
static struct ctrl_handle *g_ctrlh;
|
||||
|
||||
static RadioDevice *usrp;
|
||||
static RadioInterface *radio;
|
||||
static Transceiver *transceiver;
|
||||
|
||||
/* Create radio interface
|
||||
* The interface consists of sample rate changes, frequency shifts,
|
||||
* channel multiplexing, and other conversions. The transceiver core
|
||||
* accepts input vectors sampled at multiples of the GSM symbol rate.
|
||||
* The radio interface connects the main transceiver with the device
|
||||
* object, which may be operating some other rate.
|
||||
*/
|
||||
RadioInterface *makeRadioInterface(struct trx_ctx *trx,
|
||||
RadioDevice *usrp, int type)
|
||||
{
|
||||
RadioInterface *radio = NULL;
|
||||
|
||||
switch (type) {
|
||||
case RadioDevice::NORMAL:
|
||||
radio = new RadioInterface(usrp, trx->cfg.tx_sps,
|
||||
trx->cfg.rx_sps, trx->cfg.num_chans);
|
||||
break;
|
||||
case RadioDevice::RESAMP_64M:
|
||||
case RadioDevice::RESAMP_100M:
|
||||
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
|
||||
trx->cfg.rx_sps);
|
||||
break;
|
||||
case RadioDevice::MULTI_ARFCN:
|
||||
radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
|
||||
trx->cfg.rx_sps, trx->cfg.num_chans);
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Unsupported radio interface configuration";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!radio->init(type)) {
|
||||
LOG(ALERT) << "Failed to initialize radio interface";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return radio;
|
||||
}
|
||||
|
||||
/* Create transceiver core
|
||||
* The multi-threaded modem core operates at multiples of the GSM rate of
|
||||
* 270.8333 ksps and consists of GSM specific modulation, demodulation,
|
||||
* and decoding schemes. Also included are the socket interfaces for
|
||||
* connecting to the upper layer stack.
|
||||
*/
|
||||
int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
|
||||
{
|
||||
VectorFIFO *fifo;
|
||||
|
||||
transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr,
|
||||
trx->cfg.remote_addr, trx->cfg.tx_sps,
|
||||
trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0),
|
||||
radio, trx->cfg.rssi_offset);
|
||||
if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc,
|
||||
trx->cfg.rach_delay, trx->cfg.egprs)) {
|
||||
LOG(ALERT) << "Failed to initialize transceiver";
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < trx->cfg.num_chans; i++) {
|
||||
fifo = radio->receiveFIFO(i);
|
||||
if (fifo && transceiver->receiveFIFO(fifo, i))
|
||||
continue;
|
||||
|
||||
LOG(ALERT) << "Could not attach FIFO to channel " << i;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sig_handler(int signo)
|
||||
{
|
||||
fprintf(stdout, "signal %d received\n", signo);
|
||||
switch (signo) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
fprintf(stdout, "shutting down\n");
|
||||
gshutdown = true;
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_trx_ctx, stderr);
|
||||
talloc_report_full(tall_trx_ctx, stderr);
|
||||
break;
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_trx_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_signal_handlers()
|
||||
{
|
||||
/* Handle keyboard interrupt SIGINT */
|
||||
signal(SIGINT, &sig_handler);
|
||||
signal(SIGTERM, &sig_handler);
|
||||
signal(SIGABRT, &sig_handler);
|
||||
signal(SIGUSR1, &sig_handler);
|
||||
signal(SIGUSR2, &sig_handler);
|
||||
osmo_init_ignore_signals();
|
||||
}
|
||||
|
||||
static std::vector<std::string> comma_delimited_to_vector(char* opt)
|
||||
{
|
||||
std::string str = std::string(opt);
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss(str);
|
||||
|
||||
while( ss.good() )
|
||||
{
|
||||
std::string substr;
|
||||
getline(ss, substr, ',');
|
||||
result.push_back(substr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
fprintf(stdout, "Options:\n"
|
||||
" -h This text\n"
|
||||
" -C Filename The config file to use\n"
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:")) != -1) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'a':
|
||||
print_deprecated(option);
|
||||
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
|
||||
break;
|
||||
case 'l':
|
||||
print_deprecated(option);
|
||||
log_set_log_level(osmo_stderr_target, atoi(optarg));
|
||||
break;
|
||||
case 'i':
|
||||
print_deprecated(option);
|
||||
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg);
|
||||
break;
|
||||
case 'j':
|
||||
print_deprecated(option);
|
||||
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg);
|
||||
break;
|
||||
case 'p':
|
||||
print_deprecated(option);
|
||||
trx->cfg.base_port = atoi(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
print_deprecated(option);
|
||||
trx->cfg.num_chans = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
print_deprecated(option);
|
||||
trx->cfg.multi_arfcn = true;
|
||||
break;
|
||||
case 'x':
|
||||
print_deprecated(option);
|
||||
trx->cfg.clock_ref = REF_EXTERNAL;
|
||||
break;
|
||||
case 'g':
|
||||
print_deprecated(option);
|
||||
trx->cfg.clock_ref = REF_GPS;
|
||||
break;
|
||||
case 'f':
|
||||
print_deprecated(option);
|
||||
trx->cfg.filler = FILLER_DUMMY;
|
||||
break;
|
||||
case 'o':
|
||||
print_deprecated(option);
|
||||
trx->cfg.offset = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
print_deprecated(option);
|
||||
trx->cfg.tx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
print_deprecated(option);
|
||||
trx->cfg.rx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
print_deprecated(option);
|
||||
trx->cfg.rtsc_set = true;
|
||||
trx->cfg.rtsc = atoi(optarg);
|
||||
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
|
||||
trx->cfg.filler = FILLER_NORM_RAND;
|
||||
break;
|
||||
case 'A':
|
||||
print_deprecated(option);
|
||||
trx->cfg.rach_delay_set = true;
|
||||
trx->cfg.rach_delay = atoi(optarg);
|
||||
trx->cfg.filler = FILLER_ACCESS_RAND;
|
||||
break;
|
||||
case 'R':
|
||||
print_deprecated(option);
|
||||
trx->cfg.rssi_offset = atof(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
print_deprecated(option);
|
||||
trx->cfg.swap_channels = true;
|
||||
break;
|
||||
case 'e':
|
||||
print_deprecated(option);
|
||||
trx->cfg.egprs = true;
|
||||
break;
|
||||
case 't':
|
||||
print_deprecated(option);
|
||||
trx->cfg.sched_rr = atoi(optarg);
|
||||
break;
|
||||
case 'y':
|
||||
print_deprecated(option);
|
||||
tx_paths = comma_delimited_to_vector(optarg);
|
||||
tx_paths_set = true;
|
||||
break;
|
||||
case 'z':
|
||||
print_deprecated(option);
|
||||
rx_paths = comma_delimited_to_vector(optarg);
|
||||
rx_paths_set = true;
|
||||
break;
|
||||
case 'C':
|
||||
config_file = optarg;
|
||||
break;
|
||||
default:
|
||||
goto bad_config;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cmd line option specific validation & setup */
|
||||
|
||||
if (trx->cfg.num_chans > TRX_CHAN_MAX) {
|
||||
LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX;
|
||||
goto bad_config;
|
||||
}
|
||||
if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) ||
|
||||
(rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) {
|
||||
LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match";
|
||||
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;
|
||||
|
||||
bad_config:
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int trx_validate_config(struct trx_ctx *trx)
|
||||
{
|
||||
if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) {
|
||||
LOG(ERROR) << "Unsupported number of channels";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
|
||||
if ((trx->cfg.egprs || trx->cfg.multi_arfcn) &&
|
||||
(trx->cfg.tx_sps!=4 || trx->cfg.tx_sps!=4)) {
|
||||
LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_sched_rr(unsigned int prio)
|
||||
{
|
||||
struct sched_param param;
|
||||
int rc;
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = prio;
|
||||
printf("Setting SCHED_RR priority(%d)\n", param.sched_priority);
|
||||
rc = sched_setscheduler(getpid(), SCHED_RR, ¶m);
|
||||
if (rc != 0) {
|
||||
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;
|
||||
|
||||
std::cout << ost << std::endl;
|
||||
}
|
||||
|
||||
static void trx_stop()
|
||||
{
|
||||
std::cout << "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 (trx->cfg.multi_arfcn)
|
||||
iface = RadioDevice::MULTI_ARFCN;
|
||||
|
||||
/* Generate vector of rx/tx_path: */
|
||||
for (i = 0; i < trx->cfg.num_chans; i++) {
|
||||
rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
|
||||
tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
|
||||
}
|
||||
|
||||
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(trx, usrp, type);
|
||||
if (!radio)
|
||||
goto shutdown;
|
||||
|
||||
/* Create the transceiver core */
|
||||
if (makeTransceiver(trx, radio) < 0)
|
||||
goto shutdown;
|
||||
|
||||
chans = transceiver->numChans();
|
||||
std::cout << "-- Transceiver active with "
|
||||
<< chans << " channel(s)" << std::endl;
|
||||
|
||||
return 0;
|
||||
|
||||
shutdown:
|
||||
trx_stop();
|
||||
return -1;
|
||||
}
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "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;
|
||||
}
|
||||
228
Transceiver52M/radioBuffer.cpp
Normal file
228
Transceiver52M/radioBuffer.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Segmented Ring Buffer
|
||||
*
|
||||
* Copyright (C) 2015 Ettus Research LLC
|
||||
*
|
||||
* Author: Tom Tsou <tom@tsou.cc>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include "radioBuffer.h"
|
||||
|
||||
RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
|
||||
size_t hLen, bool outDirection)
|
||||
: writeIndex(0), readIndex(0), availSamples(0)
|
||||
{
|
||||
if (!outDirection)
|
||||
hLen = 0;
|
||||
|
||||
buffer = new float[2 * (hLen + numSegments * segmentLen)];
|
||||
bufferLen = numSegments * segmentLen;
|
||||
|
||||
segments.resize(numSegments);
|
||||
|
||||
for (size_t i = 0; i < numSegments; i++)
|
||||
segments[i] = &buffer[2 * (hLen + i * segmentLen)];
|
||||
|
||||
this->outDirection = outDirection;
|
||||
this->numSegments = numSegments;
|
||||
this->segmentLen = segmentLen;
|
||||
this->hLen = hLen;
|
||||
}
|
||||
|
||||
RadioBuffer::~RadioBuffer()
|
||||
{
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void RadioBuffer::reset()
|
||||
{
|
||||
writeIndex = 0;
|
||||
readIndex = 0;
|
||||
availSamples = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Output direction
|
||||
*
|
||||
* Return a pointer to the oldest segment or NULL if a complete segment is not
|
||||
* available.
|
||||
*/
|
||||
const float *RadioBuffer::getReadSegment()
|
||||
{
|
||||
if (!outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
if (availSamples < segmentLen) {
|
||||
std::cout << "Not enough samples " << std::endl;
|
||||
std::cout << availSamples << " available per segment "
|
||||
<< segmentLen << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t num = readIndex / segmentLen;
|
||||
|
||||
if (num >= numSegments) {
|
||||
std::cout << "Invalid segment" << std::endl;
|
||||
return NULL;
|
||||
} else if (!num) {
|
||||
memcpy(buffer,
|
||||
&buffer[2 * bufferLen],
|
||||
hLen * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
availSamples -= segmentLen;
|
||||
readIndex = (readIndex + segmentLen) % bufferLen;
|
||||
|
||||
return segments[num];
|
||||
}
|
||||
|
||||
/*
|
||||
* Output direction
|
||||
*
|
||||
* Write a non-segment length of samples to the buffer.
|
||||
*/
|
||||
bool RadioBuffer::write(const float *wr, size_t len)
|
||||
{
|
||||
if (!outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (availSamples + len > bufferLen) {
|
||||
std::cout << "Insufficient space" << std::endl;
|
||||
std::cout << bufferLen - availSamples << " available per write "
|
||||
<< len << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeIndex + len <= bufferLen) {
|
||||
memcpy(&buffer[2 * (writeIndex + hLen)],
|
||||
wr, len * 2 * sizeof(float));
|
||||
} else {
|
||||
size_t len0 = bufferLen - writeIndex;
|
||||
size_t len1 = len - len0;
|
||||
memcpy(&buffer[2 * (writeIndex + hLen)], wr, len0 * 2 * sizeof(float));
|
||||
memcpy(&buffer[2 * hLen], &wr[2 * len0], len1 * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
availSamples += len;
|
||||
writeIndex = (writeIndex + len) % bufferLen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioBuffer::zero(size_t len)
|
||||
{
|
||||
if (!outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (availSamples + len > bufferLen) {
|
||||
std::cout << "Insufficient space" << std::endl;
|
||||
std::cout << bufferLen - availSamples << " available per zero "
|
||||
<< len << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeIndex + len <= bufferLen) {
|
||||
memset(&buffer[2 * (writeIndex + hLen)],
|
||||
0, len * 2 * sizeof(float));
|
||||
} else {
|
||||
size_t len0 = bufferLen - writeIndex;
|
||||
size_t len1 = len - len0;
|
||||
memset(&buffer[2 * (writeIndex + hLen)], 0, len0 * 2 * sizeof(float));
|
||||
memset(&buffer[2 * hLen], 0, len1 * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
availSamples += len;
|
||||
writeIndex = (writeIndex + len) % bufferLen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Input direction
|
||||
*/
|
||||
float *RadioBuffer::getWriteSegment()
|
||||
{
|
||||
if (outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
if (bufferLen - availSamples < segmentLen) {
|
||||
std::cout << "Insufficient samples" << std::endl;
|
||||
std::cout << bufferLen - availSamples
|
||||
<< " available for segment " << segmentLen
|
||||
<< std::endl;
|
||||
return NULL;
|
||||
}
|
||||
if (writeIndex % segmentLen) {
|
||||
std::cout << "Internal segment error" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t num = writeIndex / segmentLen;
|
||||
|
||||
if (num >= numSegments)
|
||||
return NULL;
|
||||
|
||||
availSamples += segmentLen;
|
||||
writeIndex = (writeIndex + segmentLen) % bufferLen;
|
||||
|
||||
return segments[num];
|
||||
}
|
||||
|
||||
bool RadioBuffer::zeroWriteSegment()
|
||||
{
|
||||
float *segment = getWriteSegment();
|
||||
if (!segment)
|
||||
return false;
|
||||
|
||||
memset(segment, 0, segmentLen * 2 * sizeof(float));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioBuffer::read(float *rd, size_t len)
|
||||
{
|
||||
if (outDirection) {
|
||||
std::cout << "Invalid direction" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (availSamples < len) {
|
||||
std::cout << "Insufficient samples" << std::endl;
|
||||
std::cout << availSamples << " available for "
|
||||
<< len << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readIndex + len <= bufferLen) {
|
||||
memcpy(rd, &buffer[2 * readIndex], len * 2 * sizeof(float));
|
||||
} else {
|
||||
size_t len0 = bufferLen - readIndex;
|
||||
size_t len1 = len - len0;
|
||||
memcpy(rd, &buffer[2 * readIndex], len0 * 2 * sizeof(float));
|
||||
memcpy(&rd[2 * len0], buffer, len1 * 2 * sizeof(float));
|
||||
}
|
||||
|
||||
availSamples -= len;
|
||||
readIndex = (readIndex + len) % bufferLen;
|
||||
|
||||
return true;
|
||||
}
|
||||
45
Transceiver52M/radioBuffer.h
Normal file
45
Transceiver52M/radioBuffer.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
class RadioBuffer {
|
||||
public:
|
||||
RadioBuffer(size_t numSegments, size_t segmentLen,
|
||||
size_t hLen, bool outDirection);
|
||||
|
||||
~RadioBuffer();
|
||||
|
||||
const size_t getSegmentLen() { return segmentLen; }
|
||||
const size_t getNumSegments() { return numSegments; }
|
||||
const size_t getAvailSamples() { return availSamples; }
|
||||
const size_t getAvailSegments() { return availSamples / segmentLen; }
|
||||
|
||||
const size_t getFreeSamples()
|
||||
{
|
||||
return bufferLen - availSamples;
|
||||
}
|
||||
|
||||
const size_t getFreeSegments()
|
||||
{
|
||||
return getFreeSamples() / segmentLen;
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
/* Output direction */
|
||||
const float *getReadSegment();
|
||||
bool write(const float *wr, size_t len);
|
||||
bool zero(size_t len);
|
||||
|
||||
/* Input direction */
|
||||
float *getWriteSegment();
|
||||
bool zeroWriteSegment();
|
||||
bool read(float *rd, size_t len);
|
||||
|
||||
private:
|
||||
size_t writeIndex, readIndex, availSamples;
|
||||
size_t bufferLen, numSegments, segmentLen, hLen;
|
||||
float *buffer;
|
||||
std::vector<float *> segments;
|
||||
bool outDirection;
|
||||
};
|
||||
@@ -23,32 +23,27 @@
|
||||
|
||||
void RadioClock::set(const GSM::Time& wTime)
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
mClock = wTime;
|
||||
updateSignal.signal();
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
void RadioClock::incTN()
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
mClock.incTN();
|
||||
updateSignal.signal();
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
GSM::Time RadioClock::get()
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
GSM::Time retVal = mClock;
|
||||
mLock.unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RadioClock::wait()
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
updateSignal.wait(mLock,1);
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user