mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-03 13:43:18 +00:00
Compare commits
395 Commits
fairwaves/
...
tnt/ci
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d40f11962a | ||
|
|
dcbcfa58e4 | ||
|
|
b5def414b8 | ||
|
|
fc73c073a1 | ||
|
|
a7919265f5 | ||
|
|
9b394e0cc0 | ||
|
|
580c48b7d5 | ||
|
|
7214fde085 | ||
|
|
51509b3895 | ||
|
|
ef192d303c | ||
|
|
ac927b2690 | ||
|
|
3e9179a55e | ||
|
|
87b7d098e5 | ||
|
|
7bef2346c4 | ||
|
|
2876785f50 | ||
|
|
713b4d81cd | ||
|
|
b96d9ddee3 | ||
|
|
048c3ba300 | ||
|
|
75cb0b9dd6 | ||
|
|
46324d3597 | ||
|
|
bab1583a2c | ||
|
|
541496b65b | ||
|
|
c785fb130a | ||
|
|
2875290d95 | ||
|
|
4ce4555d0e | ||
|
|
2d130fb15d | ||
|
|
6c5f4bae46 | ||
|
|
71df42550a | ||
|
|
24f05ea1f7 | ||
|
|
d280045884 | ||
|
|
5e6f3e0cad | ||
|
|
21032b75c0 | ||
|
|
d01c7b98b6 | ||
|
|
158ea5bc66 | ||
|
|
3733ed5097 | ||
|
|
a8b3565246 | ||
|
|
a3934a11a4 | ||
|
|
acf804c034 | ||
|
|
d20b7fa579 | ||
|
|
77e18352fb | ||
|
|
f97d75b355 | ||
|
|
a944001873 | ||
|
|
42c165605a | ||
|
|
affd351787 | ||
|
|
03b11620d9 | ||
|
|
25185886f0 | ||
|
|
47031405f5 | ||
|
|
0646b3ce75 | ||
|
|
46cf9efc8e | ||
|
|
871713bf6a | ||
|
|
8d9a05ce5b | ||
|
|
fe865f45d7 | ||
|
|
e5b6664419 | ||
|
|
1595ddaa5f | ||
|
|
441d82add9 | ||
|
|
867cea575b | ||
|
|
2f53ea4cf2 | ||
|
|
138caaf09d | ||
|
|
3fadcad33e | ||
|
|
eaa0144dcb | ||
|
|
f7331764ac | ||
|
|
800c029c70 | ||
|
|
522cbe91d3 | ||
|
|
ed361f9912 | ||
|
|
2d085e290b | ||
|
|
5cea18e5cb | ||
|
|
4adc4eb8d0 | ||
|
|
41c68aa41c | ||
|
|
39e0bbacca | ||
|
|
b4ea7b5211 | ||
|
|
69869bd58f | ||
|
|
ebb37693a5 | ||
|
|
55928f23cb | ||
|
|
0277b58f6d | ||
|
|
be8a83f681 | ||
|
|
d9a460dce9 | ||
|
|
f570b4af62 | ||
|
|
46560ea254 | ||
|
|
038ab45137 | ||
|
|
6b50b62b12 | ||
|
|
3984256273 | ||
|
|
d06172ce32 | ||
|
|
762c951faa | ||
|
|
e47d7e964a | ||
|
|
3cee25ac68 | ||
|
|
0bd0782a39 | ||
|
|
8072650406 | ||
|
|
c6541e6c26 | ||
|
|
1e2c0105e2 | ||
|
|
32b3c2e4b2 | ||
|
|
1c4bbadda6 | ||
|
|
08dfe237c8 | ||
|
|
444ff34396 | ||
|
|
a79bc70737 | ||
|
|
a439fed166 | ||
|
|
8fb0c3dce4 | ||
|
|
f5e1cf8790 | ||
|
|
e7d267f178 | ||
|
|
06f3b622b8 | ||
|
|
74bcc562a9 | ||
|
|
55dd2aa5ab | ||
|
|
5b60c98769 | ||
|
|
207d8a2624 | ||
|
|
92fc186450 | ||
|
|
4d179abfd0 | ||
|
|
6fdfb68b9e | ||
|
|
5ac2cb3662 | ||
|
|
f4ee021b8c | ||
|
|
e62555370e | ||
|
|
1f15152968 | ||
|
|
21ce05c54f | ||
|
|
5e68cde779 | ||
|
|
1f4a009c67 | ||
|
|
544ce0d6b7 | ||
|
|
b148d05542 | ||
|
|
970096932e | ||
|
|
db936b9b55 | ||
|
|
49ad759072 | ||
|
|
8e498bfd35 | ||
|
|
46444637c6 | ||
|
|
86be40b4eb | ||
|
|
288d8af070 | ||
|
|
aae403f0c9 | ||
|
|
5cc8858d8f | ||
|
|
70d0344b31 | ||
|
|
62002fb8ac | ||
|
|
03b3c30533 | ||
|
|
a4b569d936 | ||
|
|
eb0945d85d | ||
|
|
f968507912 | ||
|
|
58c89fb8d6 | ||
|
|
16e7e20f85 | ||
|
|
c7756e73b7 | ||
|
|
7ed686b223 | ||
|
|
2ab6ddb0de | ||
|
|
b229439b31 | ||
|
|
ffb3301bd8 | ||
|
|
62b7900fd7 | ||
|
|
61707e8b23 | ||
|
|
ce70ba529c | ||
|
|
cfb9dac70d | ||
|
|
105a61e689 | ||
|
|
2407314f2e | ||
|
|
ff4418539c | ||
|
|
0494d05916 | ||
|
|
fd268b6f5a | ||
|
|
43f3678b40 | ||
|
|
6493dc838b | ||
|
|
f7905ac548 | ||
|
|
0c27938d44 | ||
|
|
587916e810 | ||
|
|
79024867de | ||
|
|
4aec1f1cf5 | ||
|
|
68f054169b | ||
|
|
ea8be22e50 | ||
|
|
a5054b398b | ||
|
|
a438114173 | ||
|
|
9cb4f27112 | ||
|
|
c38e45e9dc | ||
|
|
1f50fedb5f | ||
|
|
c7a0bf1ffc | ||
|
|
940738e86a | ||
|
|
8c1e2bddff | ||
|
|
01eea0aa42 | ||
|
|
55df1e43e3 | ||
|
|
e9424e241f | ||
|
|
d0ac926b56 | ||
|
|
00d5114717 | ||
|
|
3a496f3b8a | ||
|
|
fad2e09840 | ||
|
|
e09e80f5ee | ||
|
|
2e276e7edd | ||
|
|
dffc21725c | ||
|
|
96f0f2cf7e | ||
|
|
225b16d48e | ||
|
|
0ebbb2ed2e | ||
|
|
2fea950644 | ||
|
|
d0a97a5f73 | ||
|
|
295b938d51 | ||
|
|
e8605202ab | ||
|
|
2a8183bdf0 | ||
|
|
478f82f47e | ||
|
|
f37b0ad652 | ||
|
|
3b78cbfdc1 | ||
|
|
f3d7f443a0 | ||
|
|
e564f0fd84 | ||
|
|
0fc20d14b3 | ||
|
|
a4316ee4c5 | ||
|
|
2128a308eb | ||
|
|
43fedb656b | ||
|
|
53bdb7f82a | ||
|
|
6462dd3963 | ||
|
|
e1977fcd22 | ||
|
|
f97296e0ce | ||
|
|
20259cb307 | ||
|
|
ffa4e5938c | ||
|
|
c0c6d70fe9 | ||
|
|
8c6c5d2bcd | ||
|
|
a62fcf786a | ||
|
|
4d9b59c3ef | ||
|
|
bd0efb0bea | ||
|
|
8fbbd656c7 | ||
|
|
b35cba613a | ||
|
|
8dffadb8da | ||
|
|
408f25081e | ||
|
|
2001550f7d | ||
|
|
a3ab8c263d | ||
|
|
efac20b6bb | ||
|
|
0bbd8922ea | ||
|
|
28b8cc6283 | ||
|
|
3f52f0e6c5 | ||
|
|
3da1f8352e | ||
|
|
5ea1817dc2 | ||
|
|
49d42e979e | ||
|
|
3a3b220751 | ||
|
|
ab22f4c421 | ||
|
|
8b843e5bed | ||
|
|
c92dad32dd | ||
|
|
61837c0420 | ||
|
|
f83e11fefd | ||
|
|
01aff88ce9 | ||
|
|
11d50d950c | ||
|
|
8bd111c942 | ||
|
|
3808e479aa | ||
|
|
bd45a979f8 | ||
|
|
b7095c7bc5 | ||
|
|
77ce99ac67 | ||
|
|
f58cd8ac83 | ||
|
|
99eb07e232 | ||
|
|
89be118a3b | ||
|
|
6fafd33b13 | ||
|
|
6e55d51747 | ||
|
|
6cae1d7b4b | ||
|
|
28ce315a32 | ||
|
|
10d76b6863 | ||
|
|
708b8b44ae | ||
|
|
cb0fc9b21a | ||
|
|
8639fee504 | ||
|
|
ca46896cfe | ||
|
|
4a25d6b8f6 | ||
|
|
79baee3a8f | ||
|
|
c2ba427b52 | ||
|
|
611212676b | ||
|
|
4ebb289c90 | ||
|
|
2f376a3edf | ||
|
|
2edbe4d366 | ||
|
|
a3694bd303 | ||
|
|
2652f2bc39 | ||
|
|
93d9b114b7 | ||
|
|
2ac788b2c3 | ||
|
|
d36ef2f57b | ||
|
|
caf2abc58f | ||
|
|
de1685f6d7 | ||
|
|
f3837d26f9 | ||
|
|
ddf4743306 | ||
|
|
82f83ced73 | ||
|
|
cff4ed9b4c | ||
|
|
6ec26bb788 | ||
|
|
a1ff991402 | ||
|
|
d09843c692 | ||
|
|
e5448ff972 | ||
|
|
e48c1367dc | ||
|
|
aa60dda99a | ||
|
|
1468a5c3dc | ||
|
|
b0e1bd8c22 | ||
|
|
78e1cd20e2 | ||
|
|
db9c1b54cb | ||
|
|
099a44abfb | ||
|
|
8c80095017 | ||
|
|
d49a6aa136 | ||
|
|
81486e053c | ||
|
|
28d8081e25 | ||
|
|
87ed77b937 | ||
|
|
f9d996813d | ||
|
|
aa5acc953c | ||
|
|
934da48618 | ||
|
|
7c405a0c1f | ||
|
|
4cafb0fa15 | ||
|
|
f611569018 | ||
|
|
354741326c | ||
|
|
d2e5c5694e | ||
|
|
a3dce85ffc | ||
|
|
bb0c68ae61 | ||
|
|
87d158cc2d | ||
|
|
7278a87767 | ||
|
|
63eef9faf2 | ||
|
|
d67bd603e9 | ||
|
|
988a464d5d | ||
|
|
1b6ab7d7ee | ||
|
|
980525c8a9 | ||
|
|
70134a01eb | ||
|
|
1fb0ce67d8 | ||
|
|
8ca237b5c2 | ||
|
|
082bbbf8fe | ||
|
|
3bd763d2a1 | ||
|
|
ee57357682 | ||
|
|
8537b90dbe | ||
|
|
038fd7fd70 | ||
|
|
0cd246c27a | ||
|
|
61fbf2ec95 | ||
|
|
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 |
65
.gitignore
vendored
Normal file
65
.gitignore
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# build results
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
Transceiver52M/osmo-trx-uhd
|
||||||
|
Transceiver52M/osmo-trx-usrp1
|
||||||
|
Transceiver52M/osmo-trx-lms
|
||||||
|
|
||||||
|
# tests
|
||||||
|
tests/CommonLibs/BitVectorTest
|
||||||
|
tests/CommonLibs/F16Test
|
||||||
|
tests/CommonLibs/InterthreadTest
|
||||||
|
tests/CommonLibs/LogTest
|
||||||
|
tests/CommonLibs/RegexpTest
|
||||||
|
tests/CommonLibs/SocketsTest
|
||||||
|
tests/CommonLibs/TimevalTest
|
||||||
|
tests/CommonLibs/URLEncodeTest
|
||||||
|
tests/CommonLibs/VectorTest
|
||||||
|
tests/CommonLibs/PRBSTest
|
||||||
|
tests/Transceiver52M/convolve_test
|
||||||
|
tests/Transceiver52M/LMSDeviceTest
|
||||||
|
|
||||||
|
# automake/autoconf
|
||||||
|
*.in
|
||||||
|
.deps
|
||||||
|
.libs
|
||||||
|
.dirstamp
|
||||||
|
*~
|
||||||
|
Makefile
|
||||||
|
config.log
|
||||||
|
config.status
|
||||||
|
config.h
|
||||||
|
config.guess
|
||||||
|
config.sub
|
||||||
|
config/*
|
||||||
|
configure
|
||||||
|
compile
|
||||||
|
aclocal.m4
|
||||||
|
autom4te.cache
|
||||||
|
depcomp
|
||||||
|
install-sh
|
||||||
|
libtool
|
||||||
|
ltmain.sh
|
||||||
|
missing
|
||||||
|
stamp-h1
|
||||||
|
INSTALL
|
||||||
|
tests/package.m4
|
||||||
|
tests/testsuite
|
||||||
|
tests/atconfig
|
||||||
|
tests/testsuite.dir
|
||||||
|
tests/testsuite.log
|
||||||
|
|
||||||
|
# vim
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
# manuals
|
||||||
|
doc/manuals/*.html
|
||||||
|
doc/manuals/*.svg
|
||||||
|
doc/manuals/*.pdf
|
||||||
|
doc/manuals/*__*.png
|
||||||
|
doc/manuals/*.check
|
||||||
|
doc/manuals/generated/
|
||||||
|
doc/manuals/osmomsc-usermanual.xml
|
||||||
|
doc/manuals/common
|
||||||
|
doc/manuals/build
|
||||||
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.
|
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||||
#
|
#
|
||||||
# This file is part of GNU Radio
|
# This file is part of GNU Radio
|
||||||
#
|
#
|
||||||
# GNU Radio is free software; you can redistribute it and/or modify
|
# GNU Radio is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation; either version 3, or (at your option)
|
# the Free Software Foundation; either version 3, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# GNU Radio is distributed in the hope that it will be useful,
|
# GNU Radio is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
@@ -23,34 +23,17 @@ David A. Burgess, dburgess@kestrelsp.com:
|
|||||||
CLI/CLI.h
|
CLI/CLI.h
|
||||||
CommonLibs/Assert.h
|
CommonLibs/Assert.h
|
||||||
CommonLibs/BitVector.cpp
|
CommonLibs/BitVector.cpp
|
||||||
CommonLibs/BitVectorTest.cpp
|
|
||||||
CommonLibs/Configuration.cpp
|
|
||||||
CommonLibs/Configuration.h
|
|
||||||
CommonLibs/ConfigurationTest.cpp
|
|
||||||
CommonLibs/Interthread.h
|
CommonLibs/Interthread.h
|
||||||
CommonLibs/InterthreadTest.cpp
|
|
||||||
CommonLibs/LinkedLists.cpp
|
CommonLibs/LinkedLists.cpp
|
||||||
CommonLibs/LinkedLists.h
|
CommonLibs/LinkedLists.h
|
||||||
CommonLibs/Regexp.h
|
CommonLibs/Regexp.h
|
||||||
CommonLibs/RegexpTest.cpp
|
|
||||||
CommonLibs/Sockets.cpp
|
CommonLibs/Sockets.cpp
|
||||||
CommonLibs/Sockets.h
|
CommonLibs/Sockets.h
|
||||||
CommonLibs/SocketsTest.cpp
|
|
||||||
CommonLibs/Threads.cpp
|
CommonLibs/Threads.cpp
|
||||||
CommonLibs/Threads.h
|
CommonLibs/Threads.h
|
||||||
CommonLibs/Timeval.cpp
|
CommonLibs/Timeval.cpp
|
||||||
CommonLibs/Timeval.h
|
CommonLibs/Timeval.h
|
||||||
CommonLibs/TimevalTest.cpp
|
|
||||||
CommonLibs/Vector.h
|
CommonLibs/Vector.h
|
||||||
CommonLibs/VectorTest.cpp
|
|
||||||
Control/CallControl.cpp
|
|
||||||
Control/ControlCommon.cpp
|
|
||||||
Control/ControlCommon.h
|
|
||||||
Control/FACCHDispatch.cpp
|
|
||||||
Control/MobilityManagement.cpp
|
|
||||||
Control/PagerTest.cpp
|
|
||||||
Control/RadioResource.cpp
|
|
||||||
Control/SDCCHDispatch.cpp
|
|
||||||
GSM/GSM610Tables.cpp
|
GSM/GSM610Tables.cpp
|
||||||
GSM/GSM610Tables.h
|
GSM/GSM610Tables.h
|
||||||
GSM/GSMCommon.cpp
|
GSM/GSMCommon.cpp
|
||||||
@@ -82,29 +65,15 @@ David A. Burgess, dburgess@kestrelsp.com:
|
|||||||
GSM/GSMTransfer.cpp
|
GSM/GSMTransfer.cpp
|
||||||
GSM/GSMTransfer.h
|
GSM/GSMTransfer.h
|
||||||
LICENSEBLOCK
|
LICENSEBLOCK
|
||||||
SIP/SIPEngine.h
|
|
||||||
SIP/SIPInterface.h
|
|
||||||
SMS/SMSMessages.cpp
|
|
||||||
SMS/SMSMessages.h
|
|
||||||
SMS/SMSTransfer.cpp
|
|
||||||
SMS/SMSTransfer.h
|
|
||||||
TRXManager/TRXManager.cpp
|
TRXManager/TRXManager.cpp
|
||||||
Transceiver/Complex.h
|
Transceiver/Complex.h
|
||||||
apps/OpenBTS900.cpp
|
tests/CommonLibs/BitVectorTest.cpp
|
||||||
apps/OpenBTS850.cpp
|
tests/CommonLibs/InterthreadTest.cpp
|
||||||
apps/OpenBTS25c3.cpp
|
tests/CommonLibs/SocketsTest.cpp
|
||||||
tests/AGCHTest.cpp
|
tests/CommonLibs/TimevalTest.cpp
|
||||||
tests/BeaconTest.cpp
|
tests/CommonLibs/VectorTest.cpp
|
||||||
tests/CallTest.cpp
|
|
||||||
tests/CallTest2.cpp
|
|
||||||
tests/LAPDmTest.cpp
|
|
||||||
tests/LoopbackTest.cpp
|
|
||||||
tests/RegistrationTest.cpp
|
|
||||||
tests/TRXSimulator.cpp
|
|
||||||
|
|
||||||
Harvind S. Samra, hssamra@kestrelsp.com:
|
Harvind S. Samra, hssamra@kestrelsp.com:
|
||||||
Control/PagerTest.cpp
|
|
||||||
Control/RadioResource.cpp
|
|
||||||
GSM/GSMConfig.h
|
GSM/GSMConfig.h
|
||||||
GSM/GSMTransfer.h
|
GSM/GSMTransfer.h
|
||||||
LICENSEBLOCK
|
LICENSEBLOCK
|
||||||
@@ -126,13 +95,6 @@ Harvind S. Samra, hssamra@kestrelsp.com:
|
|||||||
Transceiver/testRadio.cpp
|
Transceiver/testRadio.cpp
|
||||||
|
|
||||||
Raffi Sevlian, raffisev@gmail.com:
|
Raffi Sevlian, raffisev@gmail.com:
|
||||||
Control/CallControl.cpp
|
|
||||||
Control/ControlCommon.cpp
|
|
||||||
Control/ControlCommon.h
|
|
||||||
Control/FACCHDispatch.cpp
|
|
||||||
Control/MobilityManagement.cpp
|
|
||||||
Control/PagerTest.cpp
|
|
||||||
Control/RadioResource.cpp
|
|
||||||
GSM/GSMCommon.h
|
GSM/GSMCommon.h
|
||||||
GSM/GSMConfig.h
|
GSM/GSMConfig.h
|
||||||
GSM/GSML1FEC.h
|
GSM/GSML1FEC.h
|
||||||
@@ -157,36 +119,9 @@ Raffi Sevlian, raffisev@gmail.com:
|
|||||||
GSM/GSMSAPMux.h
|
GSM/GSMSAPMux.h
|
||||||
GSM/GSMTransfer.h
|
GSM/GSMTransfer.h
|
||||||
LICENSEBLOCK
|
LICENSEBLOCK
|
||||||
SIP/SIPEngine.cpp
|
|
||||||
SIP/SIPInterface.cpp
|
|
||||||
SIP/SIPInterface.h
|
|
||||||
SIP/SIPMessage.cpp
|
|
||||||
SIP/SIPMessage.h
|
|
||||||
SIP/SIPUtility.cpp
|
|
||||||
SIP/SIPUtility.h
|
|
||||||
SMS/CMMessage.cpp
|
|
||||||
SMS/CMMessage.h
|
|
||||||
SMS/CMProcessor.cpp
|
|
||||||
SMS/CMProcessor.h
|
|
||||||
SMS/CMTest.cpp
|
|
||||||
SMS/RLMessage.cpp
|
|
||||||
SMS/RLMessage.h
|
|
||||||
SMS/RLProcessor.cpp
|
|
||||||
SMS/RLProcessor.h
|
|
||||||
SMS/SMSMessages.cpp
|
|
||||||
SMS/SMSMessages.h
|
|
||||||
SMS/SMSProcessors.cpp
|
|
||||||
SMS/SMSProcessors.h
|
|
||||||
SMS/SMSTransfer.cpp
|
|
||||||
SMS/SMSTransfer.h
|
|
||||||
SMS/TLMessage.cpp
|
|
||||||
SMS/TLMessage.h
|
|
||||||
SMS/TLProcessor.cpp
|
|
||||||
SMS/TLProcessor.h
|
|
||||||
TRXManager/TRXManager.h
|
TRXManager/TRXManager.h
|
||||||
|
|
||||||
Alon Levy, alonlevy1@gmail.com
|
Alon Levy, alonlevy1@gmail.com
|
||||||
RRLPMessages.cpp
|
RRLPMessages.cpp
|
||||||
RRLPMessages.h
|
RRLPMessages.h
|
||||||
RRLPTest.cpp
|
RRLPTest.cpp
|
||||||
|
|
||||||
|
|||||||
28
COPYING
28
COPYING
@@ -673,16 +673,16 @@ on the AGPLv3 text.
|
|||||||
=========================================================================
|
=========================================================================
|
||||||
|
|
||||||
|
|
||||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS
|
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX
|
||||||
|
|
||||||
|
|
||||||
Permissive Terms Supplementing the License
|
Permissive Terms Supplementing the License
|
||||||
|
|
||||||
1. Remote Interaction Through IP Networks.
|
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
|
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
|
for the purposes of the Affero General Public License and cellular users are
|
||||||
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
|
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
|
||||||
Network Interaction; Use with the GNU General Public License").
|
Network Interaction; Use with the GNU General Public License").
|
||||||
@@ -694,17 +694,6 @@ interfaces other than the GSM air interface from the requirements of Section 13
|
|||||||
is an additional permission granted to you.
|
is an additional permission granted to you.
|
||||||
|
|
||||||
|
|
||||||
Non-Permissive Terms Supplementing The License
|
|
||||||
|
|
||||||
1. Trademarks.
|
|
||||||
|
|
||||||
"OpenBTS" is a trademark of Range Networks, Inc., registered with
|
|
||||||
the US Patent and Trademark Office. Your use of OpenBTS software under a GPL
|
|
||||||
license does not include the right to use the OpenBTS trademark in commerce.
|
|
||||||
This additional non-permissive term is consistent with Section 7 of the AGPLv3
|
|
||||||
license.
|
|
||||||
|
|
||||||
|
|
||||||
END OF ADDITIONAL TERMS
|
END OF ADDITIONAL TERMS
|
||||||
|
|
||||||
|
|
||||||
@@ -712,13 +701,8 @@ END OF ADDITIONAL TERMS
|
|||||||
How to comply with Section 13 of the AGPLv3 license.
|
How to comply with Section 13 of the AGPLv3 license.
|
||||||
|
|
||||||
The recommended method for compliance with Section 13 of the AGPLv3 license is
|
The recommended method for compliance with Section 13 of the AGPLv3 license is
|
||||||
to deliver a text message to each handset that attaches to the OpenBTS cellular
|
to deliver a text message to each handset that attaches to the cellular
|
||||||
network. At a minimum, that text message should include the string "OpenBTS
|
network which uses OsmoTRX. At a minimum, that text message should include the string
|
||||||
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
|
"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
|
message need not be delivered to handsets that are denied registration with the
|
||||||
network, since those handsets have been denied service.
|
network, since those handsets have been denied service.
|
||||||
|
|
||||||
In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message"
|
|
||||||
feature. See the OpenBTS.config.example file for more information on the use of
|
|
||||||
this feature for AGPLv3 compliance.
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
using namespace std;
|
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 BitVector::sum() const
|
||||||
{
|
{
|
||||||
unsigned sum = 0;
|
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)
|
SoftVector::SoftVector(const BitVector& source)
|
||||||
{
|
{
|
||||||
resize(source.size());
|
resize(source.size());
|
||||||
for (size_t i=0; i<size(); i++) {
|
for (size_t i=0; i<size(); i++) {
|
||||||
if (source.bit(i)) mStart[i]=1.0F;
|
if (source.bit(i)) mStart[i]=1.0F;
|
||||||
else mStart[i]=0.0F;
|
else mStart[i]=-1.0F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,102 +260,20 @@ BitVector SoftVector::sliced() const
|
|||||||
size_t sz = size();
|
size_t sz = size();
|
||||||
BitVector newSig(sz);
|
BitVector newSig(sz);
|
||||||
for (size_t i=0; i<sz; i++) {
|
for (size_t i=0; i<sz; i++) {
|
||||||
if (mStart[i]>0.5F) newSig[i]=1;
|
if (mStart[i]>0.0F) newSig[i]=1;
|
||||||
else newSig[i] = 0;
|
else newSig[i] = 0;
|
||||||
}
|
}
|
||||||
return newSig;
|
return newSig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
|
|
||||||
{
|
|
||||||
const size_t sz = size();
|
|
||||||
const unsigned deferral = decoder.deferral();
|
|
||||||
const size_t ctsz = sz + deferral*decoder.iRate();
|
|
||||||
assert(sz <= decoder.iRate()*target.size());
|
|
||||||
|
|
||||||
// Build a "history" array where each element contains the full history.
|
|
||||||
uint32_t history[ctsz];
|
|
||||||
{
|
|
||||||
BitVector bits = sliced();
|
|
||||||
uint32_t accum = 0;
|
|
||||||
for (size_t i=0; i<sz; i++) {
|
|
||||||
accum = (accum<<1) | bits.bit(i);
|
|
||||||
history[i] = accum;
|
|
||||||
}
|
|
||||||
// Repeat last bit at the end.
|
|
||||||
for (size_t i=sz; i<ctsz; i++) {
|
|
||||||
accum = (accum<<1) | (accum & 0x01);
|
|
||||||
history[i] = accum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precompute metric tables.
|
|
||||||
float matchCostTable[ctsz];
|
|
||||||
float mismatchCostTable[ctsz];
|
|
||||||
{
|
|
||||||
const float *dp = mStart;
|
|
||||||
for (size_t i=0; i<sz; i++) {
|
|
||||||
// pVal is the probability that a bit is correct.
|
|
||||||
// ipVal is the probability that a bit is incorrect.
|
|
||||||
float pVal = dp[i];
|
|
||||||
if (pVal>0.5F) pVal = 1.0F-pVal;
|
|
||||||
float ipVal = 1.0F-pVal;
|
|
||||||
// This is a cheap approximation to an ideal cost function.
|
|
||||||
if (pVal<0.01F) pVal = 0.01;
|
|
||||||
if (ipVal<0.01F) ipVal = 0.01;
|
|
||||||
matchCostTable[i] = 0.25F/ipVal;
|
|
||||||
mismatchCostTable[i] = 0.25F/pVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad end of table with unknowns
|
|
||||||
for (size_t i=sz; i<ctsz; i++) {
|
|
||||||
matchCostTable[i] = 0.5F;
|
|
||||||
mismatchCostTable[i] = 0.5F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
decoder.initializeStates();
|
|
||||||
// Each sample of history[] carries its history.
|
|
||||||
// So we only have to process every iRate-th sample.
|
|
||||||
const unsigned step = decoder.iRate();
|
|
||||||
// input pointer
|
|
||||||
const uint32_t *ip = history + step - 1;
|
|
||||||
// output pointers
|
|
||||||
char *op = target.begin();
|
|
||||||
const char *const opt = target.end();
|
|
||||||
// table pointers
|
|
||||||
const float* match = matchCostTable;
|
|
||||||
const float* mismatch = mismatchCostTable;
|
|
||||||
size_t oCount = 0;
|
|
||||||
while (op<opt) {
|
|
||||||
// Viterbi algorithm
|
|
||||||
assert(match-matchCostTable<sizeof(matchCostTable)/sizeof(matchCostTable[0])-1);
|
|
||||||
assert(mismatch-mismatchCostTable<sizeof(mismatchCostTable)/sizeof(mismatchCostTable[0])-1);
|
|
||||||
const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch);
|
|
||||||
ip += step;
|
|
||||||
match += step;
|
|
||||||
mismatch += step;
|
|
||||||
// output
|
|
||||||
if (oCount>=deferral) *op++ = (minCost.iState >> deferral)&0x01;
|
|
||||||
oCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// (pat) Added 6-22-2012
|
|
||||||
float SoftVector::getEnergy(float *plow) const
|
float SoftVector::getEnergy(float *plow) const
|
||||||
{
|
{
|
||||||
const SoftVector &vec = *this;
|
const SoftVector &vec = *this;
|
||||||
int len = vec.size();
|
int len = vec.size();
|
||||||
float avg = 0; float low = 1;
|
float avg = 0; float low = 1;
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
float bit = vec[i];
|
float energy = fabsf(vec[i]);
|
||||||
float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
|
|
||||||
if (energy < low) low = energy;
|
if (energy < low) low = energy;
|
||||||
avg += energy/len;
|
avg += energy/len;
|
||||||
}
|
}
|
||||||
@@ -545,8 +285,12 @@ float SoftVector::getEnergy(float *plow) const
|
|||||||
ostream& operator<<(ostream& os, const SoftVector& sv)
|
ostream& operator<<(ostream& os, const SoftVector& sv)
|
||||||
{
|
{
|
||||||
for (size_t i=0; i<sv.size(); i++) {
|
for (size_t i=0; i<sv.size(); i++) {
|
||||||
if (sv[i]<0.25) os << "0";
|
if (sv[i]<-0.5) os << "0";
|
||||||
else if (sv[i]>0.75) os << "1";
|
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 << "-";
|
else os << "-";
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
|
|||||||
@@ -30,201 +30,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
class BitVector;
|
|
||||||
class SoftVector;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Shift-register (LFSR) generator. */
|
|
||||||
class Generator {
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
|
|
||||||
uint64_t mState; ///< shift register state. LSB is most recent.
|
|
||||||
uint64_t mMask; ///< mask for reading state
|
|
||||||
unsigned mLen; ///< number of bits used in shift register
|
|
||||||
unsigned mLen_1; ///< mLen - 1
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Generator(uint64_t wCoeff, unsigned wLen)
|
|
||||||
:mCoeff(wCoeff),mState(0),
|
|
||||||
mMask((1ULL<<wLen)-1),
|
|
||||||
mLen(wLen),mLen_1(wLen-1)
|
|
||||||
{ assert(wLen<64); }
|
|
||||||
|
|
||||||
void clear() { mState=0; }
|
|
||||||
|
|
||||||
/**@name Accessors */
|
|
||||||
//@{
|
|
||||||
uint64_t state() const { return mState & mMask; }
|
|
||||||
unsigned size() const { return mLen; }
|
|
||||||
//@}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Calculate one bit of a syndrome.
|
|
||||||
This is in the .h for inlining.
|
|
||||||
*/
|
|
||||||
void syndromeShift(unsigned inBit)
|
|
||||||
{
|
|
||||||
const unsigned fb = (mState>>(mLen_1)) & 0x01;
|
|
||||||
mState = (mState<<1) ^ (inBit & 0x01);
|
|
||||||
if (fb) mState ^= mCoeff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Update the generator state by one cycle.
|
|
||||||
This is in the .h for inlining.
|
|
||||||
*/
|
|
||||||
void encoderShift(unsigned inBit)
|
|
||||||
{
|
|
||||||
const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01;
|
|
||||||
mState <<= 1;
|
|
||||||
if (fb) mState ^= mCoeff;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Parity (CRC-type) generator and checker based on a Generator. */
|
|
||||||
class Parity : public Generator {
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
unsigned mCodewordSize;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize)
|
|
||||||
:Generator(wCoefficients, wParitySize),
|
|
||||||
mCodewordSize(wCodewordSize)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/** Compute the parity word and write it into the target segment. */
|
|
||||||
void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true);
|
|
||||||
|
|
||||||
/** Compute the syndrome of a received sequence. */
|
|
||||||
uint64_t syndrome(const BitVector& receivedCodeword);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Class to represent convolutional coders/decoders of rate 1/2, memory length 4.
|
|
||||||
This is the "workhorse" coder for most GSM channels.
|
|
||||||
*/
|
|
||||||
class ViterbiR2O4 {
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**name Lots of precomputed elements so the compiler can optimize like hell. */
|
|
||||||
//@{
|
|
||||||
/**@name Core values. */
|
|
||||||
//@{
|
|
||||||
static const unsigned mIRate = 2; ///< reciprocal of rate
|
|
||||||
static const unsigned mOrder = 4; ///< memory length of generators
|
|
||||||
//@}
|
|
||||||
/**@name Derived values. */
|
|
||||||
//@{
|
|
||||||
static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors
|
|
||||||
static const uint32_t mSMask = mIStates-1; ///< survivor mask
|
|
||||||
static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask
|
|
||||||
static const uint32_t mOMask = (0x01<<mIRate)-1; ///< ouput mask, all iRate low bits set
|
|
||||||
static const unsigned mNumCands = mIStates*2; ///< number of candidates to generate during branching
|
|
||||||
static const unsigned mDeferral = 6*mOrder; ///< deferral to be used
|
|
||||||
//@}
|
|
||||||
//@}
|
|
||||||
|
|
||||||
/** Precomputed tables. */
|
|
||||||
//@{
|
|
||||||
uint32_t mCoeffs[mIRate]; ///< polynomial for each generator
|
|
||||||
uint32_t mStateTable[mIRate][2*mIStates]; ///< precomputed generator output tables
|
|
||||||
uint32_t mGeneratorTable[2*mIStates]; ///< precomputed coder output table
|
|
||||||
//@}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
A candidate sequence in a Viterbi decoder.
|
|
||||||
The 32-bit state register can support a deferral of 6 with a 4th-order coder.
|
|
||||||
*/
|
|
||||||
typedef struct candStruct {
|
|
||||||
uint32_t iState; ///< encoder input associated with this candidate
|
|
||||||
uint32_t oState; ///< encoder output associated with this candidate
|
|
||||||
float cost; ///< cost (metric value), float to support soft inputs
|
|
||||||
} vCand;
|
|
||||||
|
|
||||||
/** Clear a structure. */
|
|
||||||
void clear(vCand& v)
|
|
||||||
{
|
|
||||||
v.iState=0;
|
|
||||||
v.oState=0;
|
|
||||||
v.cost=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**@name Survivors and candidates. */
|
|
||||||
//@{
|
|
||||||
vCand mSurvivors[mIStates]; ///< current survivor pool
|
|
||||||
vCand mCandidates[2*mIStates]; ///< current candidate pool
|
|
||||||
//@}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
unsigned iRate() const { return mIRate; }
|
|
||||||
uint32_t cMask() const { return mCMask; }
|
|
||||||
uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
|
|
||||||
unsigned deferral() const { return mDeferral; }
|
|
||||||
|
|
||||||
|
|
||||||
ViterbiR2O4();
|
|
||||||
|
|
||||||
/** Set all cost metrics to zero. */
|
|
||||||
void initializeStates();
|
|
||||||
|
|
||||||
/**
|
|
||||||
Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
|
|
||||||
@return reference to minimum-cost candidate.
|
|
||||||
*/
|
|
||||||
const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/** Branch survivors into new candidates. */
|
|
||||||
void branchCandidates();
|
|
||||||
|
|
||||||
/** Compute cost metrics for soft-inputs. */
|
|
||||||
void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
|
|
||||||
|
|
||||||
/** Select survivors from the candidate set. */
|
|
||||||
void pruneCandidates();
|
|
||||||
|
|
||||||
/** Find the minimum cost survivor. */
|
|
||||||
const vCand& minCost() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Precompute the state tables.
|
|
||||||
@param g Generator index 0..((1/rate)-1)
|
|
||||||
*/
|
|
||||||
void computeStateTables(unsigned g);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Precompute the generator outputs.
|
|
||||||
mCoeffs must be defined first.
|
|
||||||
*/
|
|
||||||
void computeGeneratorTable();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BitVector : public Vector<char> {
|
class BitVector : public Vector<char> {
|
||||||
|
|
||||||
|
|
||||||
@@ -282,16 +87,6 @@ class BitVector : public Vector<char> {
|
|||||||
|
|
||||||
void zero() { fill(0); }
|
void zero() { fill(0); }
|
||||||
|
|
||||||
/**@name FEC operations. */
|
|
||||||
//@{
|
|
||||||
/** Calculate the syndrome of the vector with the given Generator. */
|
|
||||||
uint64_t syndrome(Generator& gen) const;
|
|
||||||
/** Calculate the parity word for the vector with the given Generator. */
|
|
||||||
uint64_t parity(Generator& gen) const;
|
|
||||||
/** Encode the signal with the GSM rate 1/2 convolutional encoder. */
|
|
||||||
void encode(const ViterbiR2O4& encoder, BitVector& target);
|
|
||||||
//@}
|
|
||||||
|
|
||||||
|
|
||||||
/** Invert 0<->1. */
|
/** Invert 0<->1. */
|
||||||
void invert();
|
void invert();
|
||||||
@@ -427,23 +222,20 @@ class SoftVector: public Vector<float> {
|
|||||||
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
|
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
|
// How good is the SoftVector in the sense of the bits being solid?
|
||||||
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
|
// Result of 1 is perfect and 0 means all the bits were 0.0
|
||||||
|
|
||||||
// (pat) How good is the SoftVector in the sense of the bits being solid?
|
|
||||||
// Result of 1 is perfect and 0 means all the bits were 0.5
|
|
||||||
// If plow is non-NULL, also return the lowest energy bit.
|
// If plow is non-NULL, also return the lowest energy bit.
|
||||||
float getEnergy(float *low=0) const;
|
float getEnergy(float *low=0) const;
|
||||||
|
|
||||||
/** Fill with "unknown" values. */
|
/** Fill with "unknown" values. */
|
||||||
void unknown() { fill(0.5F); }
|
void unknown() { fill(0.0F); }
|
||||||
|
|
||||||
/** Return a hard bit value from a given index by slicing. */
|
/** Return a hard bit value from a given index by slicing. */
|
||||||
bool bit(size_t index) const
|
bool bit(size_t index) const
|
||||||
{
|
{
|
||||||
const float *dp = mStart+index;
|
const float *dp = mStart+index;
|
||||||
assert(dp<mEnd);
|
assert(dp<mEnd);
|
||||||
return (*dp)>0.5F;
|
return (*dp)>0.0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Slice the whole signal into bits. */
|
/** 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;
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,25 @@
|
|||||||
#include "LinkedLists.h"
|
#include "LinkedLists.h"
|
||||||
|
|
||||||
|
|
||||||
|
PointerFIFO::~PointerFIFO()
|
||||||
|
{
|
||||||
|
ListNode *node, *next;
|
||||||
|
|
||||||
|
node = mHead;
|
||||||
|
while (node != NULL) {
|
||||||
|
next = node->next();
|
||||||
|
delete node;
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = mFreeList;
|
||||||
|
while (node != NULL) {
|
||||||
|
next = node->next();
|
||||||
|
delete node;
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PointerFIFO::push_front(void* val) // by pat
|
void PointerFIFO::push_front(void* val) // by pat
|
||||||
{
|
{
|
||||||
// Pat added this routine for completeness, but never used or tested.
|
// Pat added this routine for completeness, but never used or tested.
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ class PointerFIFO {
|
|||||||
:mHead(NULL),mTail(NULL),mFreeList(NULL),
|
:mHead(NULL),mTail(NULL),mFreeList(NULL),
|
||||||
mSize(0)
|
mSize(0)
|
||||||
{}
|
{}
|
||||||
|
~PointerFIFO();
|
||||||
|
|
||||||
unsigned size() const { return mSize; }
|
unsigned size() const { return mSize; }
|
||||||
unsigned totalSize() const { return 0; } // Not used in this version.
|
unsigned totalSize() const { return 0; } // Not used in this version.
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
* Copyright (C) 2018 sysmocom - s.f.m.c. GmbH
|
||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
|
||||||
* Copyright 2011, 2012 Range Networks, Inc.
|
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* This software is distributed under the terms of the GNU Affero Public License.
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
@@ -30,276 +28,37 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <sys/time.h> // For gettimeofday
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Threads.h" // pat added
|
#include "Threads.h" // pat added
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// Switches to enable/disable logging targets
|
|
||||||
// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
|
|
||||||
bool gLogToConsole = true;
|
|
||||||
bool gLogToSyslog = false;
|
|
||||||
FILE *gLogToFile = NULL;
|
|
||||||
Mutex gLogToLock;
|
Mutex gLogToLock;
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
|
||||||
// Reference to a global config table, used all over the system.
|
|
||||||
extern ConfigurationTable gConfig;
|
|
||||||
|
|
||||||
|
|
||||||
/**@ The global alarms table. */
|
|
||||||
//@{
|
|
||||||
Mutex alarmsLock;
|
|
||||||
list<string> alarmsList;
|
|
||||||
void addAlarm(const string&);
|
|
||||||
//@}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// (pat) If Log messages are printed before the classes in this module are inited
|
|
||||||
// (which happens when static classes have constructors that do work)
|
|
||||||
// the OpenBTS just crashes.
|
|
||||||
// Prevent that by setting sLoggerInited to true when this module is inited.
|
|
||||||
static bool sLoggerInited = 0;
|
|
||||||
static struct CheckLoggerInitStatus {
|
|
||||||
CheckLoggerInitStatus() { sLoggerInited = 1; }
|
|
||||||
} sCheckloggerInitStatus;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Names of the logging levels. */
|
|
||||||
const char *levelNames[] = {
|
|
||||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
|
||||||
};
|
|
||||||
int numLevels = 8;
|
|
||||||
|
|
||||||
|
|
||||||
int levelStringToInt(const string& name)
|
|
||||||
{
|
{
|
||||||
// Reverse search, since the numerically larger levels are more common.
|
return os << ss.str();
|
||||||
for (int i=numLevels-1; i>=0; i--) {
|
|
||||||
if (name == levelNames[i]) return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common substitutions.
|
|
||||||
if (name=="INFORMATION") return 6;
|
|
||||||
if (name=="WARN") return 4;
|
|
||||||
if (name=="ERROR") return 3;
|
|
||||||
if (name=="CRITICAL") return 2;
|
|
||||||
if (name=="EMERGENCY") return 0;
|
|
||||||
|
|
||||||
// Unknown level.
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Given a string, return the corresponding level name. */
|
|
||||||
int lookupLevel(const string& key)
|
|
||||||
{
|
|
||||||
string val = gConfig.getStr(key);
|
|
||||||
int level = levelStringToInt(val);
|
|
||||||
|
|
||||||
if (level == -1) {
|
|
||||||
string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
|
|
||||||
level = levelStringToInt(defaultLevel);
|
|
||||||
_LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
|
|
||||||
gConfig.set(key, defaultLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int getLoggingLevel(const char* filename)
|
|
||||||
{
|
|
||||||
// Default level?
|
|
||||||
if (!filename) return lookupLevel("Log.Level");
|
|
||||||
|
|
||||||
// This can afford to be inefficient since it is not called that often.
|
|
||||||
const string keyName = string("Log.Level.") + string(filename);
|
|
||||||
if (gConfig.defines(keyName)) return lookupLevel(keyName);
|
|
||||||
return lookupLevel("Log.Level");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int gGetLoggingLevel(const char* filename)
|
|
||||||
{
|
|
||||||
// This is called a lot and needs to be efficient.
|
|
||||||
|
|
||||||
static Mutex sLogCacheLock;
|
|
||||||
static map<uint64_t,int> sLogCache;
|
|
||||||
static unsigned sCacheCount;
|
|
||||||
static const unsigned sCacheRefreshCount = 1000;
|
|
||||||
|
|
||||||
if (filename==NULL) return gGetLoggingLevel("");
|
|
||||||
|
|
||||||
HashString hs(filename);
|
|
||||||
uint64_t key = hs.hash();
|
|
||||||
|
|
||||||
sLogCacheLock.lock();
|
|
||||||
// Time for a cache flush?
|
|
||||||
if (sCacheCount>sCacheRefreshCount) {
|
|
||||||
sLogCache.clear();
|
|
||||||
sCacheCount=0;
|
|
||||||
}
|
|
||||||
// Is it cached already?
|
|
||||||
map<uint64_t,int>::const_iterator where = sLogCache.find(key);
|
|
||||||
sCacheCount++;
|
|
||||||
if (where!=sLogCache.end()) {
|
|
||||||
int retVal = where->second;
|
|
||||||
sLogCacheLock.unlock();
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
// Look it up in the config table and cache it.
|
|
||||||
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
|
|
||||||
// (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
|
|
||||||
sLogCacheLock.unlock();
|
|
||||||
int level = getLoggingLevel(filename);
|
|
||||||
sLogCacheLock.lock();
|
|
||||||
sLogCache.insert(pair<uint64_t,int>(key,level));
|
|
||||||
sLogCacheLock.unlock();
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// copies the alarm list and returns it. list supposed to be small.
|
|
||||||
list<string> gGetLoggerAlarms()
|
|
||||||
{
|
|
||||||
alarmsLock.lock();
|
|
||||||
list<string> ret;
|
|
||||||
// excuse the "complexity", but to use std::copy with a list you need
|
|
||||||
// an insert_iterator - copy technically overwrites, doesn't insert.
|
|
||||||
insert_iterator< list<string> > ii(ret, ret.begin());
|
|
||||||
copy(alarmsList.begin(), alarmsList.end(), ii);
|
|
||||||
alarmsLock.unlock();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Add an alarm to the alarm list. */
|
|
||||||
void addAlarm(const string& s)
|
|
||||||
{
|
|
||||||
alarmsLock.lock();
|
|
||||||
alarmsList.push_back(s);
|
|
||||||
unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
|
|
||||||
while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
|
|
||||||
alarmsLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Log::~Log()
|
Log::~Log()
|
||||||
{
|
{
|
||||||
if (mDummyInit) return;
|
int old_state;
|
||||||
// Anything at or above LOG_CRIT is an "alarm".
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
|
||||||
// Save alarms in the local list and echo them to stderr.
|
int mlen = mStream.str().size();
|
||||||
if (mPriority <= LOG_ERR) {
|
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||||
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
const char *fmt = neednl ? "%s\n" : "%s";
|
||||||
cerr << mStream.str() << endl;
|
ScopedLock lock(gLogToLock);
|
||||||
}
|
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||||
// Current logging level was already checked by the macro. So just log.
|
// so just use std::cout.
|
||||||
// Log to syslog
|
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
|
||||||
if (gLogToSyslog) {
|
pthread_setcancelstate(old_state, NULL);
|
||||||
syslog(mPriority, "%s", mStream.str().c_str());
|
|
||||||
}
|
|
||||||
// Log to file and console
|
|
||||||
if (gLogToConsole||gLogToFile) {
|
|
||||||
int mlen = mStream.str().size();
|
|
||||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
|
||||||
ScopedLock lock(gLogToLock);
|
|
||||||
if (gLogToConsole) {
|
|
||||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
|
||||||
// so just use std::cout.
|
|
||||||
std::cout << mStream.str();
|
|
||||||
if (neednl) std::cout<<"\n";
|
|
||||||
}
|
|
||||||
if (gLogToFile) {
|
|
||||||
fputs(mStream.str().c_str(),gLogToFile);
|
|
||||||
if (neednl) {fputc('\n',gLogToFile);}
|
|
||||||
fflush(gLogToFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Log::Log(const char* name, const char* level, int facility)
|
|
||||||
{
|
|
||||||
mDummyInit = true;
|
|
||||||
gLogInit(name, level, facility);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ostringstream& Log::get()
|
ostringstream& Log::get()
|
||||||
{
|
{
|
||||||
assert(mPriority<numLevels);
|
|
||||||
mStream << levelNames[mPriority] << ' ';
|
|
||||||
return mStream;
|
return mStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void gLogInit(const char* name, const char* level, int facility)
|
|
||||||
{
|
|
||||||
// Set the level if one has been specified.
|
|
||||||
if (level) {
|
|
||||||
gConfig.set("Log.Level",level);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
|
|
||||||
string str = gConfig.getStr("Log.File");
|
|
||||||
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
|
||||||
const char *fn = str.c_str();
|
|
||||||
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
|
|
||||||
gLogToFile = fopen(fn,"w"); // New log file each time we start.
|
|
||||||
if (gLogToFile) {
|
|
||||||
time_t now;
|
|
||||||
time(&now);
|
|
||||||
fprintf(gLogToFile,"Starting at %s",ctime(&now));
|
|
||||||
fflush(gLogToFile);
|
|
||||||
std::cout << "Logging to file: " << fn << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the log connection.
|
|
||||||
openlog(name,0,facility);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void gLogEarly(int level, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start(args, fmt);
|
|
||||||
|
|
||||||
if (gLogToSyslog) {
|
|
||||||
va_list args_copy;
|
|
||||||
va_copy(args_copy, args);
|
|
||||||
vsyslog(level | LOG_USER, fmt, args_copy);
|
|
||||||
va_end(args_copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gLogToConsole) {
|
|
||||||
va_list args_copy;
|
|
||||||
va_copy(args_copy, args);
|
|
||||||
vprintf(fmt, args_copy);
|
|
||||||
printf("\n");
|
|
||||||
va_end(args_copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gLogToFile) {
|
|
||||||
va_list args_copy;
|
|
||||||
va_copy(args_copy, args);
|
|
||||||
vfprintf(gLogToFile, fmt, args_copy);
|
|
||||||
fprintf(gLogToFile, "\n");
|
|
||||||
va_end(args_copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: ts=4 sw=4
|
// vim: ts=4 sw=4
|
||||||
|
|||||||
@@ -23,72 +23,44 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// (pat) WARNING is stupidly defined in /usr/local/include/osipparser2/osip_const.h.
|
|
||||||
// This must be outside the #ifndef LOGGER_H to fix it as long as Logger.h included after the above file.
|
|
||||||
#ifdef WARNING
|
|
||||||
#undef WARNING
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LOGGER_H
|
#ifndef LOGGER_H
|
||||||
#define LOGGER_H
|
#define LOGGER_H
|
||||||
|
|
||||||
#include <syslog.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#define _LOG(level) \
|
extern "C" {
|
||||||
Log(LOG_##level).get() << pthread_self() \
|
#include <osmocom/core/logging.h>
|
||||||
<< timestr() << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
|
#include "debug.h"
|
||||||
|
}
|
||||||
|
|
||||||
#define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel)
|
/* Translation for old log statements */
|
||||||
|
#ifndef LOGL_ALERT
|
||||||
#ifdef NDEBUG
|
#define LOGL_ALERT LOGL_FATAL
|
||||||
#define LOG(wLevel) \
|
#endif
|
||||||
if (LOG_##wLevel!=LOG_DEBUG && IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
|
#ifndef LOGL_ERR
|
||||||
#else
|
#define LOGL_ERR LOGL_ERROR
|
||||||
#define LOG(wLevel) \
|
#endif
|
||||||
if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
|
#ifndef LOGL_WARNING
|
||||||
|
#define LOGL_WARNING LOGL_NOTICE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// pat: And for your edification here are the 'levels' as defined in syslog.h:
|
#define LOG(level) \
|
||||||
// LOG_EMERG 0 system is unusable
|
Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
|
||||||
// LOG_ALERT 1 action must be taken immediately
|
|
||||||
// LOG_CRIT 2 critical conditions
|
|
||||||
// LOG_ERR 3 error conditions
|
|
||||||
// LOG_WARNING 4 warning conditions
|
|
||||||
// LOG_NOTICE 5 normal, but significant, condition
|
|
||||||
// LOG_INFO 6 informational message
|
|
||||||
// LOG_DEBUG 7 debug-level message
|
|
||||||
|
|
||||||
// (pat) added - print out a var and its name.
|
#define LOGC(category, level) \
|
||||||
// Use like this: int descriptive_name; LOG(INFO)<<LOGVAR(descriptive_name);
|
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
|
||||||
#define LOGVAR2(name,val) " " << name << "=" << (val)
|
|
||||||
#define LOGVAR(var) (" " #var "=") << var
|
|
||||||
#define LOGHEX(var) (" " #var "=0x") << hex << ((unsigned)var) << dec
|
|
||||||
#define LOGHEX2(name,val) " " << name << "=0x" << hex << ((unsigned)(val)) << dec
|
|
||||||
// These are kind of cheesy, but you can use for bitvector
|
|
||||||
#define LOGBV2(name,val) " " << name << "=(" << val<<" size:"<<val.size()<<")"
|
|
||||||
#define LOGBV(bv) LOGBV2(#bv,bv)
|
|
||||||
#define LOGVARRANGE(name,cur,lo,hi) " "<<name <<"=("<<(cur) << " range:"<<(lo) << " to "<<(hi) <<")"
|
|
||||||
|
|
||||||
|
#define LOGLV(category, level) \
|
||||||
|
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
|
||||||
|
|
||||||
#define OBJLOG(wLevel) \
|
#define LOGCHAN(chan, category, level) \
|
||||||
LOG(wLevel) << "obj: " << this << ' '
|
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "][chan=" << chan << "] "
|
||||||
|
|
||||||
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
|
|
||||||
|
|
||||||
|
|
||||||
#include "Threads.h" // must be after defines above, if these files are to be allowed to use LOG()
|
|
||||||
#include "Utils.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A C++ stream-based thread-safe logger.
|
A C++ stream-based thread-safe logger.
|
||||||
Derived from Dr. Dobb's Sept. 2007 issue.
|
|
||||||
Updated to use syslog.
|
|
||||||
This object is NOT the global logger;
|
This object is NOT the global logger;
|
||||||
every log record is an object of this class.
|
every log record is an object of this class.
|
||||||
*/
|
*/
|
||||||
@@ -98,42 +70,27 @@ class Log {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
std::ostringstream mStream; ///< This is where we buffer up the log entry.
|
std::ostringstream mStream; ///< This is where we buffer up the log entry.
|
||||||
int mPriority; ///< Priority of current report.
|
int mCategory; ///< Priority of current report.
|
||||||
bool mDummyInit;
|
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:
|
public:
|
||||||
|
|
||||||
Log(int wPriority)
|
Log(int wCategory, int wPriority, const char* filename, int line)
|
||||||
:mPriority(wPriority), mDummyInit(false)
|
: 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.
|
// Most of the work is in the destructor.
|
||||||
/** The destructor actually generates the log entry. */
|
/** The destructor actually generates the log entry. */
|
||||||
~Log();
|
~Log();
|
||||||
|
|
||||||
std::ostringstream& get();
|
std::ostringstream& get();
|
||||||
};
|
};
|
||||||
extern bool gLogToConsole; // Output log messages to stdout
|
|
||||||
extern bool gLogToSyslog; // Output log messages to syslog
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::list<std::string> gGetLoggerAlarms(); ///< Get a copy of the recent alarm list.
|
|
||||||
|
|
||||||
|
|
||||||
/**@ Global control and initialization of the logging system. */
|
|
||||||
//@{
|
|
||||||
/** Initialize the global logging system. */
|
|
||||||
void gLogInit(const char* name, const char* level=NULL, int facility=LOG_USER);
|
|
||||||
/** Get the logging level associated with a given file. */
|
|
||||||
int gGetLoggingLevel(const char *filename=NULL);
|
|
||||||
/** Allow early logging when still in constructors */
|
|
||||||
void gLogEarly(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
|
||||||
//@}
|
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,8 @@
|
|||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||||
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
|
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||||
|
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||||
EXTRA_DIST = \
|
|
||||||
example.config \
|
|
||||||
README.common
|
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libcommon.la
|
noinst_LTLIBRARIES = libcommon.la
|
||||||
|
|
||||||
@@ -36,78 +33,24 @@ libcommon_la_SOURCES = \
|
|||||||
Sockets.cpp \
|
Sockets.cpp \
|
||||||
Threads.cpp \
|
Threads.cpp \
|
||||||
Timeval.cpp \
|
Timeval.cpp \
|
||||||
Reporting.cpp \
|
|
||||||
Logger.cpp \
|
Logger.cpp \
|
||||||
Configuration.cpp \
|
Utils.cpp \
|
||||||
sqlite3util.cpp \
|
trx_vty.c \
|
||||||
URLEncode.cpp \
|
debug.c
|
||||||
Utils.cpp
|
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
|
||||||
BitVectorTest \
|
|
||||||
InterthreadTest \
|
|
||||||
SocketsTest \
|
|
||||||
TimevalTest \
|
|
||||||
RegexpTest \
|
|
||||||
VectorTest \
|
|
||||||
ConfigurationTest \
|
|
||||||
LogTest \
|
|
||||||
URLEncodeTest \
|
|
||||||
F16Test
|
|
||||||
|
|
||||||
# ReportingTest
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
BitVector.h \
|
BitVector.h \
|
||||||
|
PRBS.h \
|
||||||
Interthread.h \
|
Interthread.h \
|
||||||
LinkedLists.h \
|
LinkedLists.h \
|
||||||
Sockets.h \
|
Sockets.h \
|
||||||
Threads.h \
|
Threads.h \
|
||||||
Timeval.h \
|
Timeval.h \
|
||||||
Regexp.h \
|
|
||||||
Vector.h \
|
Vector.h \
|
||||||
Configuration.h \
|
|
||||||
Reporting.h \
|
|
||||||
F16.h \
|
|
||||||
URLEncode.h \
|
|
||||||
Utils.h \
|
|
||||||
Logger.h \
|
Logger.h \
|
||||||
sqlite3util.h
|
Utils.h \
|
||||||
|
trx_vty.h \
|
||||||
URLEncodeTest_SOURCES = URLEncodeTest.cpp
|
debug.h \
|
||||||
URLEncodeTest_LDADD = libcommon.la
|
osmo_signal.h \
|
||||||
|
config_defs.h
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
return send(dest,message,length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int DatagramSocket::read(char* buffer, size_t length)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int DatagramSocket::read(char* buffer)
|
|
||||||
{
|
{
|
||||||
socklen_t temp_len = sizeof(mSource);
|
socklen_t addr_len = sizeof(mSource);
|
||||||
int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0,
|
int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
|
||||||
(struct sockaddr*)&mSource,&temp_len);
|
(struct sockaddr*) &mSource, &addr_len);
|
||||||
if ((length==-1) && (errno!=EAGAIN)) {
|
|
||||||
|
if ((rd_length==-1) && (errno!=EAGAIN)) {
|
||||||
perror("DatagramSocket::read() failed");
|
perror("DatagramSocket::read() failed");
|
||||||
throw SocketError();
|
throw SocketError();
|
||||||
}
|
}
|
||||||
return length;
|
return rd_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
|
||||||
int DatagramSocket::read(char* buffer, unsigned timeout)
|
|
||||||
{
|
{
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
@@ -218,7 +214,7 @@ int DatagramSocket::read(char* buffer, unsigned timeout)
|
|||||||
throw SocketError();
|
throw SocketError();
|
||||||
}
|
}
|
||||||
if (sel==0) return -1;
|
if (sel==0) return -1;
|
||||||
if (FD_ISSET(mSocketFD,&fds)) return read(buffer);
|
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
|
||||||
return -1;
|
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()
|
:DatagramSocket()
|
||||||
{
|
{
|
||||||
open(wSrcPort);
|
open(wSrcPort, wSrcIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UDPSocket::UDPSocket(unsigned short wSrcPort,
|
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
|
||||||
const char * wDestIP, unsigned short wDestPort )
|
const char *wDestIP, unsigned short wDestPort)
|
||||||
:DatagramSocket()
|
:DatagramSocket()
|
||||||
{
|
{
|
||||||
open(wSrcPort);
|
open(wSrcPort, wSrcIP);
|
||||||
destination(wDestPort, wDestIP);
|
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
|
// create
|
||||||
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
||||||
@@ -269,7 +265,7 @@ void UDPSocket::open(unsigned short localPort)
|
|||||||
size_t length = sizeof(address);
|
size_t length = sizeof(address);
|
||||||
bzero(&address,length);
|
bzero(&address,length);
|
||||||
address.sin_family = AF_INET;
|
address.sin_family = AF_INET;
|
||||||
address.sin_addr.s_addr = INADDR_ANY;
|
address.sin_addr.s_addr = inet_addr(wlocalIP);
|
||||||
address.sin_port = htons(localPort);
|
address.sin_port = htons(localPort);
|
||||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||||
perror("bind() failed");
|
perror("bind() failed");
|
||||||
@@ -288,50 +284,4 @@ unsigned short UDPSocket::port() const
|
|||||||
return ntohs(name.sin_port);
|
return ntohs(name.sin_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UDDSocket::UDDSocket(const char* localPath, const char* remotePath)
|
|
||||||
:DatagramSocket()
|
|
||||||
{
|
|
||||||
if (localPath!=NULL) open(localPath);
|
|
||||||
if (remotePath!=NULL) destination(remotePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void UDDSocket::open(const char* localPath)
|
|
||||||
{
|
|
||||||
// create
|
|
||||||
mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0);
|
|
||||||
if (mSocketFD<0) {
|
|
||||||
perror("socket() failed");
|
|
||||||
throw SocketError();
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind
|
|
||||||
struct sockaddr_un address;
|
|
||||||
size_t length = sizeof(address);
|
|
||||||
bzero(&address,length);
|
|
||||||
address.sun_family = AF_UNIX;
|
|
||||||
strcpy(address.sun_path,localPath);
|
|
||||||
unlink(localPath);
|
|
||||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
|
||||||
perror("bind() failed");
|
|
||||||
throw SocketError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void UDDSocket::destination(const char* remotePath)
|
|
||||||
{
|
|
||||||
struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination;
|
|
||||||
strcpy(unAddr->sun_path,remotePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// vim:ts=4:sw=4
|
// vim:ts=4:sw=4
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public:
|
|||||||
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
|
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
|
||||||
@return The number of bytes received or -1 on non-blocking pass.
|
@return The number of bytes received or -1 on non-blocking pass.
|
||||||
*/
|
*/
|
||||||
int read(char* buffer);
|
int read(char* buffer, size_t length);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Receive a packet with a timeout.
|
Receive a packet with a timeout.
|
||||||
@@ -116,7 +116,7 @@ public:
|
|||||||
@param maximum wait time in milliseconds
|
@param maximum wait time in milliseconds
|
||||||
@return The number of bytes received or -1 on timeout.
|
@return The number of bytes received or -1 on timeout.
|
||||||
*/
|
*/
|
||||||
int read(char* buffer, unsigned timeout);
|
int read(char* buffer, size_t length, unsigned timeout);
|
||||||
|
|
||||||
|
|
||||||
/** Send a packet to a given destination, other than the default. */
|
/** Send a packet to a given destination, other than the default. */
|
||||||
@@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** Open a USP socket with an OS-assigned port and no default destination. */
|
/** Open a USP socket with an OS-assigned port and no default destination. */
|
||||||
UDPSocket( unsigned short localPort=0);
|
UDPSocket(const char *localIP, unsigned short localPort);
|
||||||
|
|
||||||
/** Given a full specification, open the socket and set the dest address. */
|
/** Given a full specification, open the socket and set the dest address. */
|
||||||
UDPSocket( unsigned short localPort,
|
UDPSocket(const char *localIP, unsigned short localPort,
|
||||||
const char * remoteIP, unsigned short remotePort);
|
const char *remoteIP, unsigned short remotePort);
|
||||||
|
|
||||||
/** Set the destination port. */
|
/** Set the destination port. */
|
||||||
void destination( unsigned short wDestPort, const char * wDestIP );
|
void destination( unsigned short wDestPort, const char * wDestIP );
|
||||||
@@ -157,7 +157,7 @@ public:
|
|||||||
unsigned short port() const;
|
unsigned short port() const;
|
||||||
|
|
||||||
/** Open and bind the UDP socket to a local port. */
|
/** Open and bind the UDP socket to a local port. */
|
||||||
void open(unsigned short localPort=0);
|
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
|
||||||
|
|
||||||
/** Give the return address of the most recently received packet. */
|
/** Give the return address of the most recently received packet. */
|
||||||
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
||||||
@@ -166,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
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "Threads.h"
|
#include "Threads.h"
|
||||||
#include "Timeval.h"
|
#include "Timeval.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
|
#ifndef gettid
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#define gettid() syscall(SYS_gettid)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -102,6 +108,25 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const
|
|||||||
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
|
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_selfthread_name(const char *name)
|
||||||
|
{
|
||||||
|
pthread_t selfid = pthread_self();
|
||||||
|
pid_t tid = gettid();
|
||||||
|
if (pthread_setname_np(selfid, name) == 0) {
|
||||||
|
LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
|
||||||
|
} else {
|
||||||
|
char buf[256];
|
||||||
|
int err = errno;
|
||||||
|
char* err_str = strerror_r(err, buf, sizeof(buf));
|
||||||
|
LOG(NOTICE) << "Thread "<< selfid << " (task " << tid << ") set name \"" << name << "\" failed: (" << err << ") " << err_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_enable_cancel(bool cancel)
|
||||||
|
{
|
||||||
|
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void Thread::start(void *(*task)(void*), void *arg)
|
void Thread::start(void *(*task)(void*), void *arg)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ void unlockCout(); ///< call after writing cout
|
|||||||
#define OBJDCOUT(text) {}
|
#define OBJDCOUT(text) {}
|
||||||
#else
|
#else
|
||||||
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
|
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
|
||||||
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
|
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
|
||||||
#endif
|
#endif
|
||||||
//@}
|
//@}
|
||||||
//@}
|
//@}
|
||||||
@@ -141,6 +141,9 @@ class Signal {
|
|||||||
#define START_THREAD(thread,function,argument) \
|
#define START_THREAD(thread,function,argument) \
|
||||||
thread.start((void *(*)(void*))function, (void*)argument);
|
thread.start((void *(*)(void*))function, (void*)argument);
|
||||||
|
|
||||||
|
void set_selfthread_name(const char *name);
|
||||||
|
void thread_enable_cancel(bool cancel);
|
||||||
|
|
||||||
/** A C++ wrapper for pthread threads. */
|
/** A C++ wrapper for pthread threads. */
|
||||||
class Thread {
|
class Thread {
|
||||||
|
|
||||||
@@ -150,7 +153,7 @@ class Thread {
|
|||||||
pthread_attr_t mAttrib;
|
pthread_attr_t mAttrib;
|
||||||
// FIXME -- Can this be reduced now?
|
// FIXME -- Can this be reduced now?
|
||||||
size_t mStackSize;
|
size_t mStackSize;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@@ -27,43 +27,49 @@
|
|||||||
|
|
||||||
#include "Timeval.h"
|
#include "Timeval.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/timer.h>
|
||||||
|
}
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
void Timeval::now()
|
||||||
|
{
|
||||||
|
osmo_clock_gettime(CLOCK_REALTIME, &mTimespec);
|
||||||
|
}
|
||||||
|
|
||||||
void Timeval::future(unsigned offset)
|
void Timeval::future(unsigned offset)
|
||||||
{
|
{
|
||||||
now();
|
now();
|
||||||
unsigned sec = offset/1000;
|
unsigned sec = offset/1000;
|
||||||
unsigned msec = offset%1000;
|
unsigned msec = offset%1000;
|
||||||
mTimeval.tv_usec += msec*1000;
|
mTimespec.tv_nsec += msec*1000*1000;
|
||||||
mTimeval.tv_sec += sec;
|
mTimespec.tv_sec += sec;
|
||||||
if (mTimeval.tv_usec>1000000) {
|
if (mTimespec.tv_nsec > 1000*1000*1000) {
|
||||||
mTimeval.tv_usec -= 1000000;
|
mTimespec.tv_nsec -= 1000*1000*1000;
|
||||||
mTimeval.tv_sec += 1;
|
mTimespec.tv_sec += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct timespec Timeval::timespec() const
|
struct timespec Timeval::timespec() const
|
||||||
{
|
{
|
||||||
struct timespec retVal;
|
return mTimespec;
|
||||||
retVal.tv_sec = mTimeval.tv_sec;
|
|
||||||
retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Timeval::passed() const
|
bool Timeval::passed() const
|
||||||
{
|
{
|
||||||
Timeval nowTime;
|
Timeval nowTime;
|
||||||
if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
|
if (nowTime.mTimespec.tv_sec < mTimespec.tv_sec) return false;
|
||||||
if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
|
if (nowTime.mTimespec.tv_sec > mTimespec.tv_sec) return true;
|
||||||
if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
|
if (nowTime.mTimespec.tv_nsec >= mTimespec.tv_nsec) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
double Timeval::seconds() const
|
double Timeval::seconds() const
|
||||||
{
|
{
|
||||||
return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
|
return ((double)mTimespec.tv_sec) + 1e-9*((double)mTimespec.tv_nsec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -72,8 +78,8 @@ long Timeval::delta(const Timeval& other) const
|
|||||||
{
|
{
|
||||||
// 2^31 milliseconds is just over 4 years.
|
// 2^31 milliseconds is just over 4 years.
|
||||||
int32_t deltaS = other.sec() - sec();
|
int32_t deltaS = other.sec() - sec();
|
||||||
int32_t deltaUs = other.usec() - usec();
|
int32_t deltaNs = other.nsec() - nsec();
|
||||||
return 1000*deltaS + deltaUs/1000;
|
return 1000*deltaS + deltaNs/1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -89,7 +95,7 @@ ostream& operator<<(ostream& os, const Timeval& tv)
|
|||||||
|
|
||||||
ostream& operator<<(ostream& os, const struct timespec& ts)
|
ostream& operator<<(ostream& os, const struct timespec& ts)
|
||||||
{
|
{
|
||||||
os << ts.tv_sec << "," << ts.tv_nsec;
|
os << ts.tv_sec << "," << ts.tv_nsec/1000;
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ class Timeval {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct timeval mTimeval;
|
struct timespec mTimespec;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Set the value to gettimeofday. */
|
/** Set the value to current time. */
|
||||||
void now() { gettimeofday(&mTimeval,NULL); }
|
void now();
|
||||||
|
|
||||||
/** Set the value to gettimeofday plus an offset. */
|
/** Set the value to gettimeofday plus an offset. */
|
||||||
void future(unsigned ms);
|
void future(unsigned ms);
|
||||||
@@ -55,16 +55,18 @@ class Timeval {
|
|||||||
//@{
|
//@{
|
||||||
Timeval(unsigned sec, unsigned usec)
|
Timeval(unsigned sec, unsigned usec)
|
||||||
{
|
{
|
||||||
mTimeval.tv_sec = sec;
|
mTimespec.tv_sec = sec;
|
||||||
mTimeval.tv_usec = usec;
|
mTimespec.tv_nsec = usec*1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timeval(const struct timeval& wTimeval)
|
Timeval(const struct timeval& wTimeval)
|
||||||
:mTimeval(wTimeval)
|
{
|
||||||
{}
|
mTimespec.tv_sec = wTimeval.tv_sec;
|
||||||
|
mTimespec.tv_nsec = wTimeval.tv_sec*1000;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Create a Timeval offset into the future.
|
Create a Timespec offset into the future.
|
||||||
@param offset milliseconds
|
@param offset milliseconds
|
||||||
*/
|
*/
|
||||||
Timeval(unsigned offset=0) { future(offset); }
|
Timeval(unsigned offset=0) { future(offset); }
|
||||||
@@ -76,8 +78,9 @@ class Timeval {
|
|||||||
/** Return total seconds. */
|
/** Return total seconds. */
|
||||||
double seconds() const;
|
double seconds() const;
|
||||||
|
|
||||||
uint32_t sec() const { return mTimeval.tv_sec; }
|
uint32_t sec() const { return mTimespec.tv_sec; }
|
||||||
uint32_t usec() const { return mTimeval.tv_usec; }
|
uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
|
||||||
|
uint32_t nsec() const { return mTimespec.tv_nsec; }
|
||||||
|
|
||||||
/** Return differnce from other (other-self), in ms. */
|
/** Return differnce from other (other-self), in ms. */
|
||||||
long delta(const Timeval& other) const;
|
long delta(const Timeval& other) const;
|
||||||
@@ -88,11 +91,11 @@ class Timeval {
|
|||||||
/** Remaining time in ms. */
|
/** Remaining time in ms. */
|
||||||
long remaining() const { return -elapsed(); }
|
long remaining() const { return -elapsed(); }
|
||||||
|
|
||||||
/** Return true if the time has passed, as per gettimeofday. */
|
/** Return true if the time has passed, as per clock_gettime(CLOCK_REALTIME). */
|
||||||
bool passed() const;
|
bool passed() const;
|
||||||
|
|
||||||
/** Add a given number of minutes to the time. */
|
/** Add a given number of minutes to the time. */
|
||||||
void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; }
|
void addMinutes(unsigned minutes) { mTimespec.tv_sec += minutes*60; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2008 Free Software Foundation, Inc.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* This software is distributed under the terms of the GNU Affero Public License.
|
|
||||||
* See the COPYING file in the main directory for details.
|
|
||||||
*
|
|
||||||
* This use of this software may be subject to additional restrictions.
|
|
||||||
* See the LEGAL file in the main directory for details.
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "Timeval.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
|
|
||||||
Timeval then(10000);
|
|
||||||
cout << then.elapsed() << endl;
|
|
||||||
|
|
||||||
while (!then.passed()) {
|
|
||||||
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
|
|
||||||
usleep(500000);
|
|
||||||
}
|
|
||||||
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
|
|
||||||
}
|
|
||||||
@@ -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 +1,36 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011 Range Networks, Inc.
|
* Copyright 2018 sysmocom - s.f.m.c. GmbH
|
||||||
* All Rights Reserved.
|
*
|
||||||
*
|
* This library is free software; you can redistribute it and/or
|
||||||
* This software is distributed under multiple licenses;
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* see the COPYING file in the main directory for licensing
|
* License as published by the Free Software Foundation; either
|
||||||
* information for this specific distribuion.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This use of this software may be subject to additional restrictions.
|
* This library is distributed in the hope that it will be useful,
|
||||||
* See the LEGAL file in the main directory for details.
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
#include <vector>
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
#include <string>
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
#include <sstream>
|
||||||
*/
|
|
||||||
|
|
||||||
#include <unistd.h> // For usleep
|
std::vector<std::string> comma_delimited_to_vector(const char* opt)
|
||||||
#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));
|
std::string str = std::string(opt);
|
||||||
memset(mMemTotal,0,sizeof(mMemTotal));
|
std::vector<std::string> result;
|
||||||
memset(mMemName,0,sizeof(mMemName));
|
std::stringstream ss(str);
|
||||||
}
|
|
||||||
|
|
||||||
void MemStats::text(std::ostream &os)
|
while( ss.good() )
|
||||||
{
|
{
|
||||||
os << "Structs current total:\n";
|
std::string substr;
|
||||||
for (int i = 0; i < mMax; i++) {
|
getline(ss, substr, ',');
|
||||||
os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n";
|
result.push_back(substr);
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +1,24 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011 Range Networks, Inc.
|
* Copyright 2018 sysmocom - s.f.m.c. GmbH
|
||||||
* All Rights Reserved.
|
*
|
||||||
*
|
* This library is free software; you can redistribute it and/or
|
||||||
* This software is distributed under multiple licenses;
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* see the COPYING file in the main directory for licensing
|
* License as published by the Free Software Foundation; either
|
||||||
* information for this specific distribuion.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This use of this software may be subject to additional restrictions.
|
* This library is distributed in the hope that it will be useful,
|
||||||
* See the LEGAL file in the main directory for details.
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
#pragma once
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GPRSUTILS_H
|
#include <vector>
|
||||||
#define GPRSUTILS_H
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string.h>
|
|
||||||
#include <math.h> // for sqrtf
|
|
||||||
#include "Logger.h"
|
|
||||||
|
|
||||||
|
std::vector<std::string> comma_delimited_to_vector(const char* opt);
|
||||||
namespace Utils {
|
|
||||||
|
|
||||||
extern double timef(); // high resolution time
|
|
||||||
extern const std::string timestr(); // A timestamp to print in messages.
|
|
||||||
extern void sleepf(double howlong); // high resolution sleep
|
|
||||||
extern int gcd(int x, int y);
|
|
||||||
|
|
||||||
// It is irritating to create a string just to interface to the brain-damaged
|
|
||||||
// C++ stream class, but this is only used for debug messages.
|
|
||||||
std::string format(const char *fmt, ...) __attribute__((format (printf,1,2)));
|
|
||||||
|
|
||||||
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars=NULL);
|
|
||||||
|
|
||||||
// For classes with a text() function, provide a function to return a String,
|
|
||||||
// and also a standard << stream function that takes a pointer to the object.
|
|
||||||
// We dont provide the function that takes a reference to the object
|
|
||||||
// because it is too highly overloaded and generally doesnt work.
|
|
||||||
class Text2Str {
|
|
||||||
public:
|
|
||||||
virtual void text(std::ostream &os) const = 0;
|
|
||||||
std::string str() const;
|
|
||||||
};
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Text2Str *val);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Generic Activity Timer. Lots of controls to make everybody happy.
|
|
||||||
class ATimer {
|
|
||||||
double mStart;
|
|
||||||
//bool mActive;
|
|
||||||
double mLimitTime;
|
|
||||||
public:
|
|
||||||
ATimer() : mStart(0), mLimitTime(0) { }
|
|
||||||
ATimer(double wLimitTime) : mStart(0), mLimitTime(wLimitTime) { }
|
|
||||||
void start() { mStart=timef(); }
|
|
||||||
void stop() { mStart=0; }
|
|
||||||
bool active() { return !!mStart; }
|
|
||||||
double elapsed() { return timef() - mStart; }
|
|
||||||
bool expired() { return elapsed() > mLimitTime; }
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
struct BitSet {
|
|
||||||
unsigned mBits;
|
|
||||||
void setBit(unsigned whichbit) { mBits |= 1<<whichbit; }
|
|
||||||
void clearBit(unsigned whichbit) { mBits &= ~(1<<whichbit); }
|
|
||||||
unsigned getBit(unsigned whichbit) const { return mBits & (1<<whichbit); }
|
|
||||||
bool isSet(unsigned whichbit) const { return mBits & (1<<whichbit); }
|
|
||||||
unsigned bits() const { return mBits; }
|
|
||||||
operator int(void) const { return mBits; }
|
|
||||||
BitSet() { mBits = 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Store current, min, max and compute running average and standard deviation.
|
|
||||||
template<class Type> struct Statistic {
|
|
||||||
Type mCurrent, mMin, mMax; // min,max optional initialization so you can print before adding any values.
|
|
||||||
unsigned mCnt;
|
|
||||||
double mSum;
|
|
||||||
//double mSum2; // sum of squares.
|
|
||||||
// (Type) cast needed in case Type is an enum, stupid language.
|
|
||||||
Statistic() : mCurrent((Type)0), mMin((Type)0), mMax((Type)0), mCnt(0), mSum(0) /*,mSum2(0)*/ {}
|
|
||||||
// Set the current value and add a statisical point.
|
|
||||||
void addPoint(Type val) {
|
|
||||||
mCurrent = val;
|
|
||||||
if (mCnt == 0 || val < mMin) {mMin = val;}
|
|
||||||
if (mCnt == 0 || val > mMax) {mMax = val;}
|
|
||||||
mCnt++;
|
|
||||||
mSum += val;
|
|
||||||
//mSum2 += val * val;
|
|
||||||
}
|
|
||||||
Type getCurrent() const { // Return current value.
|
|
||||||
return mCnt ? mCurrent : 0;
|
|
||||||
}
|
|
||||||
double getAvg() const { // Return average.
|
|
||||||
return mCnt==0 ? 0 : mSum/mCnt;
|
|
||||||
};
|
|
||||||
//float getSD() const { // Return standard deviation. Use low precision square root function.
|
|
||||||
// return mCnt==0 ? 0 : sqrtf(mCnt * mSum2 - mSum*mSum) / mCnt;
|
|
||||||
//}
|
|
||||||
|
|
||||||
void text(std::ostream &os) const { // Print everything in parens.
|
|
||||||
os << "("<<mCurrent;
|
|
||||||
if (mMin != mMax) { // Not point in printing all this stuff if min == max.
|
|
||||||
os <<LOGVAR2("min",mMin)<<LOGVAR2("max",mMax)<<LOGVAR2("avg",getAvg());
|
|
||||||
if (mCnt <= 999999) {
|
|
||||||
os <<LOGVAR2("N",mCnt);
|
|
||||||
} else { // Shorten this up:
|
|
||||||
char buf[10], *ep;
|
|
||||||
sprintf(buf,"%.3g",round(mCnt));
|
|
||||||
if ((ep = strchr(buf,'e')) && ep[1] == '+') { strcpy(ep+1,ep+2); }
|
|
||||||
os << LOGVAR2("N",buf);
|
|
||||||
}
|
|
||||||
// os<<LOGVAR2("sd",getSD()) standard deviation not interesting
|
|
||||||
}
|
|
||||||
os << ")";
|
|
||||||
// " min="<<mMin <<" max="<<mMax <<format(" avg=%4g sd=%3g)",getAvg(),getSD());
|
|
||||||
}
|
|
||||||
// Not sure if this works:
|
|
||||||
//std::string statStr() const {
|
|
||||||
// return (std::string)mCurrent + " min=" + (std::string) mMin +" max="+(string)mMax+ format(" avg=%4g sd=%3g",getAvg(),getSD());
|
|
||||||
//}
|
|
||||||
};
|
|
||||||
|
|
||||||
// This I/O mechanism is so dumb:
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat);
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat);
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat);
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat);
|
|
||||||
|
|
||||||
|
|
||||||
// Yes, they botched and left this out:
|
|
||||||
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
|
|
||||||
|
|
||||||
std::ostream &osprintf(std::ostream &os, const char *fmt, ...) __attribute__((format (printf,2,3)));
|
|
||||||
|
|
||||||
std::string replaceAll(const std::string input, const std::string search, const std::string replace);
|
|
||||||
|
|
||||||
}; // namespace
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -32,11 +32,14 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
// We cant use Logger.h in this file...
|
// We cant use Logger.h in this file...
|
||||||
extern int gVectorDebug;
|
extern int gVectorDebug;
|
||||||
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
|
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
|
||||||
|
|
||||||
|
typedef void (*vector_free_func)(void* wData);
|
||||||
|
typedef void *(*vector_alloc_func)(size_t newSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A simplified Vector template with aliases.
|
A simplified Vector template with aliases.
|
||||||
@@ -60,6 +63,8 @@ template <class T> class Vector {
|
|||||||
T* mData; ///< allocated data block, if any
|
T* mData; ///< allocated data block, if any
|
||||||
T* mStart; ///< start of useful data
|
T* mStart; ///< start of useful data
|
||||||
T* mEnd; ///< end of useful data + 1
|
T* mEnd; ///< end of useful data + 1
|
||||||
|
vector_alloc_func mAllocFunc; ///< function used to alloc new mData during resize.
|
||||||
|
vector_free_func mFreeFunc; ///< function used to free mData.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -85,13 +90,30 @@ template <class T> class Vector {
|
|||||||
/** Change the size of the Vector, discarding content. */
|
/** Change the size of the Vector, discarding content. */
|
||||||
void resize(size_t newSize)
|
void resize(size_t newSize)
|
||||||
{
|
{
|
||||||
if (mData!=NULL) delete[] mData;
|
if (mData!=NULL) {
|
||||||
|
if (mFreeFunc)
|
||||||
|
mFreeFunc(mData);
|
||||||
|
else
|
||||||
|
delete[] mData;
|
||||||
|
}
|
||||||
if (newSize==0) mData=NULL;
|
if (newSize==0) mData=NULL;
|
||||||
else mData = new T[newSize];
|
else {
|
||||||
|
if (mAllocFunc)
|
||||||
|
mData = (T*) mAllocFunc(newSize);
|
||||||
|
else
|
||||||
|
mData = new T[newSize];
|
||||||
|
}
|
||||||
mStart = mData;
|
mStart = mData;
|
||||||
mEnd = mStart + newSize;
|
mEnd = mStart + newSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Reduce addressable size of the Vector, keeping content. */
|
||||||
|
void shrink(size_t newSize)
|
||||||
|
{
|
||||||
|
assert(newSize <= mEnd - mStart);
|
||||||
|
mEnd = mStart + newSize;
|
||||||
|
}
|
||||||
|
|
||||||
/** Release memory and clear pointers. */
|
/** Release memory and clear pointers. */
|
||||||
void clear() { resize(0); }
|
void clear() { resize(0); }
|
||||||
|
|
||||||
@@ -100,7 +122,7 @@ template <class T> class Vector {
|
|||||||
void clone(const Vector<T>& other)
|
void clone(const Vector<T>& other)
|
||||||
{
|
{
|
||||||
resize(other.size());
|
resize(other.size());
|
||||||
memcpy(mData,other.mStart,other.bytes());
|
other.copyTo(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -109,29 +131,31 @@ template <class T> class Vector {
|
|||||||
//@{
|
//@{
|
||||||
|
|
||||||
/** Build an empty Vector of a given size. */
|
/** Build an empty Vector of a given size. */
|
||||||
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
||||||
|
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
||||||
|
{ resize(wSize); }
|
||||||
|
|
||||||
/** Build a Vector by shifting the data block. */
|
/** Build a Vector by moving another. */
|
||||||
Vector(Vector<T>& other)
|
Vector(Vector<T>&& other)
|
||||||
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc)
|
||||||
{ other.mData=NULL; }
|
{ other.mData=NULL; }
|
||||||
|
|
||||||
/** Build a Vector by copying another. */
|
/** Build a Vector by copying another. */
|
||||||
Vector(const Vector<T>& other):mData(NULL) { clone(other); }
|
Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); }
|
||||||
|
|
||||||
/** Build a Vector with explicit values. */
|
/** Build a Vector with explicit values. */
|
||||||
Vector(T* wData, T* wStart, T* wEnd)
|
Vector(T* wData, T* wStart, T* wEnd, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
||||||
:mData(wData),mStart(wStart),mEnd(wEnd)
|
:mData(wData),mStart(wStart),mEnd(wEnd), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/** Build a vector from an existing block, NOT to be deleted upon destruction. */
|
/** Build a vector from an existing block, NOT to be deleted upon destruction. */
|
||||||
Vector(T* wStart, size_t span)
|
Vector(T* wStart, size_t span, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
||||||
:mData(NULL),mStart(wStart),mEnd(wStart+span)
|
:mData(NULL),mStart(wStart),mEnd(wStart+span),mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/** Build a Vector by concatenation. */
|
/** Build a Vector by concatenation. */
|
||||||
Vector(const Vector<T>& other1, const Vector<T>& other2)
|
Vector(const Vector<T>& other1, const Vector<T>& other2, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
|
||||||
:mData(NULL)
|
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
|
||||||
{
|
{
|
||||||
resize(other1.size()+other2.size());
|
resize(other1.size()+other2.size());
|
||||||
memcpy(mStart, other1.mStart, other1.bytes());
|
memcpy(mStart, other1.mStart, other1.bytes());
|
||||||
@@ -155,6 +179,8 @@ template <class T> class Vector {
|
|||||||
mData=other.mData;
|
mData=other.mData;
|
||||||
mStart=other.mStart;
|
mStart=other.mStart;
|
||||||
mEnd=other.mEnd;
|
mEnd=other.mEnd;
|
||||||
|
mAllocFunc=other.mAllocFunc;
|
||||||
|
mFreeFunc=other.mFreeFunc;
|
||||||
other.mData=NULL;
|
other.mData=NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,10 +223,15 @@ template <class T> class Vector {
|
|||||||
*/
|
*/
|
||||||
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
|
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
|
||||||
{
|
{
|
||||||
T* base = other.mStart + start;
|
unsigned int i;
|
||||||
assert(base+span<=other.mEnd);
|
T* dst = other.mStart + start;
|
||||||
|
T* src = mStart;
|
||||||
|
assert(dst+span<=other.mEnd);
|
||||||
assert(mStart+span<=mEnd);
|
assert(mStart+span<=mEnd);
|
||||||
memcpy(base,mStart,span*sizeof(T));
|
for (i = 0; i < span; i++, src++, dst++)
|
||||||
|
*dst = *src;
|
||||||
|
/*TODO if not non-trivially copyable type class, optimize:
|
||||||
|
memcpy(dst,mStart,span*sizeof(T)); */
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Copy all of this Vector to a segment of another Vector. */
|
/** Copy all of this Vector to a segment of another Vector. */
|
||||||
@@ -222,6 +253,21 @@ template <class T> class Vector {
|
|||||||
memcpy(other.mStart,base,span*sizeof(T));
|
memcpy(other.mStart,base,span*sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Move (copy) a segment of this vector into a different position in the vector
|
||||||
|
@param from Start point from which to copy.
|
||||||
|
@param to Start point to which to copy.
|
||||||
|
@param span The number of elements to copy.
|
||||||
|
*/
|
||||||
|
void segmentMove(size_t from, size_t to, size_t span)
|
||||||
|
{
|
||||||
|
const T* baseFrom = mStart + from;
|
||||||
|
T* baseTo = mStart + to;
|
||||||
|
assert(baseFrom+span<=mEnd);
|
||||||
|
assert(baseTo+span<=mEnd);
|
||||||
|
memmove(baseTo,baseFrom,span*sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
void fill(const T& val)
|
void fill(const T& val)
|
||||||
{
|
{
|
||||||
T* dp=mStart;
|
T* dp=mStart;
|
||||||
@@ -260,7 +306,7 @@ template <class T> class Vector {
|
|||||||
T* end() { return mEnd; }
|
T* end() { return mEnd; }
|
||||||
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
|
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
20
CommonLibs/config_defs.h
Normal file
20
CommonLibs/config_defs.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file contains structures used by both VTY (C, dir CommonLibs) and
|
||||||
|
* osmo-trx (CXX, dir Transceiver52)
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum FillerType {
|
||||||
|
FILLER_DUMMY,
|
||||||
|
FILLER_ZERO,
|
||||||
|
FILLER_NORM_RAND,
|
||||||
|
FILLER_EDGE_RAND,
|
||||||
|
FILLER_ACCESS_RAND,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ReferenceType {
|
||||||
|
REF_INTERNAL,
|
||||||
|
REF_EXTERNAL,
|
||||||
|
REF_GPS,
|
||||||
|
};
|
||||||
36
CommonLibs/debug.c
Normal file
36
CommonLibs/debug.c
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include <osmocom/core/logging.h>
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
/* default categories */
|
||||||
|
static const struct log_info_cat default_categories[] = {
|
||||||
|
[DMAIN] = {
|
||||||
|
.name = "DMAIN",
|
||||||
|
.description = "Main generic category",
|
||||||
|
.color = NULL,
|
||||||
|
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||||
|
},
|
||||||
|
[DTRXCTRL] = {
|
||||||
|
.name = "DTRXCTRL",
|
||||||
|
.description = "TRX CTRL interface",
|
||||||
|
.color = "\033[1;33m",
|
||||||
|
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||||
|
},
|
||||||
|
[DDEV] = {
|
||||||
|
.name = "DDEV",
|
||||||
|
.description = "Device/Driver specific code",
|
||||||
|
.color = NULL,
|
||||||
|
.enabled = 1, .loglevel = LOGL_INFO,
|
||||||
|
},
|
||||||
|
[DLMS] = {
|
||||||
|
.name = "DLMS",
|
||||||
|
.description = "Logging from within LimeSuite itself",
|
||||||
|
.color = NULL,
|
||||||
|
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct log_info log_info = {
|
||||||
|
.cat = default_categories,
|
||||||
|
.num_cat = ARRAY_SIZE(default_categories),
|
||||||
|
};
|
||||||
11
CommonLibs/debug.h
Normal file
11
CommonLibs/debug.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
extern const struct log_info log_info;
|
||||||
|
|
||||||
|
/* Debug Areas of the code */
|
||||||
|
enum {
|
||||||
|
DMAIN,
|
||||||
|
DTRXCTRL,
|
||||||
|
DDEV,
|
||||||
|
DLMS,
|
||||||
|
};
|
||||||
35
CommonLibs/osmo_signal.h
Normal file
35
CommonLibs/osmo_signal.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/* Generic signalling/notification infrastructure */
|
||||||
|
/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/signal.h>
|
||||||
|
|
||||||
|
/* Signalling subsystems */
|
||||||
|
enum signal_subsystems {
|
||||||
|
SS_TRANSC,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SS_TRANSC signals */
|
||||||
|
enum SS_TRANSC {
|
||||||
|
S_TRANSC_STOP_REQUIRED, /* Transceiver fatal error, it should be stopped */
|
||||||
|
};
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "sqlite3.h"
|
|
||||||
#include "sqlite3util.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Wrappers to sqlite operations.
|
|
||||||
// These will eventually get moved to commonlibs.
|
|
||||||
|
|
||||||
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query)
|
|
||||||
{
|
|
||||||
int src = SQLITE_BUSY;
|
|
||||||
while (src==SQLITE_BUSY) {
|
|
||||||
src = sqlite3_prepare_v2(DB,query,strlen(query),stmt,NULL);
|
|
||||||
if (src==SQLITE_BUSY) {
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (src) {
|
|
||||||
fprintf(stderr,"sqlite3_prepare_v2 failed for \"%s\": %s\n",query,sqlite3_errmsg(DB));
|
|
||||||
sqlite3_finalize(*stmt);
|
|
||||||
}
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
int src = SQLITE_BUSY;
|
|
||||||
while (src==SQLITE_BUSY) {
|
|
||||||
src = sqlite3_step(stmt);
|
|
||||||
if (src==SQLITE_BUSY) {
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((src!=SQLITE_DONE) && (src!=SQLITE_ROW)) {
|
|
||||||
fprintf(stderr,"sqlite3_run_query failed: %s: %s\n", sqlite3_sql(stmt), sqlite3_errmsg(DB));
|
|
||||||
}
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool sqlite3_exists(sqlite3* DB, const char *tableName,
|
|
||||||
const char* keyName, const char* keyData)
|
|
||||||
{
|
|
||||||
size_t stringSize = 100 + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
|
||||||
char query[stringSize];
|
|
||||||
sprintf(query,"SELECT * FROM %s WHERE %s == \"%s\"",tableName,keyName,keyData);
|
|
||||||
// Prepare the statement.
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
|
||||||
// Read the result.
|
|
||||||
int src = sqlite3_run_query(DB,stmt);
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
// Anything there?
|
|
||||||
return (src == SQLITE_ROW);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
|
|
||||||
const char* keyName, const char* keyData,
|
|
||||||
const char* valueName, unsigned &valueData)
|
|
||||||
{
|
|
||||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
|
||||||
char query[stringSize];
|
|
||||||
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
|
|
||||||
// Prepare the statement.
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
|
||||||
// Read the result.
|
|
||||||
int src = sqlite3_run_query(DB,stmt);
|
|
||||||
bool retVal = false;
|
|
||||||
if (src == SQLITE_ROW) {
|
|
||||||
valueData = (unsigned)sqlite3_column_int64(stmt,0);
|
|
||||||
retVal = true;
|
|
||||||
}
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This function returns an allocated string that must be free'd by the caller.
|
|
||||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
|
||||||
const char* keyName, const char* keyData,
|
|
||||||
const char* valueName, char* &valueData)
|
|
||||||
{
|
|
||||||
valueData=NULL;
|
|
||||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
|
|
||||||
char query[stringSize];
|
|
||||||
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
|
|
||||||
// Prepare the statement.
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
|
||||||
// Read the result.
|
|
||||||
int src = sqlite3_run_query(DB,stmt);
|
|
||||||
bool retVal = false;
|
|
||||||
if (src == SQLITE_ROW) {
|
|
||||||
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
|
|
||||||
if (ptr) valueData = strdup(ptr);
|
|
||||||
retVal = true;
|
|
||||||
}
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This function returns an allocated string that must be free'd by tha caller.
|
|
||||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
|
||||||
const char* keyName, unsigned keyData,
|
|
||||||
const char* valueName, char* &valueData)
|
|
||||||
{
|
|
||||||
valueData=NULL;
|
|
||||||
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + 20;
|
|
||||||
char query[stringSize];
|
|
||||||
sprintf(query,"SELECT %s FROM %s WHERE %s == %u",valueName,tableName,keyName,keyData);
|
|
||||||
// Prepare the statement.
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
|
||||||
// Read the result.
|
|
||||||
int src = sqlite3_run_query(DB,stmt);
|
|
||||||
bool retVal = false;
|
|
||||||
if (src == SQLITE_ROW) {
|
|
||||||
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
|
|
||||||
if (ptr) valueData = strdup(ptr);
|
|
||||||
retVal = true;
|
|
||||||
}
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool sqlite3_command(sqlite3* DB, const char* query)
|
|
||||||
{
|
|
||||||
// Prepare the statement.
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
|
|
||||||
// Run the query.
|
|
||||||
int src = sqlite3_run_query(DB,stmt);
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
return src==SQLITE_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#ifndef SQLITE3UTIL_H
|
|
||||||
#define SQLITE3UTIL_H
|
|
||||||
|
|
||||||
#include <sqlite3.h>
|
|
||||||
|
|
||||||
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query);
|
|
||||||
|
|
||||||
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt);
|
|
||||||
|
|
||||||
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
|
|
||||||
const char* keyName, const char* keyData,
|
|
||||||
const char* valueName, unsigned &valueData);
|
|
||||||
|
|
||||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
|
||||||
const char* keyName, const char* keyData,
|
|
||||||
const char* valueName, char* &valueData);
|
|
||||||
|
|
||||||
// This function returns an allocated string that must be free'd by the caller.
|
|
||||||
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
|
|
||||||
const char* keyName, unsigned keyData,
|
|
||||||
const char* valueName, char* &valueData);
|
|
||||||
|
|
||||||
bool sqlite3_exists(sqlite3* DB, const char* tableName,
|
|
||||||
const char* keyName, const char* keyData);
|
|
||||||
|
|
||||||
/** Run a query, ignoring the result; return true on success. */
|
|
||||||
bool sqlite3_command(sqlite3* DB, const char* query);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
603
CommonLibs/trx_vty.c
Normal file
603
CommonLibs/trx_vty.c
Normal file
@@ -0,0 +1,603 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/talloc.h>
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
|
|
||||||
|
#include <osmocom/vty/command.h>
|
||||||
|
#include <osmocom/vty/vty.h>
|
||||||
|
#include <osmocom/vty/misc.h>
|
||||||
|
|
||||||
|
#include "trx_vty.h"
|
||||||
|
#include "../config.h"
|
||||||
|
|
||||||
|
static struct trx_ctx* g_trx_ctx;
|
||||||
|
|
||||||
|
static const struct value_string clock_ref_names[] = {
|
||||||
|
{ REF_INTERNAL, "internal" },
|
||||||
|
{ REF_EXTERNAL, "external" },
|
||||||
|
{ REF_GPS, "gpsdo" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct value_string filler_names[] = {
|
||||||
|
{ FILLER_DUMMY, "Dummy bursts" },
|
||||||
|
{ FILLER_ZERO, "Disabled" },
|
||||||
|
{ FILLER_NORM_RAND, "Normal bursts with random payload" },
|
||||||
|
{ FILLER_EDGE_RAND, "EDGE bursts with random payload" },
|
||||||
|
{ FILLER_ACCESS_RAND, "Access bursts with random payload" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct trx_ctx *trx_from_vty(struct vty *v)
|
||||||
|
{
|
||||||
|
/* It can't hurt to force callers to continue to pass the vty instance
|
||||||
|
* to this function, in case we'd like to retrieve the global
|
||||||
|
* trx instance from the vty at some point in the future. But
|
||||||
|
* until then, just return the global pointer, which should have been
|
||||||
|
* initialized by trx_vty_init().
|
||||||
|
*/
|
||||||
|
OSMO_ASSERT(g_trx_ctx);
|
||||||
|
return g_trx_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum trx_vty_node {
|
||||||
|
TRX_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||||
|
CHAN_NODE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cmd_node trx_node = {
|
||||||
|
TRX_NODE,
|
||||||
|
"%s(config-trx)# ",
|
||||||
|
1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cmd_node chan_node = {
|
||||||
|
CHAN_NODE,
|
||||||
|
"%s(config-trx-chan)# ",
|
||||||
|
1,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFUN(cfg_trx, cfg_trx_cmd,
|
||||||
|
"trx",
|
||||||
|
"Configure the TRX\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
if (!trx)
|
||||||
|
return CMD_WARNING;
|
||||||
|
|
||||||
|
vty->node = TRX_NODE;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
|
||||||
|
"bind-ip A.B.C.D",
|
||||||
|
"Set the IP address for the local bind\n"
|
||||||
|
"IPv4 Address\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
|
||||||
|
"remote-ip A.B.C.D",
|
||||||
|
"Set the IP address for the remote BTS\n"
|
||||||
|
"IPv4 Address\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_base_port, cfg_base_port_cmd,
|
||||||
|
"base-port <1-65535>",
|
||||||
|
"Set the TRX Base Port\n"
|
||||||
|
"TRX Base Port\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx->cfg.base_port = atoi(argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_dev_args, cfg_dev_args_cmd,
|
||||||
|
"dev-args DESC",
|
||||||
|
"Set the device-specific arguments to pass to the device\n"
|
||||||
|
"Device-specific arguments\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
|
||||||
|
"tx-sps (1|4)",
|
||||||
|
"Set the Tx Samples-per-Symbol\n"
|
||||||
|
"Tx Samples-per-Symbol\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx->cfg.tx_sps = atoi(argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
|
||||||
|
"rx-sps (1|4)",
|
||||||
|
"Set the Rx Samples-per-Symbol\n"
|
||||||
|
"Rx Samples-per-Symbol\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx->cfg.rx_sps = atoi(argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd,
|
||||||
|
"test rtsc <0-7>",
|
||||||
|
"Set the Random Normal Burst test mode with TSC\n"
|
||||||
|
"TSC\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
if (trx->cfg.rach_delay_set) {
|
||||||
|
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
|
||||||
|
VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
trx->cfg.rtsc_set = true;
|
||||||
|
trx->cfg.rtsc = atoi(argv[0]);
|
||||||
|
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
|
||||||
|
trx->cfg.filler = FILLER_NORM_RAND;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd,
|
||||||
|
"test rach-delay <0-68>",
|
||||||
|
"Set the Random Access Burst test mode with delay\n"
|
||||||
|
"RACH delay\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
if (trx->cfg.rtsc_set) {
|
||||||
|
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
|
||||||
|
VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trx->cfg.egprs) {
|
||||||
|
vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s",
|
||||||
|
VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
trx->cfg.rach_delay_set = true;
|
||||||
|
trx->cfg.rach_delay = atoi(argv[0]);
|
||||||
|
trx->cfg.filler = FILLER_ACCESS_RAND;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
|
||||||
|
"clock-ref (internal|external|gpsdo)",
|
||||||
|
"Set the Reference Clock\n"
|
||||||
|
"Enable internal referece (default)\n"
|
||||||
|
"Enable external 10 MHz reference\n"
|
||||||
|
"Enable GPSDO reference\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
|
||||||
|
"multi-arfcn (disable|enable)",
|
||||||
|
"Enable multi-ARFCN transceiver (default=disable)\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
if (strcmp("disable", argv[0]) == 0) {
|
||||||
|
trx->cfg.multi_arfcn = false;
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trx->cfg.num_chans > TRX_MCHAN_MAX) {
|
||||||
|
vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s",
|
||||||
|
TRX_MCHAN_MAX, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
trx->cfg.multi_arfcn = true;
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_offset, cfg_offset_cmd,
|
||||||
|
"offset FLOAT",
|
||||||
|
"Set the baseband frequency offset (default=0, auto)\n"
|
||||||
|
"Baseband Frequency Offset\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx->cfg.offset = atof(argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd,
|
||||||
|
"rssi-offset FLOAT",
|
||||||
|
"Set the RSSI to dBm offset in dB (default=0)\n"
|
||||||
|
"RSSI to dBm offset in dB\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx->cfg.rssi_offset = atof(argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
|
||||||
|
"swap-channels (disable|enable)",
|
||||||
|
"Swap channels (default=disable)\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
if (strcmp("disable", argv[0]) == 0) {
|
||||||
|
trx->cfg.swap_channels = false;
|
||||||
|
} else if (strcmp("enable", argv[0]) == 0) {
|
||||||
|
trx->cfg.swap_channels = true;
|
||||||
|
} else {
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_egprs, cfg_egprs_cmd,
|
||||||
|
"egprs (disable|enable)",
|
||||||
|
"Enable EDGE receiver (default=disable)\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
if (strcmp("disable", argv[0]) == 0) {
|
||||||
|
trx->cfg.egprs = false;
|
||||||
|
} else if (strcmp("enable", argv[0]) == 0) {
|
||||||
|
trx->cfg.egprs = true;
|
||||||
|
trx->cfg.filler = FILLER_EDGE_RAND;
|
||||||
|
} else {
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
|
||||||
|
"ext-rach (disable|enable)",
|
||||||
|
"Enable extended (11-bit) RACH (default=disable)\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
if (strcmp("disable", argv[0]) == 0)
|
||||||
|
trx->cfg.ext_rach = false;
|
||||||
|
|
||||||
|
if (strcmp("enable", argv[0]) == 0)
|
||||||
|
trx->cfg.ext_rach = true;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
|
||||||
|
"rt-prio <1-32>",
|
||||||
|
"Set the SCHED_RR real-time priority\n"
|
||||||
|
"Real time priority\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx->cfg.sched_rr = atoi(argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_filler, cfg_filler_cmd,
|
||||||
|
"filler dummy",
|
||||||
|
"Enable C0 filler table\n"
|
||||||
|
"Dummy method\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx->cfg.filler = FILLER_DUMMY;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_chan, cfg_chan_cmd,
|
||||||
|
"chan <0-100>",
|
||||||
|
"Select a channel to configure\n"
|
||||||
|
"Channel index\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
int idx = atoi(argv[0]);
|
||||||
|
|
||||||
|
if (idx >= TRX_CHAN_MAX) {
|
||||||
|
vty_out(vty, "Chan list full.%s", VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
} else if (trx->cfg.multi_arfcn && trx->cfg.num_chans >= TRX_MCHAN_MAX) {
|
||||||
|
vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s",
|
||||||
|
TRX_MCHAN_MAX, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */
|
||||||
|
vty_out(vty, "Non-existent or non-consecutive chan %d.%s",
|
||||||
|
idx, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
} else if (trx->cfg.num_chans == idx) { /* creating it */
|
||||||
|
trx->cfg.num_chans++;
|
||||||
|
trx->cfg.chans[idx].trx = trx;
|
||||||
|
trx->cfg.chans[idx].idx = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
vty->node = CHAN_NODE;
|
||||||
|
vty->index = &trx->cfg.chans[idx];
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd,
|
||||||
|
"rx-path NAME",
|
||||||
|
"Set the Rx Path\n"
|
||||||
|
"Rx Path name\n")
|
||||||
|
{
|
||||||
|
struct trx_chan *chan = vty->index;
|
||||||
|
|
||||||
|
osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd,
|
||||||
|
"tx-path NAME",
|
||||||
|
"Set the Tx Path\n"
|
||||||
|
"Tx Path name\n")
|
||||||
|
{
|
||||||
|
struct trx_chan *chan = vty->index;
|
||||||
|
|
||||||
|
osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dummy_config_write(struct vty *v)
|
||||||
|
{
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_write_trx(struct vty *vty)
|
||||||
|
{
|
||||||
|
struct trx_chan *chan;
|
||||||
|
int i;
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
vty_out(vty, "trx%s", VTY_NEWLINE);
|
||||||
|
if (trx->cfg.bind_addr)
|
||||||
|
vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.remote_addr)
|
||||||
|
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.base_port != DEFAULT_TRX_PORT)
|
||||||
|
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.dev_args)
|
||||||
|
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
|
||||||
|
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.rx_sps != DEFAULT_RX_SPS)
|
||||||
|
vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.rtsc_set)
|
||||||
|
vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.rach_delay_set)
|
||||||
|
vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.clock_ref != REF_INTERNAL)
|
||||||
|
vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
|
||||||
|
vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE);
|
||||||
|
if (trx->cfg.offset != 0)
|
||||||
|
vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE);
|
||||||
|
if (trx->cfg.rssi_offset != 0)
|
||||||
|
vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " ext-rach %s%s", trx->cfg.ext_rach ? "enable" : "disable", VTY_NEWLINE);
|
||||||
|
if (trx->cfg.sched_rr != 0)
|
||||||
|
vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE);
|
||||||
|
|
||||||
|
for (i = 0; i < trx->cfg.num_chans; i++) {
|
||||||
|
chan = &trx->cfg.chans[i];
|
||||||
|
vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE);
|
||||||
|
if (chan->rx_path)
|
||||||
|
vty_out(vty, " rx-path %s%s", chan->rx_path, VTY_NEWLINE);
|
||||||
|
if (chan->tx_path)
|
||||||
|
vty_out(vty, " tx-path %s%s", chan->tx_path, VTY_NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
|
||||||
|
{
|
||||||
|
struct trx_chan *chan;
|
||||||
|
int i;
|
||||||
|
vty_out(vty, "TRX Config:%s", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc,
|
||||||
|
trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay,
|
||||||
|
trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Extended RACH support: %s%s", trx->cfg.ext_rach ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr,
|
||||||
|
trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||||
|
vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE);
|
||||||
|
for (i = 0; i < trx->cfg.num_chans; i++) {
|
||||||
|
chan = &trx->cfg.chans[i];
|
||||||
|
vty_out(vty, " Channel %u:%s", chan->idx, VTY_NEWLINE);
|
||||||
|
if (chan->rx_path)
|
||||||
|
vty_out(vty, " Rx Path: %s%s", chan->rx_path, VTY_NEWLINE);
|
||||||
|
if (chan->tx_path)
|
||||||
|
vty_out(vty, " Tx Path: %s%s", chan->tx_path, VTY_NEWLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(show_trx, show_trx_cmd,
|
||||||
|
"show trx",
|
||||||
|
SHOW_STR "Display information on the TRX\n")
|
||||||
|
{
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
|
|
||||||
|
trx_dump_vty(vty, trx);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trx_vty_is_config_node(struct vty *vty, int node)
|
||||||
|
{
|
||||||
|
switch (node) {
|
||||||
|
case TRX_NODE:
|
||||||
|
case CHAN_NODE:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trx_vty_go_parent(struct vty *vty)
|
||||||
|
{
|
||||||
|
switch (vty->node) {
|
||||||
|
case TRX_NODE:
|
||||||
|
vty->node = CONFIG_NODE;
|
||||||
|
vty->index = NULL;
|
||||||
|
vty->index_sub = NULL;
|
||||||
|
break;
|
||||||
|
case CHAN_NODE:
|
||||||
|
vty->node = TRX_NODE;
|
||||||
|
vty->index = NULL;
|
||||||
|
vty->index_sub = NULL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
vty->node = CONFIG_NODE;
|
||||||
|
vty->index = NULL;
|
||||||
|
vty->index_sub = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vty->node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char trx_copyright[] =
|
||||||
|
"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n"
|
||||||
|
"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n"
|
||||||
|
"Copyright (C) 2015 Ettus Research LLC\r\n"
|
||||||
|
"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
|
||||||
|
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||||
|
"This is free software: you are free to change and redistribute it.\r\n"
|
||||||
|
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
||||||
|
|
||||||
|
struct vty_app_info g_vty_info = {
|
||||||
|
.name = "OsmoTRX",
|
||||||
|
.version = PACKAGE_VERSION,
|
||||||
|
.copyright = trx_copyright,
|
||||||
|
.go_parent_cb = trx_vty_go_parent,
|
||||||
|
.is_config_node = trx_vty_is_config_node,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
|
||||||
|
{
|
||||||
|
struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx);
|
||||||
|
|
||||||
|
trx->cfg.bind_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
|
||||||
|
trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
|
||||||
|
trx->cfg.base_port = DEFAULT_TRX_PORT;
|
||||||
|
trx->cfg.tx_sps = DEFAULT_TX_SPS;
|
||||||
|
trx->cfg.rx_sps = DEFAULT_RX_SPS;
|
||||||
|
trx->cfg.filler = FILLER_ZERO;
|
||||||
|
|
||||||
|
return trx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int trx_vty_init(struct trx_ctx* trx)
|
||||||
|
{
|
||||||
|
g_trx_ctx = trx;
|
||||||
|
install_element_ve(&show_trx_cmd);
|
||||||
|
|
||||||
|
install_element(CONFIG_NODE, &cfg_trx_cmd);
|
||||||
|
|
||||||
|
install_node(&trx_node, config_write_trx);
|
||||||
|
install_element(TRX_NODE, &cfg_bind_ip_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_remote_ip_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_base_port_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_dev_args_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_tx_sps_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_rx_sps_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_test_rtsc_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_test_rach_delay_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_clock_ref_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_multi_arfcn_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_offset_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_rssi_offset_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_swap_channels_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_egprs_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_ext_rach_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_rt_prio_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_filler_cmd);
|
||||||
|
|
||||||
|
install_element(TRX_NODE, &cfg_chan_cmd);
|
||||||
|
install_node(&chan_node, dummy_config_write);
|
||||||
|
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
|
||||||
|
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
72
CommonLibs/trx_vty.h
Normal file
72
CommonLibs/trx_vty.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/vty/command.h>
|
||||||
|
|
||||||
|
#include "config_defs.h"
|
||||||
|
|
||||||
|
extern struct vty_app_info g_vty_info;
|
||||||
|
|
||||||
|
/* Maximum number of physical RF channels */
|
||||||
|
#define TRX_CHAN_MAX 8
|
||||||
|
/* Maximum number of carriers in multi-ARFCN mode */
|
||||||
|
#define TRX_MCHAN_MAX 3
|
||||||
|
|
||||||
|
/* Samples-per-symbol for downlink path
|
||||||
|
* 4 - Uses precision modulator (more computation, less distortion)
|
||||||
|
* 1 - Uses minimized modulator (less computation, more distortion)
|
||||||
|
*
|
||||||
|
* Other values are invalid. Receive path (uplink) is always
|
||||||
|
* downsampled to 1 sps. Default to 4 sps for all cases.
|
||||||
|
*/
|
||||||
|
#define DEFAULT_TX_SPS 4
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Samples-per-symbol for uplink (receiver) path
|
||||||
|
* Do not modify this value. EDGE configures 4 sps automatically on
|
||||||
|
* B200/B210 devices only. Use of 4 sps on the receive path for other
|
||||||
|
* configurations is not supported.
|
||||||
|
*/
|
||||||
|
#define DEFAULT_RX_SPS 1
|
||||||
|
|
||||||
|
/* Default configuration parameters */
|
||||||
|
#define DEFAULT_TRX_PORT 5700
|
||||||
|
#define DEFAULT_TRX_IP "127.0.0.1"
|
||||||
|
#define DEFAULT_CHANS 1
|
||||||
|
|
||||||
|
struct trx_ctx;
|
||||||
|
|
||||||
|
struct trx_chan {
|
||||||
|
struct trx_ctx *trx; /* backpointer */
|
||||||
|
unsigned int idx; /* channel index */
|
||||||
|
char *rx_path;
|
||||||
|
char *tx_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct trx_ctx {
|
||||||
|
struct {
|
||||||
|
char *bind_addr;
|
||||||
|
char *remote_addr;
|
||||||
|
char *dev_args;
|
||||||
|
unsigned int base_port;
|
||||||
|
unsigned int tx_sps;
|
||||||
|
unsigned int rx_sps;
|
||||||
|
unsigned int rtsc;
|
||||||
|
bool rtsc_set;
|
||||||
|
unsigned int rach_delay;
|
||||||
|
bool rach_delay_set;
|
||||||
|
enum ReferenceType clock_ref;
|
||||||
|
enum FillerType filler;
|
||||||
|
bool multi_arfcn;
|
||||||
|
double offset;
|
||||||
|
double rssi_offset;
|
||||||
|
bool swap_channels;
|
||||||
|
bool ext_rach;
|
||||||
|
bool egprs;
|
||||||
|
unsigned int sched_rr;
|
||||||
|
unsigned int num_chans;
|
||||||
|
struct trx_chan chans[TRX_CHAN_MAX];
|
||||||
|
} cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
int trx_vty_init(struct trx_ctx* trx);
|
||||||
|
struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx);
|
||||||
@@ -54,7 +54,10 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
|
|||||||
|
|
||||||
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
||||||
|
|
||||||
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
|
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
|
||||||
|
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
|
||||||
|
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
|
||||||
|
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
|
||||||
|
|
||||||
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
||||||
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ extern const BitVector gEdgeTrainingSequence[];
|
|||||||
extern const BitVector gDummyBurst;
|
extern const BitVector gDummyBurst;
|
||||||
|
|
||||||
/** Random access burst synch. sequence */
|
/** Random access burst synch. sequence */
|
||||||
extern const BitVector gRACHSynchSequence;
|
extern const BitVector gRACHSynchSequenceTS0;
|
||||||
|
extern const BitVector gRACHSynchSequenceTS1;
|
||||||
|
extern const BitVector gRACHSynchSequenceTS2;
|
||||||
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
||||||
extern const BitVector gRACHBurst;
|
extern const BitVector gRACHBurst;
|
||||||
|
|
||||||
|
|||||||
22
INSTALLATION
22
INSTALLATION
@@ -2,32 +2,18 @@ Installation Requirements
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
OpenBTS compiles to a simple Unix binary and does not require special
|
osmo-trx compiles to a simple Unix binary and does not require special
|
||||||
installation.
|
installation.
|
||||||
|
|
||||||
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
|
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
|
||||||
running configure.
|
running configure.
|
||||||
|
|
||||||
To run OpenBTS, the following should be installed:
|
To run osmo-trx, the following should be installed:
|
||||||
|
libuhd (https://gnuradio.org).
|
||||||
Asterisk (http://www.asterisk.org), running SIP on port 5060.
|
|
||||||
|
|
||||||
libosip2 (http://www.gnu.org/software/osip/)
|
|
||||||
|
|
||||||
libortp (http://freshmeat.net/projects/ortp/)
|
|
||||||
|
|
||||||
libusrp (http://gnuradio.org).
|
|
||||||
This is part of the GNURadio installation.
|
This is part of the GNURadio installation.
|
||||||
It is the only part used by OpenBTS.
|
|
||||||
|
|
||||||
|
|
||||||
OpenBTS logs to syslogd as facility LOG_LOCAL7. Please set your /etc/syslog.conf
|
|
||||||
accordingly.
|
|
||||||
|
|
||||||
|
|
||||||
For information on specific executables, see tests/README.tests and
|
For information on specific executables, see tests/README.tests and
|
||||||
apps/README.apps.
|
apps/README.apps.
|
||||||
|
|
||||||
See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more
|
See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
|
|||||||
20
LEGAL
20
LEGAL
@@ -1,5 +1,8 @@
|
|||||||
OpenBTS
|
OpenBTS
|
||||||
|
|
||||||
|
The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details.
|
||||||
|
|
||||||
|
The related copyrights:
|
||||||
Most parts copyright 2008-2011 Free Software Foundation.
|
Most parts copyright 2008-2011 Free Software Foundation.
|
||||||
Some parts copyright 2010 Kestrel Signal Processing, Inc.
|
Some parts copyright 2010 Kestrel Signal Processing, Inc.
|
||||||
Some parts copyright 2011 Range Networks, Inc.
|
Some parts copyright 2011 Range Networks, Inc.
|
||||||
@@ -12,17 +15,9 @@ patented technologies. The user of this software is required to take whatever
|
|||||||
actions are necessary to avoid patent infringement.
|
actions are necessary to avoid patent infringement.
|
||||||
|
|
||||||
|
|
||||||
Trademark
|
|
||||||
|
|
||||||
"OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a
|
|
||||||
California corporation. Range reserves the right to control the use of this
|
|
||||||
trademark. Do not use this trademark in commerce without permission and do not
|
|
||||||
rebrand OpenBTS under a different trademark.
|
|
||||||
|
|
||||||
|
|
||||||
Telecom and Radio Spectrum Laws
|
Telecom and Radio Spectrum Laws
|
||||||
|
|
||||||
The primary function of 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
|
over a radio link. This activity is heavily regulated nearly everywhere in
|
||||||
the world. Users of this software are expected to comply with local and national
|
the world. Users of this software are expected to comply with local and national
|
||||||
regulations in the jurisdictions where this sortware is used with radio equipment.
|
regulations in the jurisdictions where this sortware is used with radio equipment.
|
||||||
@@ -39,7 +34,7 @@ The legal restrictions listed here are not necessarily exhaustive.
|
|||||||
|
|
||||||
Note to US Government Users
|
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
|
Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of
|
||||||
"Commercial Computer Software" and "Commercial Computer Software Documentation,"
|
"Commercial Computer Software" and "Commercial Computer Software Documentation,"
|
||||||
as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
|
as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
|
||||||
@@ -54,13 +49,12 @@ and AGPLv3.
|
|||||||
Note to US Government Contractors
|
Note to US Government Contractors
|
||||||
|
|
||||||
GPL is not compatible with "government purpose rights" (GPR). If you receive
|
GPL is not compatible with "government purpose rights" (GPR). If you receive
|
||||||
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
|
of GPL and possibly subject to enforcement actions by the original authors and
|
||||||
copyright holders, including the Free Software Foundation, Inc.
|
copyright holders, including the Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
|
||||||
Software Licensing and Distribution
|
Software Licensing and Distribution
|
||||||
|
|
||||||
A subset of OpenBTS is distributed publicly under AGPLv3. Range reserves the right to
|
The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file
|
||||||
distribute most of this source code other licenses as well. See the COPYING file
|
|
||||||
for more information on the license for this distribution.
|
for more information on the license for this distribution.
|
||||||
|
|||||||
18
Makefile.am
18
Makefile.am
@@ -22,16 +22,18 @@ include $(top_srcdir)/Makefile.common
|
|||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I config
|
ACLOCAL_AMFLAGS = -I config
|
||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
||||||
AM_CXXFLAGS = -Wall -pthread -ldl
|
AM_CXXFLAGS = -Wall -pthread
|
||||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
|
||||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
|
||||||
|
|
||||||
# Order must be preserved
|
# Order must be preserved
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
sqlite3 \
|
doc \
|
||||||
CommonLibs \
|
CommonLibs \
|
||||||
GSM \
|
GSM \
|
||||||
Transceiver52M
|
Transceiver52M \
|
||||||
|
contrib \
|
||||||
|
tests
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
autogen.sh \
|
autogen.sh \
|
||||||
@@ -40,6 +42,12 @@ EXTRA_DIST = \
|
|||||||
COPYING \
|
COPYING \
|
||||||
README
|
README
|
||||||
|
|
||||||
|
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||||
|
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||||
|
|
||||||
|
.PHONY: release
|
||||||
|
|
||||||
|
@RELMAKE@
|
||||||
|
|
||||||
dox: FORCE
|
dox: FORCE
|
||||||
doxygen doxconfig
|
doxygen doxconfig
|
||||||
|
|||||||
@@ -18,21 +18,21 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
top_srcdir = $(abs_top_srcdir)
|
|
||||||
top_builddir = $(abs_top_builddir)
|
|
||||||
|
|
||||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
||||||
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
|
|
||||||
|
|
||||||
STD_DEFINES_AND_INCLUDES = \
|
STD_DEFINES_AND_INCLUDES = \
|
||||||
$(SVNDEV) \
|
$(SVNDEV) \
|
||||||
-I$(COMMON_INCLUDEDIR) \
|
-I$(COMMON_INCLUDEDIR) \
|
||||||
-I$(GSM_INCLUDEDIR) \
|
-I$(GSM_INCLUDEDIR)
|
||||||
-I$(SQLITE_INCLUDEDIR)
|
|
||||||
|
|
||||||
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
||||||
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
||||||
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl
|
|
||||||
|
if ARCH_ARM
|
||||||
|
ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la
|
||||||
|
else
|
||||||
|
ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la
|
||||||
|
endif
|
||||||
|
|
||||||
MOSTLYCLEANFILES = *~
|
MOSTLYCLEANFILES = *~
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_ */
|
||||||
@@ -21,22 +21,10 @@
|
|||||||
|
|
||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I./common
|
SUBDIRS = arch device
|
||||||
AM_CXXFLAGS = -ldl -lpthread
|
|
||||||
|
|
||||||
SUBDIRS = arm x86
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common
|
||||||
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||||
if ARCH_ARM
|
|
||||||
ARCH_LA = arm/libarch.la
|
|
||||||
else
|
|
||||||
ARCH_LA = x86/libarch.la
|
|
||||||
endif
|
|
||||||
|
|
||||||
if USRP1
|
|
||||||
AM_CPPFLAGS += $(USRP_CFLAGS)
|
|
||||||
else
|
|
||||||
AM_CPPFLAGS += $(UHD_CFLAGS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
rev2dir = $(datadir)/usrp/rev2
|
rev2dir = $(datadir)/usrp/rev2
|
||||||
rev4dir = $(datadir)/usrp/rev4
|
rev4dir = $(datadir)/usrp/rev4
|
||||||
@@ -44,55 +32,80 @@ rev4dir = $(datadir)/usrp/rev4
|
|||||||
dist_rev2_DATA = std_inband.rbf
|
dist_rev2_DATA = std_inband.rbf
|
||||||
dist_rev4_DATA = std_inband.rbf
|
dist_rev4_DATA = std_inband.rbf
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = README
|
||||||
README \
|
|
||||||
README.Talgorithm
|
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libtransceiver.la
|
noinst_LTLIBRARIES = libtransceiver_common.la
|
||||||
|
|
||||||
COMMON_SOURCES = \
|
COMMON_SOURCES = \
|
||||||
radioInterface.cpp \
|
radioInterface.cpp \
|
||||||
radioVector.cpp \
|
radioVector.cpp \
|
||||||
radioClock.cpp \
|
radioClock.cpp \
|
||||||
|
radioBuffer.cpp \
|
||||||
sigProcLib.cpp \
|
sigProcLib.cpp \
|
||||||
signalVector.cpp \
|
signalVector.cpp \
|
||||||
Transceiver.cpp
|
Transceiver.cpp \
|
||||||
|
ChannelizerBase.cpp \
|
||||||
|
Channelizer.cpp \
|
||||||
|
Synthesis.cpp
|
||||||
|
|
||||||
libtransceiver_la_SOURCES = \
|
libtransceiver_common_la_SOURCES = \
|
||||||
$(COMMON_SOURCES) \
|
$(COMMON_SOURCES) \
|
||||||
Resampler.cpp \
|
Resampler.cpp \
|
||||||
radioInterfaceResamp.cpp \
|
radioInterfaceResamp.cpp \
|
||||||
radioInterfaceDiversity.cpp
|
radioInterfaceMulti.cpp
|
||||||
|
|
||||||
bin_PROGRAMS = osmo-trx
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
Complex.h \
|
Complex.h \
|
||||||
radioInterface.h \
|
radioInterface.h \
|
||||||
radioVector.h \
|
radioVector.h \
|
||||||
radioClock.h \
|
radioClock.h \
|
||||||
radioDevice.h \
|
radioBuffer.h \
|
||||||
sigProcLib.h \
|
sigProcLib.h \
|
||||||
signalVector.h \
|
signalVector.h \
|
||||||
Transceiver.h \
|
Transceiver.h \
|
||||||
USRPDevice.h \
|
|
||||||
Resampler.h \
|
Resampler.h \
|
||||||
common/convolve.h \
|
ChannelizerBase.h \
|
||||||
common/convert.h \
|
Channelizer.h \
|
||||||
common/scale.h \
|
Synthesis.h
|
||||||
common/mult.h
|
|
||||||
|
|
||||||
osmo_trx_SOURCES = osmo-trx.cpp
|
COMMON_LDADD = \
|
||||||
osmo_trx_LDADD = \
|
libtransceiver_common.la \
|
||||||
libtransceiver.la \
|
|
||||||
$(ARCH_LA) \
|
$(ARCH_LA) \
|
||||||
$(GSM_LA) \
|
$(GSM_LA) \
|
||||||
$(COMMON_LA) $(SQLITE_LA)
|
$(COMMON_LA) \
|
||||||
|
$(FFTWF_LIBS) \
|
||||||
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
$(LIBOSMOCTRL_LIBS) \
|
||||||
|
$(LIBOSMOVTY_LIBS)
|
||||||
|
|
||||||
if USRP1
|
bin_PROGRAMS =
|
||||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
|
||||||
osmo_trx_LDADD += $(USRP_LIBS)
|
if DEVICE_UHD
|
||||||
else
|
bin_PROGRAMS += osmo-trx-uhd
|
||||||
libtransceiver_la_SOURCES += UHDDevice.cpp
|
osmo_trx_uhd_SOURCES = osmo-trx.cpp
|
||||||
osmo_trx_LDADD += $(UHD_LIBS)
|
osmo_trx_uhd_LDADD = \
|
||||||
|
$(builddir)/device/uhd/libdevice.la \
|
||||||
|
$(COMMON_LDADD) \
|
||||||
|
$(UHD_LIBS)
|
||||||
|
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if DEVICE_USRP1
|
||||||
|
bin_PROGRAMS += osmo-trx-usrp1
|
||||||
|
osmo_trx_usrp1_SOURCES = osmo-trx.cpp
|
||||||
|
osmo_trx_usrp1_LDADD = \
|
||||||
|
$(builddir)/device/usrp1/libdevice.la \
|
||||||
|
$(COMMON_LDADD) \
|
||||||
|
$(USRP_LIBS)
|
||||||
|
osmo_trx_usrp1_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if DEVICE_LMS
|
||||||
|
bin_PROGRAMS += osmo-trx-lms
|
||||||
|
osmo_trx_lms_SOURCES = osmo-trx.cpp
|
||||||
|
osmo_trx_lms_LDADD = \
|
||||||
|
$(builddir)/device/lms/libdevice.la \
|
||||||
|
$(COMMON_LDADD) \
|
||||||
|
$(LMS_LIBS)
|
||||||
|
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "Resampler.h"
|
#include "Resampler.h"
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define MAX_OUTPUT_LEN 4096
|
#define MAX_OUTPUT_LEN 4096
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
static float sinc(float x)
|
static float sinc(float x)
|
||||||
{
|
{
|
||||||
if (x == 0.0)
|
if (x == 0.0)
|
||||||
@@ -43,32 +46,19 @@ static float sinc(float x)
|
|||||||
return sin(M_PI * x) / (M_PI * x);
|
return sin(M_PI * x) / (M_PI * x);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resampler::initFilters(float bw)
|
void Resampler::initFilters(float bw)
|
||||||
{
|
{
|
||||||
size_t proto_len = p * filt_len;
|
float cutoff;
|
||||||
float *proto, val, cutoff;
|
|
||||||
float sum = 0.0f, scale = 0.0f;
|
float sum = 0.0f, scale = 0.0f;
|
||||||
float midpt = (float) (proto_len - 1.0) / 2.0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate partition filters and the temporary prototype filter
|
* Allocate partition filters and the temporary prototype filter
|
||||||
* according to numerator of the rational rate. Coefficients are
|
* according to numerator of the rational rate. Coefficients are
|
||||||
* real only and must be 16-byte memory aligned for SSE usage.
|
* real only and must be 16-byte memory aligned for SSE usage.
|
||||||
*/
|
*/
|
||||||
proto = new float[proto_len];
|
auto proto = vector<float>(p * filt_len);
|
||||||
if (!proto)
|
for (auto &part : partitions)
|
||||||
return false;
|
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
|
||||||
|
|
||||||
partitions = (float **) malloc(sizeof(float *) * p);
|
|
||||||
if (!partitions) {
|
|
||||||
free(proto);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < p; i++) {
|
|
||||||
partitions[i] = (float *)
|
|
||||||
memalign(16, filt_len * 2 * sizeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate the prototype filter with a Blackman-harris window.
|
* Generate the prototype filter with a Blackman-harris window.
|
||||||
@@ -85,47 +75,26 @@ bool Resampler::initFilters(float bw)
|
|||||||
else
|
else
|
||||||
cutoff = (float) q;
|
cutoff = (float) q;
|
||||||
|
|
||||||
for (size_t i = 0; i < proto_len; i++) {
|
float midpt = (proto.size() - 1) / 2.0;
|
||||||
|
for (size_t i = 0; i < proto.size(); i++) {
|
||||||
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
|
||||||
proto[i] *= a0 -
|
proto[i] *= a0 -
|
||||||
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
|
a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
|
||||||
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
|
a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
|
||||||
a3 * cos(6 * M_PI * i / (proto_len - 1));
|
a3 * cos(6 * M_PI * i / (proto.size() - 1));
|
||||||
sum += proto[i];
|
sum += proto[i];
|
||||||
}
|
}
|
||||||
scale = p / sum;
|
scale = p / sum;
|
||||||
|
|
||||||
/* Populate filter partitions from the prototype filter */
|
/* Populate filter partitions from the prototype filter */
|
||||||
for (size_t i = 0; i < filt_len; i++) {
|
for (size_t i = 0; i < filt_len; i++) {
|
||||||
for (size_t n = 0; n < p; n++) {
|
for (size_t n = 0; n < p; n++)
|
||||||
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
|
partitions[n][i] = complex<float>(proto[i * p + n] * scale);
|
||||||
partitions[n][2 * i + 1] = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For convolution, we store the filter taps in reverse */
|
/* Store filter taps in reverse */
|
||||||
for (size_t n = 0; n < p; n++) {
|
for (auto &part : partitions)
|
||||||
for (size_t i = 0; i < filt_len / 2; i++) {
|
reverse(&part[0], &part[filt_len]);
|
||||||
val = partitions[n][2 * i];
|
|
||||||
partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
|
|
||||||
partitions[n][2 * (filt_len - 1 - i)] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete proto;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Resampler::releaseFilters()
|
|
||||||
{
|
|
||||||
if (partitions) {
|
|
||||||
for (size_t i = 0; i < p; i++)
|
|
||||||
free(partitions[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(partitions);
|
|
||||||
partitions = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||||
@@ -159,45 +128,22 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resampler::computePath()
|
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
|
||||||
{
|
|
||||||
for (int i = 0; i < MAX_OUTPUT_LEN; i++) {
|
|
||||||
in_index[i] = (q * i) / p;
|
|
||||||
out_path[i] = (q * i) % p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
|
|
||||||
{
|
{
|
||||||
int n, path;
|
int n, path;
|
||||||
int hist_len = filt_len - 1;
|
|
||||||
|
|
||||||
if (!check_vec_len(in_len, out_len, p, q))
|
if (!check_vec_len(in_len, out_len, p, q))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (history_on) {
|
|
||||||
memcpy(&in[-2 * hist_len],
|
|
||||||
history, hist_len * 2 * sizeof(float));
|
|
||||||
} else {
|
|
||||||
memset(&in[-2 * hist_len], 0,
|
|
||||||
hist_len * 2 * sizeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generate output from precomputed input/output paths */
|
/* Generate output from precomputed input/output paths */
|
||||||
for (size_t i = 0; i < out_len; i++) {
|
for (size_t i = 0; i < out_len; i++) {
|
||||||
n = in_index[i];
|
n = in_index[i];
|
||||||
path = out_path[i];
|
path = out_path[i];
|
||||||
|
|
||||||
convolve_real(in, in_len,
|
convolve_real(in, in_len,
|
||||||
partitions[path], filt_len,
|
reinterpret_cast<float *>(partitions[path]),
|
||||||
&out[2 * i], out_len - i,
|
filt_len, &out[2 * i], out_len - i,
|
||||||
n, 1, 1, 0);
|
n, 1);
|
||||||
}
|
|
||||||
|
|
||||||
/* Save history */
|
|
||||||
if (history_on) {
|
|
||||||
memcpy(history, &in[2 * (in_len - hist_len)],
|
|
||||||
hist_len * 2 * sizeof(float));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return out_len;
|
return out_len;
|
||||||
@@ -205,20 +151,18 @@ int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
|
|||||||
|
|
||||||
bool Resampler::init(float bw)
|
bool Resampler::init(float bw)
|
||||||
{
|
{
|
||||||
size_t hist_len = filt_len - 1;
|
if (p == 0 || q == 0 || filt_len == 0) return false;
|
||||||
|
|
||||||
/* Filterbank filter internals */
|
/* Filterbank filter internals */
|
||||||
if (initFilters(bw) < 0)
|
initFilters(bw);
|
||||||
return false;
|
|
||||||
|
|
||||||
/* History buffer */
|
|
||||||
history = new float[2 * hist_len];
|
|
||||||
memset(history, 0, 2 * hist_len * sizeof(float));
|
|
||||||
|
|
||||||
/* Precompute filterbank paths */
|
/* Precompute filterbank paths */
|
||||||
in_index = new size_t[MAX_OUTPUT_LEN];
|
int i = 0;
|
||||||
out_path = new size_t[MAX_OUTPUT_LEN];
|
for (auto &index : in_index)
|
||||||
computePath();
|
index = (q * i++) / p;
|
||||||
|
i = 0;
|
||||||
|
for (auto &path : out_path)
|
||||||
|
path = (q * i++) % p;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -228,14 +172,8 @@ size_t Resampler::len()
|
|||||||
return filt_len;
|
return filt_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resampler::enableHistory(bool on)
|
|
||||||
{
|
|
||||||
history_on = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
||||||
: in_index(NULL), out_path(NULL), partitions(NULL),
|
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
|
||||||
history(NULL), history_on(true)
|
|
||||||
{
|
{
|
||||||
this->p = p;
|
this->p = p;
|
||||||
this->q = q;
|
this->q = q;
|
||||||
@@ -244,9 +182,6 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len)
|
|||||||
|
|
||||||
Resampler::~Resampler()
|
Resampler::~Resampler()
|
||||||
{
|
{
|
||||||
releaseFilters();
|
for (auto &part : partitions)
|
||||||
|
free(part);
|
||||||
delete history;
|
|
||||||
delete in_index;
|
|
||||||
delete out_path;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
#ifndef _RESAMPLER_H_
|
#ifndef _RESAMPLER_H_
|
||||||
#define _RESAMPLER_H_
|
#define _RESAMPLER_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <complex>
|
||||||
|
|
||||||
class Resampler {
|
class Resampler {
|
||||||
public:
|
public:
|
||||||
/* Constructor for rational sample rate conversion
|
/* Constructor for rational sample rate conversion
|
||||||
@@ -52,32 +55,22 @@ public:
|
|||||||
* Input and output vector lengths must of be equal multiples of the
|
* Input and output vector lengths must of be equal multiples of the
|
||||||
* rational conversion rate denominator and numerator respectively.
|
* rational conversion rate denominator and numerator respectively.
|
||||||
*/
|
*/
|
||||||
int rotate(float *in, size_t in_len, float *out, size_t out_len);
|
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
|
||||||
|
|
||||||
/* Get filter length
|
/* Get filter length
|
||||||
* @return number of taps in each filter partition
|
* @return number of taps in each filter partition
|
||||||
*/
|
*/
|
||||||
size_t len();
|
size_t len();
|
||||||
|
|
||||||
/*
|
|
||||||
* Enable/disable history
|
|
||||||
*/
|
|
||||||
void enableHistory(bool on);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t p;
|
size_t p;
|
||||||
size_t q;
|
size_t q;
|
||||||
size_t filt_len;
|
size_t filt_len;
|
||||||
size_t *in_index;
|
std::vector<size_t> in_index;
|
||||||
size_t *out_path;
|
std::vector<size_t> out_path;
|
||||||
|
std::vector<std::complex<float> *> partitions;
|
||||||
|
|
||||||
float **partitions;
|
void initFilters(float bw);
|
||||||
float *history;
|
|
||||||
bool history_on;
|
|
||||||
|
|
||||||
bool initFilters(float bw);
|
|
||||||
void releaseFilters();
|
|
||||||
void computePath();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _RESAMPLER_H_ */
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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_ */
|
||||||
@@ -27,6 +27,10 @@
|
|||||||
#include "Transceiver.h"
|
#include "Transceiver.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "osmo_signal.h"
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -35,12 +39,6 @@ using namespace GSM;
|
|||||||
|
|
||||||
#define USB_LATENCY_INTRVL 10,0
|
#define USB_LATENCY_INTRVL 10,0
|
||||||
|
|
||||||
#if USE_UHD
|
|
||||||
# define USB_LATENCY_MIN 6,7
|
|
||||||
#else
|
|
||||||
# define USB_LATENCY_MIN 1,1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Number of running values use in noise average */
|
/* Number of running values use in noise average */
|
||||||
#define NOISE_CNT 20
|
#define NOISE_CNT 20
|
||||||
|
|
||||||
@@ -71,7 +69,7 @@ TransceiverState::~TransceiverState()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
|
bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
|
||||||
{
|
{
|
||||||
signalVector *burst;
|
signalVector *burst;
|
||||||
|
|
||||||
@@ -81,19 +79,19 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
|
|||||||
for (size_t n = 0; n < 8; n++) {
|
for (size_t n = 0; n < 8; n++) {
|
||||||
for (size_t i = 0; i < 102; i++) {
|
for (size_t i = 0; i < 102; i++) {
|
||||||
switch (filler) {
|
switch (filler) {
|
||||||
case Transceiver::FILLER_DUMMY:
|
case FILLER_DUMMY:
|
||||||
burst = generateDummyBurst(sps, n);
|
burst = generateDummyBurst(sps, n);
|
||||||
break;
|
break;
|
||||||
case Transceiver::FILLER_NORM_RAND:
|
case FILLER_NORM_RAND:
|
||||||
burst = genRandNormalBurst(rtsc, sps, n);
|
burst = genRandNormalBurst(rtsc, sps, n);
|
||||||
break;
|
break;
|
||||||
case Transceiver::FILLER_EDGE_RAND:
|
case FILLER_EDGE_RAND:
|
||||||
burst = generateEdgeBurst(rtsc);
|
burst = generateEdgeBurst(rtsc);
|
||||||
break;
|
break;
|
||||||
case Transceiver::FILLER_ACCESS_RAND:
|
case FILLER_ACCESS_RAND:
|
||||||
burst = genRandAccessBurst(sps, n);
|
burst = genRandAccessBurst(rach_delay, sps, n);
|
||||||
break;
|
break;
|
||||||
case Transceiver::FILLER_ZERO:
|
case FILLER_ZERO:
|
||||||
default:
|
default:
|
||||||
burst = generateEmptyBurst(sps, n);
|
burst = generateEmptyBurst(sps, n);
|
||||||
}
|
}
|
||||||
@@ -102,9 +100,9 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
|
|||||||
fillerTable[i][n] = burst;
|
fillerTable[i][n] = burst;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((filler == Transceiver::FILLER_NORM_RAND) ||
|
if ((filler == FILLER_NORM_RAND) ||
|
||||||
(filler == Transceiver::FILLER_EDGE_RAND)) {
|
(filler == FILLER_EDGE_RAND)) {
|
||||||
chanType[n] = Transceiver::TSC;
|
chanType[n] = TSC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,16 +110,17 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::Transceiver(int wBasePort,
|
Transceiver::Transceiver(int wBasePort,
|
||||||
const char *wTRXAddress,
|
const char *TRXAddress,
|
||||||
|
const char *GSMcoreAddress,
|
||||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface,
|
RadioInterface *wRadioInterface,
|
||||||
double wRssiOffset)
|
double wRssiOffset)
|
||||||
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
|
||||||
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
|
||||||
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||||
rssiOffset(wRssiOffset),
|
rssiOffset(wRssiOffset), sig_cbfn(NULL),
|
||||||
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mOn(false),
|
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
|
||||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
||||||
mWriteBurstToDiskMask(0)
|
mWriteBurstToDiskMask(0)
|
||||||
{
|
{
|
||||||
@@ -160,7 +159,8 @@ Transceiver::~Transceiver()
|
|||||||
* are still expected to report clock indications through control channel
|
* are still expected to report clock indications through control channel
|
||||||
* activity.
|
* activity.
|
||||||
*/
|
*/
|
||||||
bool Transceiver::init(int filler, size_t rtsc)
|
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
|
||||||
|
bool edge, bool ext_rach)
|
||||||
{
|
{
|
||||||
int d_srcport, d_dstport, c_srcport, c_dstport;
|
int d_srcport, d_dstport, c_srcport, c_dstport;
|
||||||
|
|
||||||
@@ -174,6 +174,9 @@ bool Transceiver::init(int filler, size_t rtsc)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mExtRACH = ext_rach;
|
||||||
|
mEdge = edge;
|
||||||
|
|
||||||
mDataSockets.resize(mChans);
|
mDataSockets.resize(mChans);
|
||||||
mCtrlSockets.resize(mChans);
|
mCtrlSockets.resize(mChans);
|
||||||
mControlServiceLoopThreads.resize(mChans);
|
mControlServiceLoopThreads.resize(mChans);
|
||||||
@@ -195,8 +198,8 @@ bool Transceiver::init(int filler, size_t rtsc)
|
|||||||
d_srcport = mBasePort + 2 * i + 2;
|
d_srcport = mBasePort + 2 * i + 2;
|
||||||
d_dstport = mBasePort + 2 * i + 102;
|
d_dstport = mBasePort + 2 * i + 102;
|
||||||
|
|
||||||
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
|
mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
|
||||||
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Randomize the central clock */
|
/* Randomize the central clock */
|
||||||
@@ -216,12 +219,23 @@ bool Transceiver::init(int filler, size_t rtsc)
|
|||||||
if (i && filler == FILLER_DUMMY)
|
if (i && filler == FILLER_DUMMY)
|
||||||
filler = FILLER_ZERO;
|
filler = FILLER_ZERO;
|
||||||
|
|
||||||
mStates[i].init(filler, mSPSTx, txFullScale, rtsc);
|
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Transceiver::setSignalHandler(osmo_signal_cbfn cbfn)
|
||||||
|
{
|
||||||
|
if (this->sig_cbfn)
|
||||||
|
osmo_signal_unregister_handler(SS_TRANSC, this->sig_cbfn, NULL);
|
||||||
|
|
||||||
|
if (cbfn) {
|
||||||
|
this->sig_cbfn = cbfn;
|
||||||
|
osmo_signal_register_handler(SS_TRANSC, this->sig_cbfn, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the transceiver
|
* Start the transceiver
|
||||||
*
|
*
|
||||||
@@ -271,7 +285,7 @@ bool Transceiver::start()
|
|||||||
TxUpperLoopAdapter, (void*) chan);
|
TxUpperLoopAdapter, (void*) chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeClockInterface();
|
mForceClockInterface = true;
|
||||||
mOn = true;
|
mOn = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -295,6 +309,10 @@ void Transceiver::stop()
|
|||||||
LOG(NOTICE) << "Stopping the transceiver";
|
LOG(NOTICE) << "Stopping the transceiver";
|
||||||
mTxLowerLoopThread->cancel();
|
mTxLowerLoopThread->cancel();
|
||||||
mRxLowerLoopThread->cancel();
|
mRxLowerLoopThread->cancel();
|
||||||
|
mTxLowerLoopThread->join();
|
||||||
|
mRxLowerLoopThread->join();
|
||||||
|
delete mTxLowerLoopThread;
|
||||||
|
delete mRxLowerLoopThread;
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
mRxServiceLoopThreads[i]->cancel();
|
mRxServiceLoopThreads[i]->cancel();
|
||||||
@@ -313,11 +331,6 @@ void Transceiver::stop()
|
|||||||
mTxPriorityQueues[i].clear();
|
mTxPriorityQueues[i].clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
mTxLowerLoopThread->join();
|
|
||||||
mRxLowerLoopThread->join();
|
|
||||||
delete mTxLowerLoopThread;
|
|
||||||
delete mRxLowerLoopThread;
|
|
||||||
|
|
||||||
mOn = false;
|
mOn = false;
|
||||||
LOG(NOTICE) << "Transceiver stopped";
|
LOG(NOTICE) << "Transceiver stopped";
|
||||||
}
|
}
|
||||||
@@ -377,7 +390,8 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
|||||||
state = &mStates[i];
|
state = &mStates[i];
|
||||||
|
|
||||||
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
|
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
|
||||||
LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
|
LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
|
||||||
|
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
|
||||||
if (state->mRetrans)
|
if (state->mRetrans)
|
||||||
updateFillerTable(i, burst);
|
updateFillerTable(i, burst);
|
||||||
delete burst;
|
delete burst;
|
||||||
@@ -428,7 +442,7 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
|
|||||||
case V:
|
case V:
|
||||||
state->fillerModulus[timeslot] = 51;
|
state->fillerModulus[timeslot] = 51;
|
||||||
break;
|
break;
|
||||||
//case V:
|
//case V:
|
||||||
case VII:
|
case VII:
|
||||||
state->fillerModulus[timeslot] = 102;
|
state->fillerModulus[timeslot] = 102;
|
||||||
break;
|
break;
|
||||||
@@ -441,8 +455,8 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||||
size_t chan)
|
size_t chan)
|
||||||
{
|
{
|
||||||
static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
|
static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
|
||||||
static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
|
static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
|
||||||
@@ -485,16 +499,16 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
break;
|
break;
|
||||||
case IV:
|
case IV:
|
||||||
case VI:
|
case VI:
|
||||||
return RACH;
|
return mExtRACH ? EXT_RACH : RACH;
|
||||||
break;
|
break;
|
||||||
case V: {
|
case V: {
|
||||||
int mod51 = burstFN % 51;
|
int mod51 = burstFN % 51;
|
||||||
if ((mod51 <= 36) && (mod51 >= 14))
|
if ((mod51 <= 36) && (mod51 >= 14))
|
||||||
return RACH;
|
return mExtRACH ? EXT_RACH : RACH;
|
||||||
else if ((mod51 == 4) || (mod51 == 5))
|
else if ((mod51 == 4) || (mod51 == 5))
|
||||||
return RACH;
|
return mExtRACH ? EXT_RACH : RACH;
|
||||||
else if ((mod51 == 45) || (mod51 == 46))
|
else if ((mod51 == 45) || (mod51 == 46))
|
||||||
return RACH;
|
return mExtRACH ? EXT_RACH : RACH;
|
||||||
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
|
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
|
||||||
return RACH;
|
return RACH;
|
||||||
else
|
else
|
||||||
@@ -512,7 +526,7 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
case XIII: {
|
case XIII: {
|
||||||
int mod52 = burstFN % 52;
|
int mod52 = burstFN % 52;
|
||||||
if ((mod52 == 12) || (mod52 == 38))
|
if ((mod52 == 12) || (mod52 == 38))
|
||||||
return RACH;
|
return mExtRACH ? EXT_RACH : RACH;
|
||||||
else if ((mod52 == 25) || (mod52 == 51))
|
else if ((mod52 == 25) || (mod52 == 51))
|
||||||
return IDLE;
|
return IDLE;
|
||||||
else
|
else
|
||||||
@@ -531,51 +545,6 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Transceiver::detectBurst(signalVector &burst,
|
|
||||||
complex &, float &toa, CorrType type)
|
|
||||||
{
|
|
||||||
float threshold = 5.0, rc = 0;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case EDGE:
|
|
||||||
rc = detectEdgeBurst(burst, mTSC, threshold, mSPSRx,
|
|
||||||
amp, toa, mMaxExpectedDelayNB);
|
|
||||||
if (rc > 0)
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
type = TSC;
|
|
||||||
case TSC:
|
|
||||||
rc = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx,
|
|
||||||
amp, toa, mMaxExpectedDelayNB);
|
|
||||||
break;
|
|
||||||
case RACH:
|
|
||||||
threshold = 6.0;
|
|
||||||
rc = detectRACHBurst(burst, threshold, mSPSRx, amp, toa,
|
|
||||||
mMaxExpectedDelayAB);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG(ERR) << "Invalid correlation type";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc > 0)
|
|
||||||
return type;
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Demodulate GMSK by direct rotation and soft slicing.
|
|
||||||
*/
|
|
||||||
SoftVector *Transceiver::demodulate(signalVector &burst, complex amp,
|
|
||||||
float toa, CorrType type)
|
|
||||||
{
|
|
||||||
if (type == EDGE)
|
|
||||||
return demodEdgeBurst(burst, mSPSRx, amp, toa);
|
|
||||||
|
|
||||||
return demodulateBurst(burst, mSPSRx, amp, toa);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeToFile(radioVector *radio_burst, size_t chan)
|
void writeToFile(radioVector *radio_burst, size_t chan)
|
||||||
{
|
{
|
||||||
GSM::Time time = radio_burst->getTime();
|
GSM::Time time = radio_burst->getTime();
|
||||||
@@ -588,7 +557,7 @@ void writeToFile(radioVector *radio_burst, size_t chan)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Pull bursts from the FIFO and handle according to the slot
|
* Pull bursts from the FIFO and handle according to the slot
|
||||||
* and burst correlation type. Equalzation is currently disabled.
|
* and burst correlation type. Equalzation is currently disabled.
|
||||||
*/
|
*/
|
||||||
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
||||||
double &timingOffset, double &noise,
|
double &timingOffset, double &noise,
|
||||||
@@ -596,7 +565,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
complex amp;
|
complex amp;
|
||||||
float toa, pow, max = -1.0, avg = 0.0;
|
float toa, max = -1.0, avg = 0.0;
|
||||||
int max_i = -1;
|
int max_i = -1;
|
||||||
signalVector *burst;
|
signalVector *burst;
|
||||||
SoftVector *bits = NULL;
|
SoftVector *bits = NULL;
|
||||||
@@ -612,6 +581,10 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
|||||||
GSM::Time time = radio_burst->getTime();
|
GSM::Time time = radio_burst->getTime();
|
||||||
CorrType type = expectedCorrType(time, chan);
|
CorrType type = expectedCorrType(time, chan);
|
||||||
|
|
||||||
|
/* Enable 8-PSK burst detection if EDGE is enabled */
|
||||||
|
if (mEdge && (type == TSC))
|
||||||
|
type = EDGE;
|
||||||
|
|
||||||
/* Debug: dump bursts to disk */
|
/* Debug: dump bursts to disk */
|
||||||
/* bits 0-7 - chan 0 timeslots
|
/* bits 0-7 - chan 0 timeslots
|
||||||
* bits 8-15 - chan 1 timeslots */
|
* bits 8-15 - chan 1 timeslots */
|
||||||
@@ -627,7 +600,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
|||||||
|
|
||||||
/* Select the diversity channel with highest energy */
|
/* Select the diversity channel with highest energy */
|
||||||
for (size_t i = 0; i < radio_burst->chans(); i++) {
|
for (size_t i = 0; i < radio_burst->chans(); i++) {
|
||||||
energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow);
|
float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
|
||||||
if (pow > max) {
|
if (pow > max) {
|
||||||
max = pow;
|
max = pow;
|
||||||
max_i = i;
|
max_i = i;
|
||||||
@@ -664,8 +637,11 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
|||||||
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
|
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned max_toa = (type == RACH || type == EXT_RACH) ?
|
||||||
|
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
|
||||||
|
|
||||||
/* Detect normal or RACH bursts */
|
/* Detect normal or RACH bursts */
|
||||||
rc = detectBurst(*burst, amp, toa, type);
|
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
|
||||||
|
|
||||||
if (rc > 0) {
|
if (rc > 0) {
|
||||||
type = (CorrType) rc;
|
type = (CorrType) rc;
|
||||||
@@ -680,9 +656,9 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
timingOffset = toa / mSPSRx;
|
timingOffset = toa;
|
||||||
|
|
||||||
bits = demodulate(*burst, amp, toa, type);
|
bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
|
||||||
|
|
||||||
delete radio_burst;
|
delete radio_burst;
|
||||||
return bits;
|
return bits;
|
||||||
@@ -694,87 +670,113 @@ void Transceiver::reset()
|
|||||||
mTxPriorityQueues[i].clear();
|
mTxPriorityQueues[i].clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_PACKET_LENGTH 100
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches a buffer with a command.
|
||||||
|
* @param buf a buffer to look command in
|
||||||
|
* @param cmd a command to look in buffer
|
||||||
|
* @param params pointer to arguments, or NULL
|
||||||
|
* @return true if command matches, otherwise false
|
||||||
|
*/
|
||||||
|
static bool match_cmd(char *buf,
|
||||||
|
const char *cmd, char **params)
|
||||||
|
{
|
||||||
|
size_t cmd_len = strlen(cmd);
|
||||||
|
|
||||||
|
/* Check a command itself */
|
||||||
|
if (strncmp(buf, cmd, cmd_len))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* A command has arguments */
|
||||||
|
if (params != NULL) {
|
||||||
|
/* Make sure there is a space */
|
||||||
|
if (buf[cmd_len] != ' ')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Update external pointer */
|
||||||
|
*params = buf + cmd_len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Transceiver::driveControl(size_t chan)
|
void Transceiver::driveControl(size_t chan)
|
||||||
{
|
{
|
||||||
int MAX_PACKET_LENGTH = 100;
|
char buffer[MAX_PACKET_LENGTH + 1];
|
||||||
|
char response[MAX_PACKET_LENGTH + 1];
|
||||||
|
char *command, *params;
|
||||||
|
int msgLen;
|
||||||
|
|
||||||
// check control socket
|
/* Attempt to read from control socket */
|
||||||
char buffer[MAX_PACKET_LENGTH];
|
msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
|
||||||
int msgLen = -1;
|
if (msgLen < 1)
|
||||||
buffer[0] = '\0';
|
return;
|
||||||
|
|
||||||
msgLen = mCtrlSockets[chan]->read(buffer);
|
/* Zero-terminate received string */
|
||||||
|
buffer[msgLen] = '\0';
|
||||||
|
|
||||||
if (msgLen < 1) {
|
/* Verify a command signature */
|
||||||
|
if (strncmp(buffer, "CMD ", 4)) {
|
||||||
|
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char cmdcheck[4];
|
/* Set command pointer */
|
||||||
char command[MAX_PACKET_LENGTH];
|
command = buffer + 4;
|
||||||
char response[MAX_PACKET_LENGTH];
|
LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
|
||||||
|
|
||||||
sscanf(buffer,"%3s %s",cmdcheck,command);
|
if (match_cmd(command, "POWEROFF", NULL)) {
|
||||||
|
|
||||||
if (!chan)
|
|
||||||
writeClockInterface();
|
|
||||||
|
|
||||||
if (strcmp(cmdcheck,"CMD")!=0) {
|
|
||||||
LOG(WARNING) << "bogus message on control interface";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG(INFO) << "command is " << buffer;
|
|
||||||
|
|
||||||
if (strcmp(command,"POWEROFF")==0) {
|
|
||||||
stop();
|
stop();
|
||||||
sprintf(response,"RSP POWEROFF 0");
|
sprintf(response,"RSP POWEROFF 0");
|
||||||
}
|
} else if (match_cmd(command, "POWERON", NULL)) {
|
||||||
else if (strcmp(command,"POWERON")==0) {
|
if (!start()) {
|
||||||
if (!start())
|
|
||||||
sprintf(response,"RSP POWERON 1");
|
sprintf(response,"RSP POWERON 1");
|
||||||
else
|
} else {
|
||||||
sprintf(response,"RSP POWERON 0");
|
sprintf(response,"RSP POWERON 0");
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
for (int j = 0; j < 8; j++)
|
for (int j = 0; j < 8; j++)
|
||||||
mHandover[i][j] = false;
|
mHandover[i][j] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(command,"HANDOVER")==0){
|
} else if (match_cmd(command, "HANDOVER", ¶ms)) {
|
||||||
int ts=0,ss=0;
|
unsigned ts = 0, ss = 0;
|
||||||
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
|
sscanf(params, "%u %u", &ts, &ss);
|
||||||
mHandover[ts][ss] = true;
|
if (ts > 7 || ss > 7) {
|
||||||
LOG(WARNING) << "HANDOVER RACH at timeslot " << ts << " subslot " << ss;
|
sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
|
||||||
sprintf(response,"RSP HANDOVER 0 %d %d",ts,ss);
|
} else {
|
||||||
}
|
mHandover[ts][ss] = true;
|
||||||
else if (strcmp(command,"NOHANDOVER")==0){
|
sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
|
||||||
int ts=0,ss=0;
|
}
|
||||||
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
|
} else if (match_cmd(command, "NOHANDOVER", ¶ms)) {
|
||||||
mHandover[ts][ss] = false;
|
unsigned ts = 0, ss = 0;
|
||||||
LOG(WARNING) << "NOHANDOVER at timeslot " << ts << " subslot " << ss;
|
sscanf(params, "%u %u", &ts, &ss);
|
||||||
sprintf(response,"RSP NOHANDOVER 0 %d %d",ts,ss);
|
if (ts > 7 || ss > 7) {
|
||||||
}
|
sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
|
||||||
else if (strcmp(command,"SETMAXDLY")==0) {
|
} else {
|
||||||
|
mHandover[ts][ss] = false;
|
||||||
|
sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
|
||||||
|
}
|
||||||
|
} else if (match_cmd(command, "SETMAXDLY", ¶ms)) {
|
||||||
//set expected maximum time-of-arrival
|
//set expected maximum time-of-arrival
|
||||||
int maxDelay;
|
int maxDelay;
|
||||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
|
sscanf(params, "%d", &maxDelay);
|
||||||
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
|
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||||
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
|
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
|
||||||
}
|
} else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) {
|
||||||
else if (strcmp(command,"SETMAXDLYNB")==0) {
|
|
||||||
//set expected maximum time-of-arrival
|
//set expected maximum time-of-arrival
|
||||||
int maxDelay;
|
int maxDelay;
|
||||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
|
sscanf(params, "%d", &maxDelay);
|
||||||
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
|
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||||
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
|
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
|
||||||
}
|
} else if (match_cmd(command, "SETRXGAIN", ¶ms)) {
|
||||||
else if (strcmp(command,"SETRXGAIN")==0) {
|
|
||||||
//set expected maximum time-of-arrival
|
//set expected maximum time-of-arrival
|
||||||
int newGain;
|
int newGain;
|
||||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
|
sscanf(params, "%d", &newGain);
|
||||||
newGain = mRadioInterface->setRxGain(newGain, chan);
|
newGain = mRadioInterface->setRxGain(newGain, chan);
|
||||||
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
|
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
|
||||||
}
|
} else if (match_cmd(command, "NOISELEV", NULL)) {
|
||||||
else if (strcmp(command,"NOISELEV")==0) {
|
|
||||||
if (mOn) {
|
if (mOn) {
|
||||||
float lev = mStates[chan].mNoiseLev;
|
float lev = mStates[chan].mNoiseLev;
|
||||||
sprintf(response,"RSP NOISELEV 0 %d",
|
sprintf(response,"RSP NOISELEV 0 %d",
|
||||||
@@ -783,98 +785,97 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
else {
|
else {
|
||||||
sprintf(response,"RSP NOISELEV 1 0");
|
sprintf(response,"RSP NOISELEV 1 0");
|
||||||
}
|
}
|
||||||
}
|
} else if (match_cmd(command, "SETPOWER", ¶ms)) {
|
||||||
else if (!strcmp(command, "SETPOWER")) {
|
|
||||||
int power;
|
int power;
|
||||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
|
sscanf(params, "%d", &power);
|
||||||
power = mRadioInterface->setPowerAttenuation(power, chan);
|
power = mRadioInterface->setPowerAttenuation(power, chan);
|
||||||
mStates[chan].mPower = power;
|
mStates[chan].mPower = power;
|
||||||
sprintf(response, "RSP SETPOWER 0 %d", power);
|
sprintf(response, "RSP SETPOWER 0 %d", power);
|
||||||
}
|
} else if (match_cmd(command, "ADJPOWER", ¶ms)) {
|
||||||
else if (!strcmp(command,"ADJPOWER")) {
|
|
||||||
int power, step;
|
int power, step;
|
||||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
|
sscanf(params, "%d", &step);
|
||||||
power = mStates[chan].mPower + step;
|
power = mStates[chan].mPower + step;
|
||||||
power = mRadioInterface->setPowerAttenuation(power, chan);
|
power = mRadioInterface->setPowerAttenuation(power, chan);
|
||||||
mStates[chan].mPower = power;
|
mStates[chan].mPower = power;
|
||||||
sprintf(response, "RSP ADJPOWER 0 %d", power);
|
sprintf(response, "RSP ADJPOWER 0 %d", power);
|
||||||
}
|
} else if (match_cmd(command, "RXTUNE", ¶ms)) {
|
||||||
else if (strcmp(command,"RXTUNE")==0) {
|
|
||||||
// tune receiver
|
// tune receiver
|
||||||
int freqKhz;
|
int freqKhz;
|
||||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
sscanf(params, "%d", &freqKhz);
|
||||||
mRxFreq = freqKhz * 1e3;
|
mRxFreq = freqKhz * 1e3;
|
||||||
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
|
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
|
||||||
LOG(ALERT) << "RX failed to tune";
|
LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
|
||||||
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
|
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
|
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
|
||||||
}
|
} else if (match_cmd(command, "TXTUNE", ¶ms)) {
|
||||||
else if (strcmp(command,"TXTUNE")==0) {
|
|
||||||
// tune txmtr
|
// tune txmtr
|
||||||
int freqKhz;
|
int freqKhz;
|
||||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
sscanf(params, "%d", &freqKhz);
|
||||||
mTxFreq = freqKhz * 1e3;
|
mTxFreq = freqKhz * 1e3;
|
||||||
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
|
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
|
||||||
LOG(ALERT) << "TX failed to tune";
|
LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
|
||||||
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
|
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
|
||||||
}
|
} else if (match_cmd(command, "SETTSC", ¶ms)) {
|
||||||
else if (!strcmp(command,"SETTSC")) {
|
|
||||||
// set TSC
|
// set TSC
|
||||||
unsigned TSC;
|
unsigned TSC;
|
||||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
|
sscanf(params, "%u", &TSC);
|
||||||
if (mOn || (TSC < 0) || (TSC > 7))
|
if (TSC > 7) {
|
||||||
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
||||||
else if (chan && (TSC != mTSC))
|
} else {
|
||||||
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
|
||||||
else {
|
|
||||||
mTSC = TSC;
|
mTSC = TSC;
|
||||||
sprintf(response,"RSP SETTSC 0 %d", TSC);
|
sprintf(response,"RSP SETTSC 0 %d", TSC);
|
||||||
}
|
}
|
||||||
}
|
} else if (match_cmd(command, "SETSLOT", ¶ms)) {
|
||||||
else if (strcmp(command,"SETSLOT")==0) {
|
|
||||||
// set slot type
|
// set slot type
|
||||||
int corrCode;
|
int corrCode;
|
||||||
int timeslot;
|
int timeslot;
|
||||||
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode);
|
sscanf(params, "%d %d", ×lot, &corrCode);
|
||||||
if ((timeslot < 0) || (timeslot > 7)) {
|
if ((timeslot < 0) || (timeslot > 7)) {
|
||||||
LOG(WARNING) << "bogus message on control interface";
|
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
|
||||||
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
||||||
setModulus(timeslot, chan);
|
setModulus(timeslot, chan);
|
||||||
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
||||||
|
} else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) {
|
||||||
}
|
|
||||||
else if (strcmp(command,"_SETBURSTTODISKMASK")==0) {
|
|
||||||
// debug command! may change or disapear without notice
|
// debug command! may change or disapear without notice
|
||||||
// set a mask which bursts to dump to disk
|
// set a mask which bursts to dump to disk
|
||||||
int mask;
|
int mask;
|
||||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&mask);
|
sscanf(params, "%d", &mask);
|
||||||
mWriteBurstToDiskMask = mask;
|
mWriteBurstToDiskMask = mask;
|
||||||
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
|
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
|
||||||
}
|
} else {
|
||||||
else {
|
LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
|
||||||
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
|
||||||
sprintf(response,"RSP ERR 1");
|
sprintf(response,"RSP ERR 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
|
||||||
mCtrlSockets[chan]->write(response, strlen(response) + 1);
|
mCtrlSockets[chan]->write(response, strlen(response) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Transceiver::driveTxPriorityQueue(size_t chan)
|
bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||||
{
|
{
|
||||||
char buffer[gSlotLen+50];
|
int burstLen;
|
||||||
|
char buffer[EDGE_BURST_NBITS + 50];
|
||||||
|
|
||||||
// check data socket
|
// check data socket
|
||||||
size_t msgLen = mDataSockets[chan]->read(buffer);
|
size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
|
||||||
|
|
||||||
if (msgLen!=gSlotLen+1+4+1) {
|
if (msgLen == gSlotLen + 1 + 4 + 1) {
|
||||||
|
burstLen = gSlotLen;
|
||||||
|
} else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
|
||||||
|
if (mSPSTx != 4)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
burstLen = EDGE_BURST_NBITS;
|
||||||
|
} else {
|
||||||
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
|
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -885,14 +886,14 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
|||||||
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
|
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
|
||||||
|
|
||||||
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
||||||
|
|
||||||
int RSSI = (int) buffer[5];
|
int RSSI = (int) buffer[5];
|
||||||
static BitVector newBurst(gSlotLen);
|
BitVector newBurst(burstLen);
|
||||||
BitVector::iterator itr = newBurst.begin();
|
BitVector::iterator itr = newBurst.begin();
|
||||||
char *bufferItr = buffer+6;
|
char *bufferItr = buffer+6;
|
||||||
while (itr < newBurst.end())
|
while (itr < newBurst.end())
|
||||||
*itr++ = *bufferItr++;
|
*itr++ = *bufferItr++;
|
||||||
|
|
||||||
GSM::Time currTime = GSM::Time(frameNum,timeSlot);
|
GSM::Time currTime = GSM::Time(frameNum,timeSlot);
|
||||||
|
|
||||||
addRadioVector(chan, newBurst, RSSI, currTime);
|
addRadioVector(chan, newBurst, RSSI, currTime);
|
||||||
@@ -904,18 +905,23 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
|||||||
|
|
||||||
void Transceiver::driveReceiveRadio()
|
void Transceiver::driveReceiveRadio()
|
||||||
{
|
{
|
||||||
if (!mRadioInterface->driveReceiveRadio()) {
|
int rc = mRadioInterface->driveReceiveRadio();
|
||||||
|
if (rc == 0) {
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
} else {
|
} else if (rc < 0) {
|
||||||
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
LOG(FATAL) << "radio Interface receive failed, requesting stop.";
|
||||||
writeClockInterface();
|
osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this);
|
||||||
|
} else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
|
||||||
|
mForceClockInterface = false;
|
||||||
|
writeClockInterface();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transceiver::logRxBurst(SoftVector *burst, GSM::Time time, double dbm,
|
void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
|
||||||
double rssi, double noise, double toa)
|
double rssi, double noise, double toa)
|
||||||
{
|
{
|
||||||
LOG(DEBUG) << std::fixed << std::right
|
LOG(DEBUG) << std::fixed << std::right
|
||||||
|
<< " chan: " << chan
|
||||||
<< " time: " << time
|
<< " time: " << time
|
||||||
<< " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
|
<< " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
|
||||||
<< "dBFS/" << std::setw(6) << -dbm << "dBm"
|
<< "dBFS/" << std::setw(6) << -dbm << "dBm"
|
||||||
@@ -941,6 +947,9 @@ void Transceiver::driveReceiveFIFO(size_t chan)
|
|||||||
if (!rxBurst)
|
if (!rxBurst)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Convert -1..+1 soft bits to 0..1 soft bits
|
||||||
|
vectorSlicer(rxBurst);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* EDGE demodulator returns 444 (148 * 3) bits
|
* EDGE demodulator returns 444 (148 * 3) bits
|
||||||
*/
|
*/
|
||||||
@@ -948,7 +957,7 @@ void Transceiver::driveReceiveFIFO(size_t chan)
|
|||||||
nbits = gSlotLen * 3;
|
nbits = gSlotLen * 3;
|
||||||
|
|
||||||
dBm = RSSI + rssiOffset;
|
dBm = RSSI + rssiOffset;
|
||||||
logRxBurst(rxBurst, burstTime, dBm, RSSI, noise, TOA);
|
logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
|
||||||
|
|
||||||
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
|
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
|
||||||
|
|
||||||
@@ -974,7 +983,7 @@ void Transceiver::driveTxFIFO()
|
|||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Features a carefully controlled latency mechanism, to
|
Features a carefully controlled latency mechanism, to
|
||||||
assure that transmit packets arrive at the radio/USRP
|
assure that transmit packets arrive at the radio/USRP
|
||||||
before they need to be transmitted.
|
before they need to be transmitted.
|
||||||
|
|
||||||
@@ -985,7 +994,7 @@ void Transceiver::driveTxFIFO()
|
|||||||
|
|
||||||
|
|
||||||
RadioClock *radioClock = (mRadioInterface->getClock());
|
RadioClock *radioClock = (mRadioInterface->getClock());
|
||||||
|
|
||||||
if (mOn) {
|
if (mOn) {
|
||||||
//radioClock->wait(); // wait until clock updates
|
//radioClock->wait(); // wait until clock updates
|
||||||
LOG(DEBUG) << "radio clock " << radioClock->get();
|
LOG(DEBUG) << "radio clock " << radioClock->get();
|
||||||
@@ -997,14 +1006,15 @@ void Transceiver::driveTxFIFO()
|
|||||||
// only update latency at the defined frame interval
|
// only update latency at the defined frame interval
|
||||||
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
|
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
|
||||||
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
|
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
|
||||||
LOG(INFO) << "new latency: " << mTransmitLatency;
|
LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
|
||||||
|
<< radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
|
||||||
mLatencyUpdateTime = radioClock->get();
|
mLatencyUpdateTime = radioClock->get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// if underrun hasn't occurred in the last sec (216 frames) drop
|
// if underrun hasn't occurred in the last sec (216 frames) drop
|
||||||
// transmit latency by a timeslot
|
// transmit latency by a timeslot
|
||||||
if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
|
if (mTransmitLatency > mRadioInterface->minLatency()) {
|
||||||
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
|
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
|
||||||
mTransmitLatency.decTN();
|
mTransmitLatency.decTN();
|
||||||
LOG(INFO) << "reduced latency: " << mTransmitLatency;
|
LOG(INFO) << "reduced latency: " << mTransmitLatency;
|
||||||
@@ -1040,11 +1050,15 @@ void Transceiver::writeClockInterface()
|
|||||||
|
|
||||||
void *RxUpperLoopAdapter(TransceiverChannel *chan)
|
void *RxUpperLoopAdapter(TransceiverChannel *chan)
|
||||||
{
|
{
|
||||||
|
char thread_name[16];
|
||||||
Transceiver *trx = chan->trx;
|
Transceiver *trx = chan->trx;
|
||||||
size_t num = chan->num;
|
size_t num = chan->num;
|
||||||
|
|
||||||
delete chan;
|
delete chan;
|
||||||
|
|
||||||
|
snprintf(thread_name, 16, "RxUpper%zu", num);
|
||||||
|
set_selfthread_name(thread_name);
|
||||||
|
|
||||||
trx->setPriority(0.42);
|
trx->setPriority(0.42);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -1056,6 +1070,8 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan)
|
|||||||
|
|
||||||
void *RxLowerLoopAdapter(Transceiver *transceiver)
|
void *RxLowerLoopAdapter(Transceiver *transceiver)
|
||||||
{
|
{
|
||||||
|
set_selfthread_name("RxLower");
|
||||||
|
|
||||||
transceiver->setPriority(0.45);
|
transceiver->setPriority(0.45);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -1067,6 +1083,8 @@ void *RxLowerLoopAdapter(Transceiver *transceiver)
|
|||||||
|
|
||||||
void *TxLowerLoopAdapter(Transceiver *transceiver)
|
void *TxLowerLoopAdapter(Transceiver *transceiver)
|
||||||
{
|
{
|
||||||
|
set_selfthread_name("TxLower");
|
||||||
|
|
||||||
transceiver->setPriority(0.44);
|
transceiver->setPriority(0.44);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -1078,11 +1096,15 @@ void *TxLowerLoopAdapter(Transceiver *transceiver)
|
|||||||
|
|
||||||
void *ControlServiceLoopAdapter(TransceiverChannel *chan)
|
void *ControlServiceLoopAdapter(TransceiverChannel *chan)
|
||||||
{
|
{
|
||||||
|
char thread_name[16];
|
||||||
Transceiver *trx = chan->trx;
|
Transceiver *trx = chan->trx;
|
||||||
size_t num = chan->num;
|
size_t num = chan->num;
|
||||||
|
|
||||||
delete chan;
|
delete chan;
|
||||||
|
|
||||||
|
snprintf(thread_name, 16, "CtrlService%zu", num);
|
||||||
|
set_selfthread_name(thread_name);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
trx->driveControl(num);
|
trx->driveControl(num);
|
||||||
pthread_testcancel();
|
pthread_testcancel();
|
||||||
@@ -1092,11 +1114,15 @@ void *ControlServiceLoopAdapter(TransceiverChannel *chan)
|
|||||||
|
|
||||||
void *TxUpperLoopAdapter(TransceiverChannel *chan)
|
void *TxUpperLoopAdapter(TransceiverChannel *chan)
|
||||||
{
|
{
|
||||||
|
char thread_name[16];
|
||||||
Transceiver *trx = chan->trx;
|
Transceiver *trx = chan->trx;
|
||||||
size_t num = chan->num;
|
size_t num = chan->num;
|
||||||
|
|
||||||
delete chan;
|
delete chan;
|
||||||
|
|
||||||
|
snprintf(thread_name, 16, "TxUpper%zu", num);
|
||||||
|
set_selfthread_name(thread_name);
|
||||||
|
|
||||||
trx->setPriority(0.40);
|
trx->setPriority(0.40);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|||||||
@@ -30,6 +30,11 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/signal.h>
|
||||||
|
#include "config_defs.h"
|
||||||
|
}
|
||||||
|
|
||||||
class Transceiver;
|
class Transceiver;
|
||||||
|
|
||||||
/** Channel descriptor for transceiver object and channel number pair */
|
/** Channel descriptor for transceiver object and channel number pair */
|
||||||
@@ -54,7 +59,7 @@ struct TransceiverState {
|
|||||||
~TransceiverState();
|
~TransceiverState();
|
||||||
|
|
||||||
/* Initialize a multiframe slot in the filler table */
|
/* Initialize a multiframe slot in the filler table */
|
||||||
bool init(int filler, size_t sps, float scale, size_t rtsc);
|
bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
|
||||||
|
|
||||||
int chanType[8];
|
int chanType[8];
|
||||||
|
|
||||||
@@ -89,15 +94,17 @@ struct TransceiverState {
|
|||||||
/** The Transceiver class, responsible for physical layer of basestation */
|
/** The Transceiver class, responsible for physical layer of basestation */
|
||||||
class Transceiver {
|
class Transceiver {
|
||||||
public:
|
public:
|
||||||
/** Transceiver constructor
|
/** Transceiver constructor
|
||||||
@param wBasePort base port number of UDP sockets
|
@param wBasePort base port number of UDP sockets
|
||||||
@param TRXAddress IP address of the TRX manager, as a string
|
@param TRXAddress IP address of the TRX, as a string
|
||||||
|
@param GSMcoreAddress IP address of the GSM core, as a string
|
||||||
@param wSPS number of samples per GSM symbol
|
@param wSPS number of samples per GSM symbol
|
||||||
@param wTransmitLatency initial setting of transmit latency
|
@param wTransmitLatency initial setting of transmit latency
|
||||||
@param radioInterface associated radioInterface object
|
@param radioInterface associated radioInterface object
|
||||||
*/
|
*/
|
||||||
Transceiver(int wBasePort,
|
Transceiver(int wBasePort,
|
||||||
const char *TRXAddress,
|
const char *TRXAddress,
|
||||||
|
const char *GSMcoreAddress,
|
||||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface,
|
RadioInterface *wRadioInterface,
|
||||||
@@ -107,7 +114,8 @@ public:
|
|||||||
~Transceiver();
|
~Transceiver();
|
||||||
|
|
||||||
/** Start the control loop */
|
/** Start the control loop */
|
||||||
bool init(int filler, size_t rtsc);
|
bool init(FillerType filler, size_t rtsc, unsigned rach_delay,
|
||||||
|
bool edge, bool ext_rach);
|
||||||
|
|
||||||
/** attach the radioInterface receive FIFO */
|
/** attach the radioInterface receive FIFO */
|
||||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||||
@@ -122,6 +130,8 @@ public:
|
|||||||
/** accessor for number of channels */
|
/** accessor for number of channels */
|
||||||
size_t numChans() const { return mChans; };
|
size_t numChans() const { return mChans; };
|
||||||
|
|
||||||
|
void setSignalHandler(osmo_signal_cbfn cbfn);
|
||||||
|
|
||||||
/** Codes for channel combinations */
|
/** Codes for channel combinations */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FILL, ///< Channel is transmitted, but unused
|
FILL, ///< Channel is transmitted, but unused
|
||||||
@@ -142,26 +152,10 @@ public:
|
|||||||
LOOPBACK ///< similar go VII, used in loopback testing
|
LOOPBACK ///< similar go VII, used in loopback testing
|
||||||
} ChannelCombination;
|
} ChannelCombination;
|
||||||
|
|
||||||
/** Codes for burst types of received bursts*/
|
|
||||||
typedef enum {
|
|
||||||
OFF, ///< timeslot is off
|
|
||||||
TSC, ///< timeslot should contain a normal burst
|
|
||||||
RACH, ///< timeslot should contain an access burst
|
|
||||||
EDGE, ///< timeslot should contain an EDGE burst
|
|
||||||
IDLE ///< timeslot is an idle (or dummy) burst
|
|
||||||
} CorrType;
|
|
||||||
|
|
||||||
enum FillerType {
|
|
||||||
FILLER_DUMMY,
|
|
||||||
FILLER_ZERO,
|
|
||||||
FILLER_NORM_RAND,
|
|
||||||
FILLER_EDGE_RAND,
|
|
||||||
FILLER_ACCESS_RAND,
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mBasePort;
|
int mBasePort;
|
||||||
std::string mAddr;
|
std::string mLocalAddr;
|
||||||
|
std::string mRemoteAddr;
|
||||||
|
|
||||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||||
@@ -178,7 +172,7 @@ private:
|
|||||||
|
|
||||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||||
|
|
||||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||||
@@ -187,6 +181,8 @@ private:
|
|||||||
|
|
||||||
double rssiOffset; ///< RSSI to dBm conversion offset
|
double rssiOffset; ///< RSSI to dBm conversion offset
|
||||||
|
|
||||||
|
osmo_signal_cbfn *sig_cbfn; ///< Registered Signal Handler to announce events.
|
||||||
|
|
||||||
/** modulate and add a burst to the transmit queue */
|
/** modulate and add a burst to the transmit queue */
|
||||||
void addRadioVector(size_t chan, BitVector &bits,
|
void addRadioVector(size_t chan, BitVector &bits,
|
||||||
int RSSI, GSM::Time &wTime);
|
int RSSI, GSM::Time &wTime);
|
||||||
@@ -211,19 +207,14 @@ private:
|
|||||||
/** send messages over the clock socket */
|
/** send messages over the clock socket */
|
||||||
void writeClockInterface(void);
|
void writeClockInterface(void);
|
||||||
|
|
||||||
/** Detectbursts */
|
|
||||||
int detectBurst(signalVector &burst,
|
|
||||||
complex &, float &toa, CorrType type);
|
|
||||||
|
|
||||||
/** Demodulate burst and output soft bits */
|
|
||||||
SoftVector *demodulate(signalVector &burst,
|
|
||||||
complex amp, float toa, CorrType type);
|
|
||||||
|
|
||||||
int mSPSTx; ///< number of samples per Tx symbol
|
int mSPSTx; ///< number of samples per Tx symbol
|
||||||
int mSPSRx; ///< number of samples per Rx symbol
|
int mSPSRx; ///< number of samples per Rx symbol
|
||||||
size_t mChans;
|
size_t mChans;
|
||||||
|
|
||||||
|
bool mExtRACH;
|
||||||
|
bool mEdge;
|
||||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
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
|
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||||
double mTxFreq; ///< the transmit frequency
|
double mTxFreq; ///< the transmit frequency
|
||||||
double mRxFreq; ///< the receive frequency
|
double mRxFreq; ///< the receive frequency
|
||||||
@@ -276,7 +267,7 @@ protected:
|
|||||||
/** set priority on current thread */
|
/** set priority on current thread */
|
||||||
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
|
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
|
||||||
|
|
||||||
void logRxBurst(SoftVector *burst, GSM::Time time, double dbm,
|
void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
|
||||||
double rssi, double noise, double toa);
|
double rssi, double noise, double toa);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -291,4 +282,3 @@ void *ControlServiceLoopAdapter(TransceiverChannel *);
|
|||||||
|
|
||||||
/** transmit queueing thread loop */
|
/** transmit queueing thread loop */
|
||||||
void *TxUpperLoopAdapter(TransceiverChannel *);
|
void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
8
Transceiver52M/arch/Makefile.am
Normal file
8
Transceiver52M/arch/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
|
SUBDIRS = common
|
||||||
|
if ARCH_ARM
|
||||||
|
SUBDIRS += arm
|
||||||
|
else
|
||||||
|
SUBDIRS += x86
|
||||||
|
endif
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
if ARCH_ARM
|
|
||||||
if ARCH_ARM_A15
|
if ARCH_ARM_A15
|
||||||
ARCH_FLAGS = -mfpu=neon-vfpv4
|
ARCH_FLAGS = -mfpu=neon-vfpv4
|
||||||
else
|
else
|
||||||
ARCH_FLAGS = -mfpu=neon
|
ARCH_FLAGS = -mfpu=neon
|
||||||
endif
|
endif
|
||||||
|
|
||||||
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I../common
|
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common
|
||||||
AM_CCASFLAGS = $(ARCH_FLAGS)
|
AM_CCASFLAGS = $(ARCH_FLAGS)
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libarch.la
|
noinst_LTLIBRARIES = libarch.la
|
||||||
|
|
||||||
|
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
|
||||||
|
|
||||||
libarch_la_SOURCES = \
|
libarch_la_SOURCES = \
|
||||||
../common/convolve_base.c \
|
|
||||||
convert.c \
|
convert.c \
|
||||||
convert_neon.S \
|
convert_neon.S \
|
||||||
convolve.c \
|
convolve.c \
|
||||||
@@ -20,4 +20,3 @@ libarch_la_SOURCES = \
|
|||||||
scale_neon.S \
|
scale_neon.S \
|
||||||
mult.c \
|
mult.c \
|
||||||
mult_neon.S
|
mult_neon.S
|
||||||
endif
|
|
||||||
@@ -28,19 +28,9 @@
|
|||||||
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
|
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
|
||||||
void neon_convert_si16_ps_4n(float *, const short *, int);
|
void neon_convert_si16_ps_4n(float *, const short *, int);
|
||||||
|
|
||||||
#ifndef HAVE_NEON
|
void convert_init(void) {
|
||||||
static void convert_si16_ps(float *out, const short *in, int len)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
out[i] = in[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_ps_si16(short *out, const float *in, float scale, int len)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
out[i] = in[i] * scale;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* 4*N 16-bit signed integer conversion with remainder */
|
/* 4*N 16-bit signed integer conversion with remainder */
|
||||||
static void neon_convert_si16_ps(float *out,
|
static void neon_convert_si16_ps(float *out,
|
||||||
const short *in,
|
const short *in,
|
||||||
@@ -67,7 +57,6 @@ static void neon_convert_ps_si16(short *out,
|
|||||||
for (int i = 0; i < len % 4; i++)
|
for (int i = 0; i < len % 4; i++)
|
||||||
out[start + i] = (short) (in[start + i] * (*scale));
|
out[start + i] = (short) (in[start + i] * (*scale));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void convert_float_short(short *out, const float *in, float scale, int len)
|
void convert_float_short(short *out, const float *in, float scale, int len)
|
||||||
{
|
{
|
||||||
@@ -79,7 +68,7 @@ void convert_float_short(short *out, const float *in, float scale, int len)
|
|||||||
else
|
else
|
||||||
neon_convert_ps_si16_4n(out, in, q, len >> 2);
|
neon_convert_ps_si16_4n(out, in, q, len >> 2);
|
||||||
#else
|
#else
|
||||||
convert_ps_si16(out, in, scale, len);
|
base_convert_float_short(out, in, scale, len);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +80,6 @@ void convert_short_float(float *out, const short *in, int len)
|
|||||||
else
|
else
|
||||||
neon_convert_si16_ps_4n(out, in, len >> 2);
|
neon_convert_si16_ps_4n(out, in, len >> 2);
|
||||||
#else
|
#else
|
||||||
convert_si16_ps(out, in, len);
|
base_convert_short_float(out, in, len);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -29,17 +29,15 @@
|
|||||||
int _base_convolve_real(float *x, int x_len,
|
int _base_convolve_real(float *x, int x_len,
|
||||||
float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len);
|
||||||
int step, int offset);
|
|
||||||
|
|
||||||
int _base_convolve_complex(float *x, int x_len,
|
int _base_convolve_complex(float *x, int x_len,
|
||||||
float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len);
|
||||||
int step, int offset);
|
|
||||||
|
|
||||||
int bounds_check(int x_len, int h_len, int y_len,
|
int bounds_check(int x_len, int h_len, int y_len,
|
||||||
int start, int len, int step);
|
int start, int len);
|
||||||
|
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
/* Calls into NEON assembler */
|
/* Calls into NEON assembler */
|
||||||
@@ -58,39 +56,43 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* API: Initalize convolve module */
|
||||||
|
void convolve_init(void)
|
||||||
|
{
|
||||||
|
/* Stub */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* API: Aligned complex-real */
|
/* API: Aligned complex-real */
|
||||||
int convolve_real(float *x, int x_len,
|
int convolve_real(float *x, int x_len,
|
||||||
float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len)
|
||||||
int step, int offset)
|
|
||||||
{
|
{
|
||||||
void (*conv_func)(float *, float *, float *, int) = NULL;
|
void (*conv_func)(float *, float *, float *, int) = NULL;
|
||||||
|
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
|
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
if (step <= 4) {
|
switch (h_len) {
|
||||||
switch (h_len) {
|
case 4:
|
||||||
case 4:
|
conv_func = neon_conv_real4;
|
||||||
conv_func = neon_conv_real4;
|
break;
|
||||||
break;
|
case 8:
|
||||||
case 8:
|
conv_func = neon_conv_real8;
|
||||||
conv_func = neon_conv_real8;
|
break;
|
||||||
break;
|
case 12:
|
||||||
case 12:
|
conv_func = neon_conv_real12;
|
||||||
conv_func = neon_conv_real12;
|
break;
|
||||||
break;
|
case 16:
|
||||||
case 16:
|
conv_func = neon_conv_real16;
|
||||||
conv_func = neon_conv_real16;
|
break;
|
||||||
break;
|
case 20:
|
||||||
case 20:
|
conv_func = neon_conv_real20;
|
||||||
conv_func = neon_conv_real20;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (conv_func) {
|
if (conv_func) {
|
||||||
@@ -100,7 +102,7 @@ int convolve_real(float *x, int x_len,
|
|||||||
_base_convolve_real(x, x_len,
|
_base_convolve_real(x, x_len,
|
||||||
h, h_len,
|
h, h_len,
|
||||||
y, y_len,
|
y, y_len,
|
||||||
start, len, step, offset);
|
start, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@@ -111,18 +113,17 @@ int convolve_real(float *x, int x_len,
|
|||||||
int convolve_complex(float *x, int x_len,
|
int convolve_complex(float *x, int x_len,
|
||||||
float *h, int h_len,
|
float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len)
|
||||||
int step, int offset)
|
|
||||||
{
|
{
|
||||||
void (*conv_func)(float *, float *, float *, int, int) = NULL;
|
void (*conv_func)(float *, float *, float *, int, int) = NULL;
|
||||||
|
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
|
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
if (step <= 4 && !(h_len % 4))
|
if (!(h_len % 4))
|
||||||
conv_func = neon_conv_cmplx_4n;
|
conv_func = neon_conv_cmplx_4n;
|
||||||
#endif
|
#endif
|
||||||
if (conv_func) {
|
if (conv_func) {
|
||||||
@@ -132,7 +133,7 @@ int convolve_complex(float *x, int x_len,
|
|||||||
_base_convolve_complex(x, x_len,
|
_base_convolve_complex(x, x_len,
|
||||||
h, h_len,
|
h, h_len,
|
||||||
y, y_len,
|
y, y_len,
|
||||||
start, len, step, offset);
|
start, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@@ -92,8 +92,8 @@ neon_conv_real12:
|
|||||||
vld2.32 {q8-q9}, [r4], r6
|
vld2.32 {q8-q9}, [r4], r6
|
||||||
vld2.32 {q10-q11}, [r5], r6
|
vld2.32 {q10-q11}, [r5], r6
|
||||||
#ifdef HAVE_NEON_FMA
|
#ifdef HAVE_NEON_FMA
|
||||||
vfma.f32 q1, q6, q0
|
vmul.f32 q1, q6, q0
|
||||||
vfma.f32 q3, q7, q0
|
vmul.f32 q3, q7, q0
|
||||||
vfma.f32 q1, q8, q2
|
vfma.f32 q1, q8, q2
|
||||||
vfma.f32 q3, q9, q2
|
vfma.f32 q3, q9, q2
|
||||||
vfma.f32 q1, q10, q4
|
vfma.f32 q1, q10, q4
|
||||||
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
|
||||||
@@ -2,6 +2,14 @@
|
|||||||
#define _CONVERT_H_
|
#define _CONVERT_H_
|
||||||
|
|
||||||
void convert_float_short(short *out, const float *in, float scale, int len);
|
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 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_ */
|
#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];
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,30 +1,28 @@
|
|||||||
#ifndef _CONVOLVE_H_
|
#ifndef _CONVOLVE_H_
|
||||||
#define _CONVOLVE_H_
|
#define _CONVOLVE_H_
|
||||||
|
|
||||||
void *convolve_h_alloc(int num);
|
void *convolve_h_alloc(size_t num);
|
||||||
|
|
||||||
int convolve_real(const float *x, int x_len,
|
int convolve_real(const float *x, int x_len,
|
||||||
const float *h, int h_len,
|
const float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len);
|
||||||
int step, int offset);
|
|
||||||
|
|
||||||
int convolve_complex(const float *x, int x_len,
|
int convolve_complex(const float *x, int x_len,
|
||||||
const float *h, int h_len,
|
const float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len);
|
||||||
int step, int offset);
|
|
||||||
|
|
||||||
int base_convolve_real(const float *x, int x_len,
|
int base_convolve_real(const float *x, int x_len,
|
||||||
const float *h, int h_len,
|
const float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len);
|
||||||
int step, int offset);
|
|
||||||
|
|
||||||
int base_convolve_complex(const float *x, int x_len,
|
int base_convolve_complex(const float *x, int x_len,
|
||||||
const float *h, int h_len,
|
const float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len);
|
||||||
int step, int offset);
|
|
||||||
|
void convolve_init(void);
|
||||||
|
|
||||||
#endif /* _CONVOLVE_H_ */
|
#endif /* _CONVOLVE_H_ */
|
||||||
@@ -41,17 +41,17 @@ static void mac_cmplx(const float *x, const float *h, float *y)
|
|||||||
|
|
||||||
/* Base vector complex-complex multiply and accumulate */
|
/* Base vector complex-complex multiply and accumulate */
|
||||||
static void mac_real_vec_n(const float *x, const float *h, float *y,
|
static void mac_real_vec_n(const float *x, const float *h, float *y,
|
||||||
int len, int step, int offset)
|
int len)
|
||||||
{
|
{
|
||||||
for (int i = offset; i < len; i += step)
|
for (int i=0; i<len; i++)
|
||||||
mac_real(&x[2 * i], &h[2 * i], y);
|
mac_real(&x[2 * i], &h[2 * i], y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Base vector complex-complex multiply and accumulate */
|
/* Base vector complex-complex multiply and accumulate */
|
||||||
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
|
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
|
||||||
int len, int step, int offset)
|
int len)
|
||||||
{
|
{
|
||||||
for (int i = offset; i < len; i += step)
|
for (int i=0; i<len; i++)
|
||||||
mac_cmplx(&x[2 * i], &h[2 * i], y);
|
mac_cmplx(&x[2 * i], &h[2 * i], y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,14 +59,12 @@ static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
|
|||||||
int _base_convolve_real(const float *x, int x_len,
|
int _base_convolve_real(const float *x, int x_len,
|
||||||
const float *h, int h_len,
|
const float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len)
|
||||||
int step, int offset)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
||||||
h,
|
h,
|
||||||
&y[2 * i], h_len,
|
&y[2 * i], h_len);
|
||||||
step, offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@@ -76,14 +74,13 @@ int _base_convolve_real(const float *x, int x_len,
|
|||||||
int _base_convolve_complex(const float *x, int x_len,
|
int _base_convolve_complex(const float *x, int x_len,
|
||||||
const float *h, int h_len,
|
const float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len)
|
||||||
int step, int offset)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
|
||||||
h,
|
h,
|
||||||
&y[2 * i],
|
&y[2 * i],
|
||||||
h_len, step, offset);
|
h_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@@ -91,10 +88,10 @@ int _base_convolve_complex(const float *x, int x_len,
|
|||||||
|
|
||||||
/* Buffer validity checks */
|
/* Buffer validity checks */
|
||||||
int bounds_check(int x_len, int h_len, int y_len,
|
int bounds_check(int x_len, int h_len, int y_len,
|
||||||
int start, int len, int step)
|
int start, int len)
|
||||||
{
|
{
|
||||||
if ((x_len < 1) || (h_len < 1) ||
|
if ((x_len < 1) || (h_len < 1) ||
|
||||||
(y_len < 1) || (len < 1) || (step < 1)) {
|
(y_len < 1) || (len < 1)) {
|
||||||
fprintf(stderr, "Convolve: Invalid input\n");
|
fprintf(stderr, "Convolve: Invalid input\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -113,10 +110,9 @@ int bounds_check(int x_len, int h_len, int y_len,
|
|||||||
int base_convolve_real(const float *x, int x_len,
|
int base_convolve_real(const float *x, int x_len,
|
||||||
const float *h, int h_len,
|
const float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len)
|
||||||
int step, int offset)
|
|
||||||
{
|
{
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
@@ -124,17 +120,16 @@ int base_convolve_real(const float *x, int x_len,
|
|||||||
return _base_convolve_real(x, x_len,
|
return _base_convolve_real(x, x_len,
|
||||||
h, h_len,
|
h, h_len,
|
||||||
y, y_len,
|
y, y_len,
|
||||||
start, len, step, offset);
|
start, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* API: Non-aligned (no SSE) complex-complex */
|
/* API: Non-aligned (no SSE) complex-complex */
|
||||||
int base_convolve_complex(const float *x, int x_len,
|
int base_convolve_complex(const float *x, int x_len,
|
||||||
const float *h, int h_len,
|
const float *h, int h_len,
|
||||||
float *y, int y_len,
|
float *y, int y_len,
|
||||||
int start, int len,
|
int start, int len)
|
||||||
int step, int offset)
|
|
||||||
{
|
{
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
@@ -142,11 +137,11 @@ int base_convolve_complex(const float *x, int x_len,
|
|||||||
return _base_convolve_complex(x, x_len,
|
return _base_convolve_complex(x, x_len,
|
||||||
h, h_len,
|
h, h_len,
|
||||||
y, y_len,
|
y, y_len,
|
||||||
start, len, step, offset);
|
start, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Aligned filter tap allocation */
|
/* Aligned filter tap allocation */
|
||||||
void *convolve_h_alloc(int len)
|
void *convolve_h_alloc(size_t len)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_SSE3
|
#ifdef HAVE_SSE3
|
||||||
return memalign(16, len * 2 * sizeof(float));
|
return memalign(16, len * 2 * sizeof(float));
|
||||||
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_ */
|
||||||
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);
|
||||||
154
Transceiver52M/arch/x86/convolve.c
Normal file
154
Transceiver52M/arch/x86/convolve.c
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* SSE Convolution
|
||||||
|
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "convolve.h"
|
||||||
|
#include "convolve_sse_3.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Architecture dependant function pointers */
|
||||||
|
struct convolve_cpu_context {
|
||||||
|
void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_cmplx_8n) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_cmplx) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_real4) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_real8) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_real12) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_real16) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_real20) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_real4n) (const float *, int, const float *, int, float *,
|
||||||
|
int, int, int);
|
||||||
|
void (*conv_real) (const float *, int, const float *, int, float *, int,
|
||||||
|
int, int);
|
||||||
|
};
|
||||||
|
static struct convolve_cpu_context c;
|
||||||
|
|
||||||
|
/* Forward declarations from base implementation */
|
||||||
|
int _base_convolve_real(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
int _base_convolve_complex(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
int bounds_check(int x_len, int h_len, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
/* API: Initalize convolve module */
|
||||||
|
void convolve_init(void)
|
||||||
|
{
|
||||||
|
c.conv_cmplx_4n = (void *)_base_convolve_complex;
|
||||||
|
c.conv_cmplx_8n = (void *)_base_convolve_complex;
|
||||||
|
c.conv_cmplx = (void *)_base_convolve_complex;
|
||||||
|
c.conv_real4 = (void *)_base_convolve_real;
|
||||||
|
c.conv_real8 = (void *)_base_convolve_real;
|
||||||
|
c.conv_real12 = (void *)_base_convolve_real;
|
||||||
|
c.conv_real16 = (void *)_base_convolve_real;
|
||||||
|
c.conv_real20 = (void *)_base_convolve_real;
|
||||||
|
c.conv_real4n = (void *)_base_convolve_real;
|
||||||
|
c.conv_real = (void *)_base_convolve_real;
|
||||||
|
|
||||||
|
#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
|
||||||
|
if (__builtin_cpu_supports("sse3")) {
|
||||||
|
c.conv_cmplx_4n = sse_conv_cmplx_4n;
|
||||||
|
c.conv_cmplx_8n = sse_conv_cmplx_8n;
|
||||||
|
c.conv_real4 = sse_conv_real4;
|
||||||
|
c.conv_real8 = sse_conv_real8;
|
||||||
|
c.conv_real12 = sse_conv_real12;
|
||||||
|
c.conv_real16 = sse_conv_real16;
|
||||||
|
c.conv_real20 = sse_conv_real20;
|
||||||
|
c.conv_real4n = sse_conv_real4n;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* API: Aligned complex-real */
|
||||||
|
int convolve_real(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len, int start, int len)
|
||||||
|
{
|
||||||
|
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
|
|
||||||
|
switch (h_len) {
|
||||||
|
case 4:
|
||||||
|
c.conv_real4(x, x_len, h, h_len, y, y_len, start, len);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
c.conv_real8(x, x_len, h, h_len, y, y_len, start, len);
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
c.conv_real12(x, x_len, h, h_len, y, y_len, start, len);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
c.conv_real16(x, x_len, h, h_len, y, y_len, start, len);
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
c.conv_real20(x, x_len, h, h_len, y, y_len, start, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (!(h_len % 4))
|
||||||
|
c.conv_real4n(x, x_len, h, h_len, y, y_len,
|
||||||
|
start, len);
|
||||||
|
else
|
||||||
|
c.conv_real(x, x_len, h, h_len, y, y_len, start,
|
||||||
|
len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* API: Aligned complex-complex */
|
||||||
|
int convolve_complex(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len)
|
||||||
|
{
|
||||||
|
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(y, 0, len * 2 * sizeof(float));
|
||||||
|
|
||||||
|
if (!(h_len % 8))
|
||||||
|
c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start, len);
|
||||||
|
else if (!(h_len % 4))
|
||||||
|
c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start, len);
|
||||||
|
else
|
||||||
|
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
@@ -20,40 +20,31 @@
|
|||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "convolve.h"
|
#include "convolve_sse_3.h"
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
#ifdef HAVE_SSE3
|
#ifdef HAVE_SSE3
|
||||||
#include <xmmintrin.h>
|
#include <xmmintrin.h>
|
||||||
#include <pmmintrin.h>
|
#include <pmmintrin.h>
|
||||||
|
|
||||||
/* 4-tap SSE complex-real convolution */
|
/* 4-tap SSE complex-real convolution */
|
||||||
static void sse_conv_real4(const float *restrict x,
|
void sse_conv_real4(const float *x, int x_len,
|
||||||
const float *restrict h,
|
const float *h, int h_len,
|
||||||
float *restrict y,
|
float *y, int y_len,
|
||||||
int len)
|
int start, int len)
|
||||||
{
|
{
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||||
|
|
||||||
|
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||||
|
|
||||||
/* Load (aligned) filter taps */
|
/* Load (aligned) filter taps */
|
||||||
m0 = _mm_load_ps(&h[0]);
|
m0 = _mm_load_ps(&h[0]);
|
||||||
m1 = _mm_load_ps(&h[4]);
|
m1 = _mm_load_ps(&h[4]);
|
||||||
@@ -61,8 +52,8 @@ static void sse_conv_real4(const float *restrict x,
|
|||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
/* Load (unaligned) input data */
|
/* Load (unaligned) input data */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
@@ -81,13 +72,17 @@ static void sse_conv_real4(const float *restrict x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 8-tap SSE complex-real convolution */
|
/* 8-tap SSE complex-real convolution */
|
||||||
static void sse_conv_real8(const float *restrict x,
|
void sse_conv_real8(const float *x, int x_len,
|
||||||
const float *restrict h,
|
const float *h, int h_len,
|
||||||
float *restrict y,
|
float *y, int y_len,
|
||||||
int len)
|
int start, int len)
|
||||||
{
|
{
|
||||||
|
/* See NOTE in sse_conv_real4() */
|
||||||
|
|
||||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
|
__m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
|
||||||
|
|
||||||
|
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||||
|
|
||||||
/* Load (aligned) filter taps */
|
/* Load (aligned) filter taps */
|
||||||
m0 = _mm_load_ps(&h[0]);
|
m0 = _mm_load_ps(&h[0]);
|
||||||
m1 = _mm_load_ps(&h[4]);
|
m1 = _mm_load_ps(&h[4]);
|
||||||
@@ -99,10 +94,10 @@ static void sse_conv_real8(const float *restrict x,
|
|||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
/* Load (unaligned) input data */
|
/* Load (unaligned) input data */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||||
m2 = _mm_loadu_ps(&x[2 * i + 8]);
|
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||||
m3 = _mm_loadu_ps(&x[2 * i + 12]);
|
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||||
|
|
||||||
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
@@ -128,14 +123,18 @@ static void sse_conv_real8(const float *restrict x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 12-tap SSE complex-real convolution */
|
/* 12-tap SSE complex-real convolution */
|
||||||
static void sse_conv_real12(const float *restrict x,
|
void sse_conv_real12(const float *x, int x_len,
|
||||||
const float *restrict h,
|
const float *h, int h_len,
|
||||||
float *restrict y,
|
float *y, int y_len,
|
||||||
int len)
|
int start, int len)
|
||||||
{
|
{
|
||||||
|
/* See NOTE in sse_conv_real4() */
|
||||||
|
|
||||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||||
__m128 m8, m9, m10, m11, m12, m13, m14;
|
__m128 m8, m9, m10, m11, m12, m13, m14;
|
||||||
|
|
||||||
|
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||||
|
|
||||||
/* Load (aligned) filter taps */
|
/* Load (aligned) filter taps */
|
||||||
m0 = _mm_load_ps(&h[0]);
|
m0 = _mm_load_ps(&h[0]);
|
||||||
m1 = _mm_load_ps(&h[4]);
|
m1 = _mm_load_ps(&h[4]);
|
||||||
@@ -150,18 +149,18 @@ static void sse_conv_real12(const float *restrict x,
|
|||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
/* Load (unaligned) input data */
|
/* Load (unaligned) input data */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||||
m2 = _mm_loadu_ps(&x[2 * i + 8]);
|
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||||
m3 = _mm_loadu_ps(&x[2 * i + 12]);
|
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||||
|
|
||||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 16]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 20]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||||
|
|
||||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
@@ -175,8 +174,8 @@ static void sse_conv_real12(const float *restrict x,
|
|||||||
m5 = _mm_mul_ps(m9, m14);
|
m5 = _mm_mul_ps(m9, m14);
|
||||||
|
|
||||||
/* Sum and store */
|
/* Sum and store */
|
||||||
m8 = _mm_add_ps(m0, m2);
|
m8 = _mm_add_ps(m0, m2);
|
||||||
m9 = _mm_add_ps(m1, m3);
|
m9 = _mm_add_ps(m1, m3);
|
||||||
m10 = _mm_add_ps(m8, m4);
|
m10 = _mm_add_ps(m8, m4);
|
||||||
m11 = _mm_add_ps(m9, m5);
|
m11 = _mm_add_ps(m9, m5);
|
||||||
|
|
||||||
@@ -190,14 +189,18 @@ static void sse_conv_real12(const float *restrict x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 16-tap SSE complex-real convolution */
|
/* 16-tap SSE complex-real convolution */
|
||||||
static void sse_conv_real16(const float *restrict x,
|
void sse_conv_real16(const float *x, int x_len,
|
||||||
const float *restrict h,
|
const float *h, int h_len,
|
||||||
float *restrict y,
|
float *y, int y_len,
|
||||||
int len)
|
int start, int len)
|
||||||
{
|
{
|
||||||
|
/* See NOTE in sse_conv_real4() */
|
||||||
|
|
||||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||||
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
||||||
|
|
||||||
|
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||||
|
|
||||||
/* Load (aligned) filter taps */
|
/* Load (aligned) filter taps */
|
||||||
m0 = _mm_load_ps(&h[0]);
|
m0 = _mm_load_ps(&h[0]);
|
||||||
m1 = _mm_load_ps(&h[4]);
|
m1 = _mm_load_ps(&h[4]);
|
||||||
@@ -216,23 +219,23 @@ static void sse_conv_real16(const float *restrict x,
|
|||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
/* Load (unaligned) input data */
|
/* Load (unaligned) input data */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||||
m2 = _mm_loadu_ps(&x[2 * i + 8]);
|
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||||
m3 = _mm_loadu_ps(&x[2 * i + 12]);
|
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||||
|
|
||||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 16]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 20]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||||
m2 = _mm_loadu_ps(&x[2 * i + 24]);
|
m2 = _mm_loadu_ps(&_x[2 * i + 24]);
|
||||||
m3 = _mm_loadu_ps(&x[2 * i + 28]);
|
m3 = _mm_loadu_ps(&_x[2 * i + 28]);
|
||||||
|
|
||||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
@@ -248,8 +251,8 @@ static void sse_conv_real16(const float *restrict x,
|
|||||||
m7 = _mm_mul_ps(m11, m15);
|
m7 = _mm_mul_ps(m11, m15);
|
||||||
|
|
||||||
/* Sum and store */
|
/* Sum and store */
|
||||||
m8 = _mm_add_ps(m0, m2);
|
m8 = _mm_add_ps(m0, m2);
|
||||||
m9 = _mm_add_ps(m1, m3);
|
m9 = _mm_add_ps(m1, m3);
|
||||||
m10 = _mm_add_ps(m4, m6);
|
m10 = _mm_add_ps(m4, m6);
|
||||||
m11 = _mm_add_ps(m5, m7);
|
m11 = _mm_add_ps(m5, m7);
|
||||||
|
|
||||||
@@ -265,14 +268,18 @@ static void sse_conv_real16(const float *restrict x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 20-tap SSE complex-real convolution */
|
/* 20-tap SSE complex-real convolution */
|
||||||
static void sse_conv_real20(const float *restrict x,
|
void sse_conv_real20(const float *x, int x_len,
|
||||||
const float *restrict h,
|
const float *h, int h_len,
|
||||||
float *restrict y,
|
float *y, int y_len,
|
||||||
int len)
|
int start, int len)
|
||||||
{
|
{
|
||||||
|
/* See NOTE in sse_conv_real4() */
|
||||||
|
|
||||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||||
__m128 m8, m9, m11, m12, m13, m14, m15;
|
__m128 m8, m9, m11, m12, m13, m14, m15;
|
||||||
|
|
||||||
|
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||||
|
|
||||||
/* Load (aligned) filter taps */
|
/* Load (aligned) filter taps */
|
||||||
m0 = _mm_load_ps(&h[0]);
|
m0 = _mm_load_ps(&h[0]);
|
||||||
m1 = _mm_load_ps(&h[4]);
|
m1 = _mm_load_ps(&h[4]);
|
||||||
@@ -293,19 +300,19 @@ static void sse_conv_real20(const float *restrict x,
|
|||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
/* Multiply-accumulate first 12 taps */
|
/* Multiply-accumulate first 12 taps */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 0]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 0]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 4]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 4]);
|
||||||
m2 = _mm_loadu_ps(&x[2 * i + 8]);
|
m2 = _mm_loadu_ps(&_x[2 * i + 8]);
|
||||||
m3 = _mm_loadu_ps(&x[2 * i + 12]);
|
m3 = _mm_loadu_ps(&_x[2 * i + 12]);
|
||||||
m4 = _mm_loadu_ps(&x[2 * i + 16]);
|
m4 = _mm_loadu_ps(&_x[2 * i + 16]);
|
||||||
m5 = _mm_loadu_ps(&x[2 * i + 20]);
|
m5 = _mm_loadu_ps(&_x[2 * i + 20]);
|
||||||
|
|
||||||
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3));
|
m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
m2 = _mm_mul_ps(m6, m11);
|
m2 = _mm_mul_ps(m6, m11);
|
||||||
m3 = _mm_mul_ps(m7, m11);
|
m3 = _mm_mul_ps(m7, m11);
|
||||||
@@ -314,16 +321,16 @@ static void sse_conv_real20(const float *restrict x,
|
|||||||
m6 = _mm_mul_ps(m0, m13);
|
m6 = _mm_mul_ps(m0, m13);
|
||||||
m7 = _mm_mul_ps(m1, m13);
|
m7 = _mm_mul_ps(m1, m13);
|
||||||
|
|
||||||
m0 = _mm_add_ps(m2, m4);
|
m0 = _mm_add_ps(m2, m4);
|
||||||
m1 = _mm_add_ps(m3, m5);
|
m1 = _mm_add_ps(m3, m5);
|
||||||
m8 = _mm_add_ps(m0, m6);
|
m8 = _mm_add_ps(m0, m6);
|
||||||
m9 = _mm_add_ps(m1, m7);
|
m9 = _mm_add_ps(m1, m7);
|
||||||
|
|
||||||
/* Multiply-accumulate last 8 taps */
|
/* Multiply-accumulate last 8 taps */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 24]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 24]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 28]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 28]);
|
||||||
m2 = _mm_loadu_ps(&x[2 * i + 32]);
|
m2 = _mm_loadu_ps(&_x[2 * i + 32]);
|
||||||
m3 = _mm_loadu_ps(&x[2 * i + 36]);
|
m3 = _mm_loadu_ps(&_x[2 * i + 36]);
|
||||||
|
|
||||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
@@ -335,8 +342,8 @@ static void sse_conv_real20(const float *restrict x,
|
|||||||
m2 = _mm_mul_ps(m6, m15);
|
m2 = _mm_mul_ps(m6, m15);
|
||||||
m3 = _mm_mul_ps(m7, m15);
|
m3 = _mm_mul_ps(m7, m15);
|
||||||
|
|
||||||
m4 = _mm_add_ps(m0, m2);
|
m4 = _mm_add_ps(m0, m2);
|
||||||
m5 = _mm_add_ps(m1, m3);
|
m5 = _mm_add_ps(m1, m3);
|
||||||
|
|
||||||
/* Final sum and store */
|
/* Final sum and store */
|
||||||
m0 = _mm_add_ps(m8, m4);
|
m0 = _mm_add_ps(m8, m4);
|
||||||
@@ -351,13 +358,17 @@ static void sse_conv_real20(const float *restrict x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 4*N-tap SSE complex-real convolution */
|
/* 4*N-tap SSE complex-real convolution */
|
||||||
static void sse_conv_real4n(const float *x,
|
void sse_conv_real4n(const float *x, int x_len,
|
||||||
const float *h,
|
const float *h, int h_len,
|
||||||
float *y,
|
float *y, int y_len,
|
||||||
int h_len, int len)
|
int start, int len)
|
||||||
{
|
{
|
||||||
|
/* See NOTE in sse_conv_real4() */
|
||||||
|
|
||||||
__m128 m0, m1, m2, m4, m5, m6, m7;
|
__m128 m0, m1, m2, m4, m5, m6, m7;
|
||||||
|
|
||||||
|
const float *_x = &x[2 * (-(h_len - 1) + start)];
|
||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
/* Zero */
|
/* Zero */
|
||||||
m6 = _mm_setzero_ps();
|
m6 = _mm_setzero_ps();
|
||||||
@@ -370,8 +381,8 @@ static void sse_conv_real4n(const float *x,
|
|||||||
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
|
|
||||||
/* Load (unaligned) input data */
|
/* Load (unaligned) input data */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]);
|
||||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
@@ -394,13 +405,20 @@ static void sse_conv_real4n(const float *x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 4*N-tap SSE complex-complex convolution */
|
/* 4*N-tap SSE complex-complex convolution */
|
||||||
static void sse_conv_cmplx_4n(const float *x,
|
void sse_conv_cmplx_4n(const float *x, int x_len,
|
||||||
const float *h,
|
const float *h, int h_len,
|
||||||
float *y,
|
float *y, int y_len,
|
||||||
int h_len, int len)
|
int start, int len)
|
||||||
{
|
{
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
__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++) {
|
for (int i = 0; i < len; i++) {
|
||||||
/* Zero */
|
/* Zero */
|
||||||
m6 = _mm_setzero_ps();
|
m6 = _mm_setzero_ps();
|
||||||
@@ -414,8 +432,8 @@ static void sse_conv_cmplx_4n(const float *x,
|
|||||||
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
/* Load (unaligned) input data */
|
/* Load (unaligned) input data */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]);
|
||||||
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
@@ -445,14 +463,18 @@ static void sse_conv_cmplx_4n(const float *x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 8*N-tap SSE complex-complex convolution */
|
/* 8*N-tap SSE complex-complex convolution */
|
||||||
static void sse_conv_cmplx_8n(const float *x,
|
void sse_conv_cmplx_8n(const float *x, int x_len,
|
||||||
const float *h,
|
const float *h, int h_len,
|
||||||
float *y,
|
float *y, int y_len,
|
||||||
int h_len, int len)
|
int start, int len)
|
||||||
{
|
{
|
||||||
|
/* See NOTE in sse_conv_cmplx_4n() */
|
||||||
|
|
||||||
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
|
||||||
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
|
__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++) {
|
for (int i = 0; i < len; i++) {
|
||||||
/* Zero */
|
/* Zero */
|
||||||
m12 = _mm_setzero_ps();
|
m12 = _mm_setzero_ps();
|
||||||
@@ -473,13 +495,13 @@ static void sse_conv_cmplx_8n(const float *x,
|
|||||||
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
/* Load (unaligned) input data */
|
/* Load (unaligned) input data */
|
||||||
m0 = _mm_loadu_ps(&x[2 * i + 16 * n + 0]);
|
m0 = _mm_loadu_ps(&_x[2 * i + 16 * n + 0]);
|
||||||
m1 = _mm_loadu_ps(&x[2 * i + 16 * n + 4]);
|
m1 = _mm_loadu_ps(&_x[2 * i + 16 * n + 4]);
|
||||||
m2 = _mm_loadu_ps(&x[2 * i + 16 * n + 8]);
|
m2 = _mm_loadu_ps(&_x[2 * i + 16 * n + 8]);
|
||||||
m3 = _mm_loadu_ps(&x[2 * i + 16 * n + 12]);
|
m3 = _mm_loadu_ps(&_x[2 * i + 16 * n + 12]);
|
||||||
|
|
||||||
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
|
||||||
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
|
||||||
|
|
||||||
@@ -518,96 +540,3 @@ static void sse_conv_cmplx_8n(const float *x,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
|
||||||
{
|
|
||||||
void (*conv_func)(const float *, const float *,
|
|
||||||
float *, int) = NULL;
|
|
||||||
void (*conv_func_n)(const float *, const float *,
|
|
||||||
float *, int, int) = NULL;
|
|
||||||
|
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
|
||||||
|
|
||||||
#ifdef HAVE_SSE3
|
|
||||||
if (step <= 4) {
|
|
||||||
switch (h_len) {
|
|
||||||
case 4:
|
|
||||||
conv_func = sse_conv_real4;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
conv_func = sse_conv_real8;
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
conv_func = sse_conv_real12;
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
conv_func = sse_conv_real16;
|
|
||||||
break;
|
|
||||||
case 20:
|
|
||||||
conv_func = sse_conv_real20;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!(h_len % 4))
|
|
||||||
conv_func_n = sse_conv_real4n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (conv_func) {
|
|
||||||
conv_func(&x[2 * (-(h_len - 1) + start)],
|
|
||||||
h, y, len);
|
|
||||||
} else if (conv_func_n) {
|
|
||||||
conv_func_n(&x[2 * (-(h_len - 1) + start)],
|
|
||||||
h, y, h_len, len);
|
|
||||||
} else {
|
|
||||||
_base_convolve_real(x, x_len,
|
|
||||||
h, h_len,
|
|
||||||
y, y_len,
|
|
||||||
start, len, step, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
void (*conv_func)(const float *, const float *,
|
|
||||||
float *, int, int) = NULL;
|
|
||||||
|
|
||||||
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memset(y, 0, len * 2 * sizeof(float));
|
|
||||||
|
|
||||||
#ifdef HAVE_SSE3
|
|
||||||
if (step <= 4) {
|
|
||||||
if (!(h_len % 8))
|
|
||||||
conv_func = sse_conv_cmplx_8n;
|
|
||||||
else if (!(h_len % 4))
|
|
||||||
conv_func = sse_conv_cmplx_4n;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (conv_func) {
|
|
||||||
conv_func(&x[2 * (-(h_len - 1) + start)],
|
|
||||||
h, y, h_len, len);
|
|
||||||
} else {
|
|
||||||
_base_convolve_complex(x, x_len,
|
|
||||||
h, h_len,
|
|
||||||
y, y_len,
|
|
||||||
start, len, step, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
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);
|
||||||
|
|
||||||
|
/* 8-tap SSE complex-real convolution */
|
||||||
|
void sse_conv_real8(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
/* 12-tap SSE complex-real convolution */
|
||||||
|
void sse_conv_real12(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
/* 16-tap SSE complex-real convolution */
|
||||||
|
void sse_conv_real16(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
/* 20-tap SSE complex-real convolution */
|
||||||
|
void sse_conv_real20(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
/* 4*N-tap SSE complex-real convolution */
|
||||||
|
void sse_conv_real4n(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
/* 4*N-tap SSE complex-complex convolution */
|
||||||
|
void sse_conv_cmplx_4n(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
|
/* 8*N-tap SSE complex-complex convolution */
|
||||||
|
void sse_conv_cmplx_8n(const float *x, int x_len,
|
||||||
|
const float *h, int h_len,
|
||||||
|
float *y, int y_len,
|
||||||
|
int start, int len);
|
||||||
15
Transceiver52M/device/Makefile.am
Normal file
15
Transceiver52M/device/Makefile.am
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
|
SUBDIRS = common
|
||||||
|
|
||||||
|
if DEVICE_USRP1
|
||||||
|
SUBDIRS += usrp1
|
||||||
|
endif
|
||||||
|
|
||||||
|
if DEVICE_UHD
|
||||||
|
SUBDIRS += uhd
|
||||||
|
endif
|
||||||
|
|
||||||
|
if DEVICE_LMS
|
||||||
|
SUBDIRS += lms
|
||||||
|
endif
|
||||||
12
Transceiver52M/device/common/Makefile.am
Normal file
12
Transceiver52M/device/common/Makefile.am
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
|
||||||
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
noinst_HEADERS = radioDevice.h smpl_buf.h
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = libdevice_common.la
|
||||||
|
|
||||||
|
libdevice_common_la_SOURCES = \
|
||||||
|
smpl_buf.cpp
|
||||||
@@ -18,11 +18,19 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "GSMCommon.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "config_defs.h"
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define GSMRATE 1625e3/6
|
#define GSMRATE (1625e3/6)
|
||||||
|
#define MCBTS_SPACING 800000.0
|
||||||
|
|
||||||
/** a 64-bit virtual timestamp for radio data */
|
/** a 64-bit virtual timestamp for radio data */
|
||||||
typedef unsigned long long TIMESTAMP;
|
typedef unsigned long long TIMESTAMP;
|
||||||
@@ -32,16 +40,23 @@ class RadioDevice {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/* Available transport bus types */
|
/* Available transport bus types */
|
||||||
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
|
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED, TX_WINDOW_LMS1 };
|
||||||
|
|
||||||
/* Radio interface types */
|
/* Radio interface types */
|
||||||
enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, DIVERSITY };
|
enum InterfaceType {
|
||||||
|
NORMAL,
|
||||||
|
RESAMP_64M,
|
||||||
|
RESAMP_100M,
|
||||||
|
MULTI_ARFCN,
|
||||||
|
};
|
||||||
|
|
||||||
static RadioDevice *make(size_t tx_sps, size_t rx_sps = 1, size_t chans = 1,
|
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||||
bool diversity = false, double offset = 0.0);
|
size_t chans = 1, double offset = 0.0,
|
||||||
|
const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
|
||||||
|
const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
|
||||||
|
|
||||||
/** Initialize the USRP */
|
/** Initialize the USRP */
|
||||||
virtual int open(const std::string &args = "", bool extref = false, bool swap_channels = false)=0;
|
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
|
||||||
|
|
||||||
virtual ~RadioDevice() { }
|
virtual ~RadioDevice() { }
|
||||||
|
|
||||||
@@ -124,12 +139,69 @@ class RadioDevice {
|
|||||||
/** return minimum Tx Gain **/
|
/** return minimum Tx Gain **/
|
||||||
virtual double minTxGain(void) = 0;
|
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 */
|
/** Return internal status values */
|
||||||
virtual double getTxFreq(size_t chan = 0) = 0;
|
virtual double getTxFreq(size_t chan = 0) = 0;
|
||||||
virtual double getRxFreq(size_t chan = 0) = 0;
|
virtual double getRxFreq(size_t chan = 0) = 0;
|
||||||
virtual double getSampleRate()=0;
|
virtual double getSampleRate()=0;
|
||||||
virtual double numberRead()=0;
|
|
||||||
virtual double numberWritten()=0;
|
protected:
|
||||||
|
size_t tx_sps, rx_sps;
|
||||||
|
InterfaceType iface;
|
||||||
|
size_t chans;
|
||||||
|
double lo_offset;
|
||||||
|
std::vector<std::string> tx_paths, rx_paths;
|
||||||
|
|
||||||
|
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths):
|
||||||
|
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chans), lo_offset(offset),
|
||||||
|
tx_paths(tx_paths), rx_paths(rx_paths)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool set_antennas() {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < tx_paths.size(); i++) {
|
||||||
|
if (tx_paths[i] == "")
|
||||||
|
continue;
|
||||||
|
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
|
||||||
|
if (!setTxAntenna(tx_paths[i], i)) {
|
||||||
|
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < rx_paths.size(); i++) {
|
||||||
|
if (rx_paths[i] == "")
|
||||||
|
continue;
|
||||||
|
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
|
||||||
|
if (!setRxAntenna(rx_paths[i], i)) {
|
||||||
|
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Antennas configured successfully";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
169
Transceiver52M/device/common/smpl_buf.cpp
Normal file
169
Transceiver52M/device/common/smpl_buf.cpp
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Sample Buffer - Allows reading and writing of timed samples
|
||||||
|
*
|
||||||
|
* Copyright 2010,2011 Free Software Foundation, Inc.
|
||||||
|
* Copyright (C) 2015 Ettus Research LLC
|
||||||
|
* Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* Author: Tom Tsou <tom.tsou@ettus.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "smpl_buf.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
smpl_buf::smpl_buf(size_t len)
|
||||||
|
: buf_len(len), 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::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)) % buf_len;
|
||||||
|
|
||||||
|
// 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::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;
|
||||||
|
|
||||||
|
if (timestamp < time_end) {
|
||||||
|
LOGC(DDEV, ERR) << "Overwriting old buffer data: timestamp="
|
||||||
|
<< timestamp << " time_end=" << time_end;
|
||||||
|
// Do not return error here, because it's a rounding error and is not fatal
|
||||||
|
}
|
||||||
|
if (timestamp > time_end && time_end != 0) {
|
||||||
|
LOGC(DDEV, ERR) << "Skipping buffer data: timestamp="
|
||||||
|
<< timestamp << " time_end=" << time_end;
|
||||||
|
// Do not return error here, because it's a rounding error and is not fatal
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (!data_start)
|
||||||
|
data_start = write_start;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string smpl_buf::str_status(TIMESTAMP timestamp) const
|
||||||
|
{
|
||||||
|
std::ostringstream ost("Sample buffer: ");
|
||||||
|
|
||||||
|
ost << "timestamp = " << timestamp;
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
87
Transceiver52M/device/common/smpl_buf.h
Normal file
87
Transceiver52M/device/common/smpl_buf.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Sample Buffer - Allows reading and writing of timed samples
|
||||||
|
*
|
||||||
|
* Copyright 2010,2011 Free Software Foundation, Inc.
|
||||||
|
* Copyright (C) 2015 Ettus Research LLC
|
||||||
|
* Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* Author: Tom Tsou <tom.tsou@ettus.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "radioDevice.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sample Buffer - Allows reading and writing of timed samples using osmo-trx
|
||||||
|
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 timestamp
|
||||||
|
*/
|
||||||
|
smpl_buf(size_t len);
|
||||||
|
~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;
|
||||||
|
|
||||||
|
/** 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 write(void *buf, size_t len, TIMESTAMP timestamp);
|
||||||
|
|
||||||
|
/** Buffer status string
|
||||||
|
@return a formatted string describing internal buffer state
|
||||||
|
*/
|
||||||
|
std::string str_status(TIMESTAMP timestamp) 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;
|
||||||
|
|
||||||
|
TIMESTAMP time_start;
|
||||||
|
TIMESTAMP time_end;
|
||||||
|
|
||||||
|
size_t data_start;
|
||||||
|
size_t data_end;
|
||||||
|
};
|
||||||
773
Transceiver52M/device/lms/LMSDevice.cpp
Normal file
773
Transceiver52M/device/lms/LMSDevice.cpp
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 sysmocom - s.f.m.c. GmbH
|
||||||
|
*
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "Logger.h"
|
||||||
|
#include "Threads.h"
|
||||||
|
#include "LMSDevice.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
#include <lime/LimeSuite.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
constexpr double LMSDevice::masterClockRate;
|
||||||
|
|
||||||
|
#define MAX_ANTENNA_LIST_SIZE 10
|
||||||
|
#define LMS_SAMPLE_RATE GSMRATE*32
|
||||||
|
#define GSM_CARRIER_BW 270000.0 /* 270kHz */
|
||||||
|
#define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */
|
||||||
|
#define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED)
|
||||||
|
#define SAMPLE_BUF_SZ (1 << 20) /* Size of Rx timestamp based Ring buffer, in bytes */
|
||||||
|
|
||||||
|
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths):
|
||||||
|
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
|
||||||
|
m_lms_dev(NULL)
|
||||||
|
{
|
||||||
|
LOGC(DDEV, INFO) << "creating LMS device...";
|
||||||
|
|
||||||
|
m_lms_stream_rx.resize(chans);
|
||||||
|
m_lms_stream_tx.resize(chans);
|
||||||
|
|
||||||
|
m_last_rx_underruns.resize(chans, 0);
|
||||||
|
m_last_rx_overruns.resize(chans, 0);
|
||||||
|
m_last_rx_dropped.resize(chans, 0);
|
||||||
|
m_last_tx_underruns.resize(chans, 0);
|
||||||
|
|
||||||
|
rx_buffers.resize(chans);
|
||||||
|
}
|
||||||
|
|
||||||
|
LMSDevice::~LMSDevice()
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
LOGC(DDEV, INFO) << "Closing LMS device";
|
||||||
|
if (m_lms_dev) {
|
||||||
|
/* disable all channels */
|
||||||
|
for (i=0; i<chans; i++) {
|
||||||
|
LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, false);
|
||||||
|
LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, false);
|
||||||
|
}
|
||||||
|
LMS_Close(m_lms_dev);
|
||||||
|
m_lms_dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
|
delete rx_buffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lms_log_callback(int lvl, const char *msg)
|
||||||
|
{
|
||||||
|
/* map lime specific log levels */
|
||||||
|
static const int lvl_map[5] = {
|
||||||
|
[0] = LOGL_FATAL,
|
||||||
|
[LMS_LOG_ERROR] = LOGL_ERROR,
|
||||||
|
[LMS_LOG_WARNING] = LOGL_NOTICE,
|
||||||
|
[LMS_LOG_INFO] = LOGL_INFO,
|
||||||
|
[LMS_LOG_DEBUG] = LOGL_DEBUG,
|
||||||
|
};
|
||||||
|
/* protect against future higher log level values (lower importance) */
|
||||||
|
if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map))
|
||||||
|
lvl = ARRAY_SIZE(lvl_map)-1;
|
||||||
|
|
||||||
|
LOGLV(DLMS, lvl_map[lvl]) << msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_range(const char* name, lms_range_t *range)
|
||||||
|
{
|
||||||
|
LOGC(DDEV, INFO) << name << ": Min=" << range->min << " Max=" << range->max
|
||||||
|
<< " Step=" << range->step;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Find the device string that matches all filters from \a args.
|
||||||
|
* \param[in] info_list device addresses found by LMS_GetDeviceList()
|
||||||
|
* \param[in] count length of info_list
|
||||||
|
* \param[in] args dev-args value from osmo-trx.cfg, containing comma separated key=value pairs
|
||||||
|
* \return index of first matching device or -1 (no match) */
|
||||||
|
int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
vector<string> filters;
|
||||||
|
|
||||||
|
filters = comma_delimited_to_vector(args.c_str());
|
||||||
|
|
||||||
|
/* iterate over device addresses */
|
||||||
|
for (i=0; i < count; i++) {
|
||||||
|
/* check if all filters match */
|
||||||
|
bool match = true;
|
||||||
|
for (j=0; j < filters.size(); j++) {
|
||||||
|
if (!strstr(info_list[i], filters[j].c_str())) {
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||||
|
{
|
||||||
|
lms_info_str_t* info_list;
|
||||||
|
const lms_dev_info_t* device_info;
|
||||||
|
lms_range_t range_sr;
|
||||||
|
float_type sr_host, sr_rf;
|
||||||
|
unsigned int i, n;
|
||||||
|
int rc, dev_id;
|
||||||
|
|
||||||
|
LOGC(DDEV, INFO) << "Opening LMS device..";
|
||||||
|
|
||||||
|
LMS_RegisterLogHandler(&lms_log_callback);
|
||||||
|
|
||||||
|
if ((n = LMS_GetDeviceList(NULL)) < 0)
|
||||||
|
LOGC(DDEV, ERROR) << "LMS_GetDeviceList(NULL) failed";
|
||||||
|
LOGC(DDEV, INFO) << "Devices found: " << n;
|
||||||
|
if (n < 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
info_list = new lms_info_str_t[n];
|
||||||
|
|
||||||
|
if (LMS_GetDeviceList(info_list) < 0)
|
||||||
|
LOGC(DDEV, ERROR) << "LMS_GetDeviceList(info_list) failed";
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
|
||||||
|
|
||||||
|
dev_id = info_list_find(info_list, n, args);
|
||||||
|
if (dev_id == -1) {
|
||||||
|
LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
|
||||||
|
delete[] info_list;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGC(DDEV, INFO) << "Using device[" << dev_id << "]";
|
||||||
|
rc = LMS_Open(&m_lms_dev, info_list[dev_id], NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
LOGC(DDEV, ERROR) << "LMS_GetDeviceList() failed)";
|
||||||
|
delete [] info_list;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete [] info_list;
|
||||||
|
|
||||||
|
device_info = LMS_GetDeviceInfo(m_lms_dev);
|
||||||
|
|
||||||
|
if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
|
||||||
|
LOGC(DDEV, ERROR) << "Invalid reference type";
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if reference clock is external setup must happen _before_ calling LMS_Init */
|
||||||
|
/* FIXME make external reference frequency configurable */
|
||||||
|
if (ref == REF_EXTERNAL) {
|
||||||
|
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
|
||||||
|
/* Assume an external 10 MHz reference clock */
|
||||||
|
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGC(DDEV, INFO) << "Init LMS device";
|
||||||
|
if (LMS_Init(m_lms_dev) != 0) {
|
||||||
|
LOGC(DDEV, ERROR) << "LMS_Init() failed";
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LimeSDR-Mini does not have switches but needs soldering to select external/internal clock */
|
||||||
|
/* LimeNET-Micro also does not like selecting internal clock*/
|
||||||
|
/* also set device specific maximum tx levels selected by phasenoise measurements*/
|
||||||
|
if (strncmp(device_info->deviceName,"LimeSDR-USB",11) == 0){
|
||||||
|
/* if reference clock is internal setup must happen _after_ calling LMS_Init */
|
||||||
|
/* according to lms using LMS_CLOCK_EXTREF with a frequency <= 0 is the correct way to set clock to internal reference*/
|
||||||
|
if (ref == REF_INTERNAL) {
|
||||||
|
LOGC(DDEV, INFO) << "Setting Internal clock reference";
|
||||||
|
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, -1) < 0)
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
maxTxGainClamp = 73.0;
|
||||||
|
} else if (strncmp(device_info->deviceName,"LimeSDR-Mini",12) == 0)
|
||||||
|
maxTxGainClamp = 66.0;
|
||||||
|
else
|
||||||
|
maxTxGainClamp = 71.0; /* "LimeNET-Micro", etc FIXME pciE based LMS boards?*/
|
||||||
|
|
||||||
|
/* enable all used channels */
|
||||||
|
for (i=0; i<chans; i++) {
|
||||||
|
if (LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, true) < 0)
|
||||||
|
goto out_close;
|
||||||
|
if (LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, true) < 0)
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set samplerate */
|
||||||
|
if (LMS_GetSampleRateRange(m_lms_dev, LMS_CH_RX, &range_sr))
|
||||||
|
goto out_close;
|
||||||
|
print_range("Sample Rate", &range_sr);
|
||||||
|
|
||||||
|
LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps;
|
||||||
|
if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0)
|
||||||
|
goto out_close;
|
||||||
|
|
||||||
|
if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf))
|
||||||
|
goto out_close;
|
||||||
|
LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf;
|
||||||
|
|
||||||
|
/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */
|
||||||
|
ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */
|
||||||
|
|
||||||
|
/* configure antennas */
|
||||||
|
if (!set_antennas()) {
|
||||||
|
LOGC(DDEV, FATAL) << "LMS antenna setting failed";
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up per-channel Rx timestamp based Ring buffers */
|
||||||
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
|
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
|
||||||
|
|
||||||
|
started = false;
|
||||||
|
|
||||||
|
return NORMAL;
|
||||||
|
|
||||||
|
out_close:
|
||||||
|
LOGC(DDEV, FATAL) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
|
||||||
|
LMS_Close(m_lms_dev);
|
||||||
|
m_lms_dev = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::start()
|
||||||
|
{
|
||||||
|
LOGC(DDEV, INFO) << "starting LMS...";
|
||||||
|
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (started) {
|
||||||
|
LOGC(DDEV, ERR) << "Device already started";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* configure the channels/streams */
|
||||||
|
for (i=0; i<chans; i++) {
|
||||||
|
/* Set gains for calibration/filter setup */
|
||||||
|
/* TX gain to maximum */
|
||||||
|
setTxGain(maxTxGain(), i);
|
||||||
|
/* RX gain to midpoint */
|
||||||
|
setRxGain((minRxGain() + maxRxGain()) / 2, i);
|
||||||
|
|
||||||
|
/* set up Rx and Tx filters */
|
||||||
|
if (!do_filters(i))
|
||||||
|
return false;
|
||||||
|
/* Perform Rx and Tx calibration */
|
||||||
|
if (!do_calib(i))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* configure Streams */
|
||||||
|
m_lms_stream_rx[i] = {};
|
||||||
|
m_lms_stream_rx[i].isTx = false;
|
||||||
|
m_lms_stream_rx[i].channel = i;
|
||||||
|
m_lms_stream_rx[i].fifoSize = 1024 * 1024;
|
||||||
|
m_lms_stream_rx[i].throughputVsLatency = 0.3;
|
||||||
|
m_lms_stream_rx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
|
||||||
|
|
||||||
|
m_lms_stream_tx[i] = {};
|
||||||
|
m_lms_stream_tx[i].isTx = true;
|
||||||
|
m_lms_stream_tx[i].channel = i;
|
||||||
|
m_lms_stream_tx[i].fifoSize = 1024 * 1024;
|
||||||
|
m_lms_stream_tx[i].throughputVsLatency = 0.3;
|
||||||
|
m_lms_stream_tx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
|
||||||
|
|
||||||
|
if (LMS_SetupStream(m_lms_dev, &m_lms_stream_rx[i]) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (LMS_SetupStream(m_lms_dev, &m_lms_stream_tx[i]) < 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now start the streams in a second loop, as we can no longer call
|
||||||
|
* LMS_SetupStream() after LMS_StartStream() of the first stream */
|
||||||
|
for (i = 0; i < chans; i++) {
|
||||||
|
if (LMS_StartStream(&m_lms_stream_rx[i]) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (LMS_StartStream(&m_lms_stream_tx[i]) < 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
flush_recv(10);
|
||||||
|
|
||||||
|
started = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::stop()
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (!started)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (i=0; i<chans; i++) {
|
||||||
|
LMS_StopStream(&m_lms_stream_tx[i]);
|
||||||
|
LMS_StopStream(&m_lms_stream_rx[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<chans; i++) {
|
||||||
|
LMS_DestroyStream(m_lms_dev, &m_lms_stream_tx[i]);
|
||||||
|
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
started = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do rx/tx calibration - depends on gain, freq and bw */
|
||||||
|
bool LMSDevice::do_calib(size_t chan)
|
||||||
|
{
|
||||||
|
LOGCHAN(chan, DDEV, INFO) << "Calibrating";
|
||||||
|
if (LMS_Calibrate(m_lms_dev, LMS_CH_RX, chan, LMS_CALIBRATE_BW_HZ, 0) < 0)
|
||||||
|
return false;
|
||||||
|
if (LMS_Calibrate(m_lms_dev, LMS_CH_TX, chan, LMS_CALIBRATE_BW_HZ, 0) < 0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do rx/tx filter config - depends on bw only? */
|
||||||
|
bool LMSDevice::do_filters(size_t chan)
|
||||||
|
{
|
||||||
|
lms_range_t range_lpfbw_rx, range_lpfbw_tx;
|
||||||
|
float_type lpfbw_rx, lpfbw_tx;
|
||||||
|
|
||||||
|
LOGCHAN(chan, DDEV, INFO) << "Setting filters";
|
||||||
|
if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_rx))
|
||||||
|
return false;
|
||||||
|
print_range("LPFBWRange Rx", &range_lpfbw_rx);
|
||||||
|
if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_tx))
|
||||||
|
return false;
|
||||||
|
print_range("LPFBWRange Tx", &range_lpfbw_tx);
|
||||||
|
|
||||||
|
lpfbw_rx = OSMO_MIN(OSMO_MAX(1.4001e6, range_lpfbw_rx.min), range_lpfbw_rx.max);
|
||||||
|
lpfbw_tx = OSMO_MIN(OSMO_MAX(5.2e6, range_lpfbw_tx.min), range_lpfbw_tx.max);
|
||||||
|
|
||||||
|
LOGCHAN(chan, DDEV, INFO) << "LPFBW: Rx=" << lpfbw_rx << " Tx=" << lpfbw_tx;
|
||||||
|
|
||||||
|
LOGCHAN(chan, DDEV, INFO) << "Setting LPFBW";
|
||||||
|
if (LMS_SetLPFBW(m_lms_dev, LMS_CH_RX, chan, lpfbw_rx) < 0)
|
||||||
|
return false;
|
||||||
|
if (LMS_SetLPFBW(m_lms_dev, LMS_CH_TX, chan, lpfbw_tx) < 0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double LMSDevice::maxTxGain()
|
||||||
|
{
|
||||||
|
return maxTxGainClamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LMSDevice::minTxGain()
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LMSDevice::maxRxGain()
|
||||||
|
{
|
||||||
|
return 73.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LMSDevice::minRxGain()
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LMSDevice::setTxGain(double dB, size_t chan)
|
||||||
|
{
|
||||||
|
if (dB > maxTxGain())
|
||||||
|
dB = maxTxGain();
|
||||||
|
if (dB < minTxGain())
|
||||||
|
dB = minTxGain();
|
||||||
|
|
||||||
|
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX gain to " << dB << " dB";
|
||||||
|
|
||||||
|
if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
|
||||||
|
LOGCHAN(chan, DDEV, ERR) << "Error setting TX gain to " << dB << " dB";
|
||||||
|
|
||||||
|
return dB;
|
||||||
|
}
|
||||||
|
|
||||||
|
double LMSDevice::setRxGain(double dB, size_t chan)
|
||||||
|
{
|
||||||
|
if (dB > maxRxGain())
|
||||||
|
dB = maxRxGain();
|
||||||
|
if (dB < minRxGain())
|
||||||
|
dB = minRxGain();
|
||||||
|
|
||||||
|
LOGCHAN(chan, DDEV, NOTICE) << "Setting RX gain to " << dB << " dB";
|
||||||
|
|
||||||
|
if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0)
|
||||||
|
LOGCHAN(chan, DDEV, ERR) << "Error setting RX gain to " << dB << " dB";
|
||||||
|
|
||||||
|
return dB;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan)
|
||||||
|
{
|
||||||
|
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
|
||||||
|
const char* c_name = name.c_str();
|
||||||
|
int num_names;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
num_names = LMS_GetAntennaList(m_lms_dev, dir_tx, chan, name_list);
|
||||||
|
for (i = 0; i < num_names; i++) {
|
||||||
|
if (!strcmp(c_name, name_list[i]))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::flush_recv(size_t num_pkts)
|
||||||
|
{
|
||||||
|
#define CHUNK 625
|
||||||
|
int len = CHUNK * tx_sps;
|
||||||
|
short *buffer = (short*) alloca(sizeof(short) * len * 2);
|
||||||
|
int rc;
|
||||||
|
lms_stream_meta_t rx_metadata = {};
|
||||||
|
rx_metadata.flushPartialPacket = false;
|
||||||
|
rx_metadata.waitForTimestamp = false;
|
||||||
|
|
||||||
|
ts_initial = 0;
|
||||||
|
|
||||||
|
while (!ts_initial || (num_pkts-- > 0)) {
|
||||||
|
rc = LMS_RecvStream(&m_lms_stream_rx[0], &buffer[0], len, &rx_metadata, 100);
|
||||||
|
LOGC(DDEV, DEBUG) << "Flush: Recv buffer of len " << rc << " at " << std::hex << rx_metadata.timestamp;
|
||||||
|
if (rc != len) {
|
||||||
|
LOGC(DDEV, ERROR) << "Flush: Device receive timed out";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts_initial = rx_metadata.timestamp + len;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
if (chan >= rx_paths.size()) {
|
||||||
|
LOGC(DDEV, ERROR) << "Requested non-existent channel " << chan;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = get_ant_idx(ant, LMS_CH_RX, chan);
|
||||||
|
if (idx < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LMS_SetAntenna(m_lms_dev, LMS_CH_RX, chan, idx) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Unable to set Rx Antenna";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LMSDevice::getRxAntenna(size_t chan)
|
||||||
|
{
|
||||||
|
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
if (chan >= rx_paths.size()) {
|
||||||
|
LOGC(DDEV, ERROR) << "Requested non-existent channel " << chan;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = LMS_GetAntenna(m_lms_dev, LMS_CH_RX, chan);
|
||||||
|
if (idx < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Error getting Rx Antenna";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, name_list) < idx) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Error getting Rx Antenna List";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return name_list[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
if (chan >= tx_paths.size()) {
|
||||||
|
LOGC(DDEV, ERROR) << "Requested non-existent channel " << chan;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = get_ant_idx(ant, LMS_CH_TX, chan);
|
||||||
|
if (idx < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LMS_SetAntenna(m_lms_dev, LMS_CH_TX, chan, idx) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Unable to set Rx Antenna";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LMSDevice::getTxAntenna(size_t chan)
|
||||||
|
{
|
||||||
|
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
if (chan >= tx_paths.size()) {
|
||||||
|
LOGC(DDEV, ERROR) << "Requested non-existent channel " << chan;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = LMS_GetAntenna(m_lms_dev, LMS_CH_TX, chan);
|
||||||
|
if (idx < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Error getting Tx Antenna";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, name_list) < idx) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Error getting Tx Antenna List";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return name_list[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::requiresRadioAlign()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GSM::Time LMSDevice::minLatency() {
|
||||||
|
/* UNUSED on limesdr (only used on usrp1/2) */
|
||||||
|
return GSM::Time(0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LMSDevice::update_stream_stats(size_t chan, bool * underrun, bool * overrun)
|
||||||
|
{
|
||||||
|
lms_stream_status_t status;
|
||||||
|
if (LMS_GetStreamStatus(&m_lms_stream_rx[chan], &status) == 0) {
|
||||||
|
if (status.underrun > m_last_rx_underruns[chan]) {
|
||||||
|
*underrun = true;
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "recv Underrun! ("
|
||||||
|
<< m_last_rx_underruns[chan] << " -> "
|
||||||
|
<< status.underrun << ")";
|
||||||
|
}
|
||||||
|
m_last_rx_underruns[chan] = status.underrun;
|
||||||
|
|
||||||
|
if (status.overrun > m_last_rx_overruns[chan]) {
|
||||||
|
*overrun = true;
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "recv Overrun! ("
|
||||||
|
<< m_last_rx_overruns[chan] << " -> "
|
||||||
|
<< status.overrun << ")";
|
||||||
|
}
|
||||||
|
m_last_rx_overruns[chan] = status.overrun;
|
||||||
|
|
||||||
|
if (status.droppedPackets > m_last_rx_dropped[chan]) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "recv Dropped packets by HW! ("
|
||||||
|
<< m_last_rx_dropped[chan] << " -> "
|
||||||
|
<< status.droppedPackets << ")";
|
||||||
|
}
|
||||||
|
m_last_rx_dropped[chan] = m_last_rx_overruns[chan];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Assumes sequential reads
|
||||||
|
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
|
||||||
|
TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
|
||||||
|
{
|
||||||
|
int rc, num_smpls, expect_smpls;
|
||||||
|
ssize_t avail_smpls;
|
||||||
|
TIMESTAMP expect_timestamp;
|
||||||
|
unsigned int i;
|
||||||
|
lms_stream_meta_t rx_metadata = {};
|
||||||
|
rx_metadata.flushPartialPacket = false;
|
||||||
|
rx_metadata.waitForTimestamp = false;
|
||||||
|
rx_metadata.timestamp = 0;
|
||||||
|
|
||||||
|
if (bufs.size() != chans) {
|
||||||
|
LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*overrun = false;
|
||||||
|
*underrun = false;
|
||||||
|
|
||||||
|
/* Check that timestamp is valid */
|
||||||
|
rc = rx_buffers[0]->avail_smpls(timestamp);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
|
||||||
|
LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i<chans; i++) {
|
||||||
|
/* Receive samples from HW until we have enough */
|
||||||
|
while ((avail_smpls = rx_buffers[i]->avail_smpls(timestamp)) < len) {
|
||||||
|
thread_enable_cancel(false);
|
||||||
|
num_smpls = LMS_RecvStream(&m_lms_stream_rx[i], bufs[i], len - avail_smpls, &rx_metadata, 100);
|
||||||
|
update_stream_stats(i, underrun, overrun);
|
||||||
|
thread_enable_cancel(true);
|
||||||
|
if (num_smpls <= 0) {
|
||||||
|
LOGCHAN(i, DDEV, ERROR) << "Device receive timed out (" << rc << " vs exp " << len << ").";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGCHAN(i, DDEV, DEBUG) "Received timestamp = " << (TIMESTAMP)rx_metadata.timestamp << " (" << num_smpls << ")";
|
||||||
|
|
||||||
|
expect_smpls = len - avail_smpls;
|
||||||
|
if (expect_smpls != num_smpls)
|
||||||
|
LOGCHAN(i, DDEV, NOTICE) << "Unexpected recv buffer len: expect "
|
||||||
|
<< expect_smpls << " got " << num_smpls
|
||||||
|
<< ", diff=" << expect_smpls - num_smpls;
|
||||||
|
|
||||||
|
expect_timestamp = timestamp + avail_smpls;
|
||||||
|
if (expect_timestamp != (TIMESTAMP)rx_metadata.timestamp)
|
||||||
|
LOGCHAN(i, DDEV, ERROR) << "Unexpected recv buffer timestamp: expect "
|
||||||
|
<< expect_timestamp << " got " << (TIMESTAMP)rx_metadata.timestamp
|
||||||
|
<< ", diff=" << rx_metadata.timestamp - expect_timestamp;
|
||||||
|
|
||||||
|
rc = rx_buffers[i]->write(bufs[i], num_smpls, (TIMESTAMP)rx_metadata.timestamp);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc);
|
||||||
|
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
|
||||||
|
if (rc != smpl_buf::ERROR_OVERFLOW)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have enough samples */
|
||||||
|
for (size_t i = 0; i < rx_buffers.size(); i++) {
|
||||||
|
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
|
||||||
|
if ((rc < 0) || (rc != len)) {
|
||||||
|
LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
|
||||||
|
LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
|
||||||
|
bool * underrun, unsigned long long timestamp,
|
||||||
|
bool isControl)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
unsigned int i;
|
||||||
|
lms_stream_status_t status;
|
||||||
|
lms_stream_meta_t tx_metadata = {};
|
||||||
|
tx_metadata.flushPartialPacket = false;
|
||||||
|
tx_metadata.waitForTimestamp = true;
|
||||||
|
tx_metadata.timestamp = timestamp - ts_offset; /* Shift Tx time by offset */
|
||||||
|
|
||||||
|
if (isControl) {
|
||||||
|
LOGC(DDEV, ERROR) << "Control packets not supported";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufs.size() != chans) {
|
||||||
|
LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*underrun = false;
|
||||||
|
|
||||||
|
for (i = 0; i<chans; i++) {
|
||||||
|
LOGCHAN(i, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << tx_metadata.timestamp;
|
||||||
|
thread_enable_cancel(false);
|
||||||
|
rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100);
|
||||||
|
if (rc != len) {
|
||||||
|
LOGCHAN(i, DDEV, ERROR) << "LMS: Device send timed out";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LMS_GetStreamStatus(&m_lms_stream_tx[i], &status) == 0) {
|
||||||
|
if (status.underrun > m_last_tx_underruns[i])
|
||||||
|
*underrun = true;
|
||||||
|
m_last_tx_underruns[i] = status.underrun;
|
||||||
|
}
|
||||||
|
thread_enable_cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
|
||||||
|
{
|
||||||
|
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
|
||||||
|
|
||||||
|
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Error setting Tx Freq to " << wFreq << " Hz";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
|
||||||
|
{
|
||||||
|
LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
|
||||||
|
|
||||||
|
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ERROR) << "Error setting Rx Freq to " << wFreq << " Hz";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||||
|
InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector < std::string > &tx_paths,
|
||||||
|
const std::vector < std::string > &rx_paths)
|
||||||
|
{
|
||||||
|
if (tx_sps != rx_sps) {
|
||||||
|
LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (lo_offset != 0.0) {
|
||||||
|
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||||
|
}
|
||||||
208
Transceiver52M/device/lms/LMSDevice.h
Normal file
208
Transceiver52M/device/lms/LMSDevice.h
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 sysmocom - s.f.m.c. GmbH
|
||||||
|
*
|
||||||
|
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LMS_DEVICE_H_
|
||||||
|
#define _LMS_DEVICE_H_
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "radioDevice.h"
|
||||||
|
#include "smpl_buf.h"
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <lime/LimeSuite.h>
|
||||||
|
|
||||||
|
/* Definition of LIMESDR_TX_AMPL limits maximum amplitude of I and Q
|
||||||
|
* channels separately. Hence LIMESDR_TX_AMPL value must be 1/sqrt(2) =
|
||||||
|
* 0.7071.... to get an amplitude of 1 of the complex signal:
|
||||||
|
* A^2 = I^2 + Q^2
|
||||||
|
* A^2 = (1/sqrt(2))^2 + (1/sqrt(2))^2
|
||||||
|
* A^2 = 1/2 + 1/2
|
||||||
|
* A^2 = 1 */
|
||||||
|
#define LIMESDR_TX_AMPL 0.707
|
||||||
|
|
||||||
|
/** A class to handle a LimeSuite supported device */
|
||||||
|
class LMSDevice:public RadioDevice {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static constexpr double masterClockRate = 52.0e6;
|
||||||
|
|
||||||
|
lms_device_t *m_lms_dev;
|
||||||
|
std::vector<lms_stream_t> m_lms_stream_rx;
|
||||||
|
std::vector<lms_stream_t> m_lms_stream_tx;
|
||||||
|
|
||||||
|
std::vector<uint32_t> m_last_rx_underruns;
|
||||||
|
std::vector<uint32_t> m_last_rx_overruns;
|
||||||
|
std::vector<uint32_t> m_last_rx_dropped;
|
||||||
|
std::vector<uint32_t> m_last_tx_underruns;
|
||||||
|
|
||||||
|
std::vector<smpl_buf *> rx_buffers;
|
||||||
|
|
||||||
|
double actualSampleRate; ///< the actual USRP sampling rate
|
||||||
|
|
||||||
|
bool started; ///< flag indicates LMS has started
|
||||||
|
bool skipRx; ///< set if LMS is transmit-only.
|
||||||
|
|
||||||
|
TIMESTAMP ts_initial, ts_offset;
|
||||||
|
|
||||||
|
double rxGain;
|
||||||
|
double maxTxGainClamp;
|
||||||
|
|
||||||
|
bool do_calib(size_t chan);
|
||||||
|
bool do_filters(size_t chan);
|
||||||
|
int get_ant_idx(const std::string & name, bool dir_tx, size_t chan);
|
||||||
|
bool flush_recv(size_t num_pkts);
|
||||||
|
void update_stream_stats(size_t chan, bool * underrun, bool * overrun);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** Object constructor */
|
||||||
|
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths);
|
||||||
|
~LMSDevice();
|
||||||
|
|
||||||
|
/** Instantiate the LMS */
|
||||||
|
int open(const std::string &args, int ref, bool swap_channels);
|
||||||
|
|
||||||
|
/** Start the LMS */
|
||||||
|
bool start();
|
||||||
|
|
||||||
|
/** Stop the LMS */
|
||||||
|
bool stop();
|
||||||
|
|
||||||
|
/** Set priority not supported */
|
||||||
|
void setPriority(float prio = 0.5) {
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TxWindowType getWindowType() {
|
||||||
|
return TX_WINDOW_LMS1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Read samples from the LMS.
|
||||||
|
@param buf preallocated buf to contain read result
|
||||||
|
@param len number of samples desired
|
||||||
|
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
|
||||||
|
@param timestamp The timestamp of the first samples to be read
|
||||||
|
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
|
||||||
|
@param RSSI The received signal strength of the read result
|
||||||
|
@return The number of samples actually read
|
||||||
|
*/
|
||||||
|
int readSamples(std::vector < short *>&buf, int len, bool * overrun,
|
||||||
|
TIMESTAMP timestamp = 0xffffffff, bool * underrun =
|
||||||
|
NULL, unsigned *RSSI = NULL);
|
||||||
|
/**
|
||||||
|
Write samples to the LMS.
|
||||||
|
@param buf Contains the data to be written.
|
||||||
|
@param len number of samples to write.
|
||||||
|
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
|
||||||
|
@param timestamp The timestamp of the first sample of the data buffer.
|
||||||
|
@param isControl Set if data is a control packet, e.g. a ping command
|
||||||
|
@return The number of samples actually written
|
||||||
|
*/
|
||||||
|
int writeSamples(std::vector < short *>&bufs, int len, bool * underrun,
|
||||||
|
TIMESTAMP timestamp = 0xffffffff, bool isControl =
|
||||||
|
false);
|
||||||
|
|
||||||
|
/** Update the alignment between the read and write timestamps */
|
||||||
|
bool updateAlignment(TIMESTAMP timestamp);
|
||||||
|
|
||||||
|
/** Set the transmitter frequency */
|
||||||
|
bool setTxFreq(double wFreq, size_t chan = 0);
|
||||||
|
|
||||||
|
/** Set the receiver frequency */
|
||||||
|
bool setRxFreq(double wFreq, size_t chan = 0);
|
||||||
|
|
||||||
|
/** Returns the starting write Timestamp*/
|
||||||
|
TIMESTAMP initialWriteTimestamp(void) {
|
||||||
|
return ts_initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the starting read Timestamp*/
|
||||||
|
TIMESTAMP initialReadTimestamp(void) {
|
||||||
|
return ts_initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** returns the full-scale transmit amplitude **/
|
||||||
|
double fullScaleInputValue() {
|
||||||
|
return(double) SHRT_MAX * LIMESDR_TX_AMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** returns the full-scale receive amplitude **/
|
||||||
|
double fullScaleOutputValue() {
|
||||||
|
return (double) SHRT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** sets the receive chan gain, returns the gain setting **/
|
||||||
|
double setRxGain(double dB, size_t chan = 0);
|
||||||
|
|
||||||
|
/** get the current receive gain */
|
||||||
|
double getRxGain(size_t chan = 0) {
|
||||||
|
return rxGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** return maximum Rx Gain **/
|
||||||
|
double maxRxGain(void);
|
||||||
|
|
||||||
|
/** return minimum Rx Gain **/
|
||||||
|
double minRxGain(void);
|
||||||
|
|
||||||
|
/** sets the transmit chan gain, returns the gain setting **/
|
||||||
|
double setTxGain(double dB, size_t chan = 0);
|
||||||
|
|
||||||
|
/** return maximum Tx Gain **/
|
||||||
|
double maxTxGain(void);
|
||||||
|
|
||||||
|
/** return minimum Rx Gain **/
|
||||||
|
double minTxGain(void);
|
||||||
|
|
||||||
|
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||||
|
bool setRxAntenna(const std::string & ant, size_t chan = 0);
|
||||||
|
|
||||||
|
/* return the used RX path */
|
||||||
|
std::string getRxAntenna(size_t chan = 0);
|
||||||
|
|
||||||
|
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||||
|
bool setTxAntenna(const std::string & ant, size_t chan = 0);
|
||||||
|
|
||||||
|
/* return the used RX path */
|
||||||
|
std::string getTxAntenna(size_t chan = 0);
|
||||||
|
|
||||||
|
/** return whether user drives synchronization of Tx/Rx of USRP */
|
||||||
|
bool requiresRadioAlign();
|
||||||
|
|
||||||
|
/** return whether user drives synchronization of Tx/Rx of USRP */
|
||||||
|
virtual GSM::Time minLatency();
|
||||||
|
|
||||||
|
/** Return internal status values */
|
||||||
|
inline double getTxFreq(size_t chan = 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
inline double getRxFreq(size_t chan = 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
inline double getSampleRate() {
|
||||||
|
return actualSampleRate;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _LMS_DEVICE_H_
|
||||||
11
Transceiver52M/device/lms/Makefile.am
Normal file
11
Transceiver52M/device/lms/Makefile.am
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||||
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
|
||||||
|
|
||||||
|
noinst_HEADERS = LMSDevice.h
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = libdevice.la
|
||||||
|
|
||||||
|
libdevice_la_SOURCES = LMSDevice.cpp
|
||||||
|
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
|
||||||
11
Transceiver52M/device/uhd/Makefile.am
Normal file
11
Transceiver52M/device/uhd/Makefile.am
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||||
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
|
||||||
|
|
||||||
|
noinst_HEADERS = UHDDevice.h
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = libdevice.la
|
||||||
|
|
||||||
|
libdevice_la_SOURCES = UHDDevice.cpp
|
||||||
|
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
|
||||||
1183
Transceiver52M/device/uhd/UHDDevice.cpp
Normal file
1183
Transceiver52M/device/uhd/UHDDevice.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user