aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore53
-rw-r--r--.gitreview3
-rw-r--r--AUTHORS127
-rw-r--r--COPYING708
-rw-r--r--ChangeLog0
-rw-r--r--CommonLibs/BitVector.cpp369
-rw-r--r--CommonLibs/BitVector.h256
-rw-r--r--CommonLibs/Interthread.h696
-rw-r--r--CommonLibs/LinkedLists.cpp83
-rw-r--r--CommonLibs/LinkedLists.h175
-rw-r--r--CommonLibs/Logger.cpp64
-rw-r--r--CommonLibs/Logger.h94
-rw-r--r--CommonLibs/Makefile.am54
-rw-r--r--CommonLibs/PRBS.h110
-rw-r--r--CommonLibs/Sockets.cpp287
-rw-r--r--CommonLibs/Sockets.h173
-rw-r--r--CommonLibs/Threads.cpp140
-rw-r--r--CommonLibs/Threads.h192
-rw-r--r--CommonLibs/Timeval.cpp98
-rw-r--r--CommonLibs/Timeval.h105
-rw-r--r--CommonLibs/Vector.h308
-rw-r--r--CommonLibs/config_defs.h20
-rw-r--r--CommonLibs/debug.c30
-rw-r--r--CommonLibs/debug.h10
-rw-r--r--CommonLibs/osmo_signal.h35
-rw-r--r--CommonLibs/trx_vty.c576
-rw-r--r--CommonLibs/trx_vty.h68
-rw-r--r--GSM/GSMCommon.cpp93
-rw-r--r--GSM/GSMCommon.h253
-rw-r--r--GSM/Makefile.am32
-rw-r--r--INSTALLATION19
-rw-r--r--LEGAL60
-rw-r--r--Makefile.am55
-rw-r--r--Makefile.common38
-rw-r--r--NEWS0
-rw-r--r--README116
-rw-r--r--Transceiver52M/Channelizer.cpp107
-rw-r--r--Transceiver52M/Channelizer.h34
-rw-r--r--Transceiver52M/ChannelizerBase.cpp251
-rw-r--r--Transceiver52M/ChannelizerBase.h39
-rw-r--r--Transceiver52M/Complex.h266
-rw-r--r--Transceiver52M/Makefile.am111
-rw-r--r--Transceiver52M/README35
-rw-r--r--Transceiver52M/README.DFEsymbolspaced14
-rw-r--r--Transceiver52M/Resampler.cpp187
-rw-r--r--Transceiver52M/Resampler.h76
-rw-r--r--Transceiver52M/Synthesis.cpp121
-rw-r--r--Transceiver52M/Synthesis.h35
-rw-r--r--Transceiver52M/Transceiver.cpp1128
-rw-r--r--Transceiver52M/Transceiver.h282
-rw-r--r--Transceiver52M/arch/Makefile.am8
-rw-r--r--Transceiver52M/arch/arm/Makefile.am22
-rw-r--r--Transceiver52M/arch/arm/convert.c85
-rw-r--r--Transceiver52M/arch/arm/convert_neon.S51
-rw-r--r--Transceiver52M/arch/arm/convolve.c146
-rw-r--r--Transceiver52M/arch/arm/convolve_neon.S277
-rw-r--r--Transceiver52M/arch/arm/mult.c56
-rw-r--r--Transceiver52M/arch/arm/mult_neon.S42
-rw-r--r--Transceiver52M/arch/arm/scale.c56
-rw-r--r--Transceiver52M/arch/arm/scale_neon.S50
-rw-r--r--Transceiver52M/arch/common/Makefile.am15
-rw-r--r--Transceiver52M/arch/common/convert.h15
-rw-r--r--Transceiver52M/arch/common/convert_base.c34
-rw-r--r--Transceiver52M/arch/common/convolve.h32
-rw-r--r--Transceiver52M/arch/common/convolve_base.c156
-rw-r--r--Transceiver52M/arch/common/fft.c112
-rw-r--r--Transceiver52M/arch/common/fft.h13
-rw-r--r--Transceiver52M/arch/common/mult.h6
-rw-r--r--Transceiver52M/arch/common/scale.h6
-rw-r--r--Transceiver52M/arch/x86/Makefile.am33
-rw-r--r--Transceiver52M/arch/x86/convert.c83
-rw-r--r--Transceiver52M/arch/x86/convert_sse_3.c107
-rw-r--r--Transceiver52M/arch/x86/convert_sse_3.h34
-rw-r--r--Transceiver52M/arch/x86/convert_sse_4_1.c77
-rw-r--r--Transceiver52M/arch/x86/convert_sse_4_1.h28
-rw-r--r--Transceiver52M/arch/x86/convolve.c172
-rw-r--r--Transceiver52M/arch/x86/convolve_sse_3.c542
-rw-r--r--Transceiver52M/arch/x86/convolve_sse_3.h68
-rw-r--r--Transceiver52M/device/Makefile.am17
-rw-r--r--Transceiver52M/device/lms/LMSDevice.cpp637
-rw-r--r--Transceiver52M/device/lms/LMSDevice.h202
-rw-r--r--Transceiver52M/device/lms/Makefile.am10
-rw-r--r--Transceiver52M/device/radioDevice.h210
-rw-r--r--Transceiver52M/device/uhd/Makefile.am8
-rw-r--r--Transceiver52M/device/uhd/UHDDevice.cpp1561
-rw-r--r--Transceiver52M/device/usrp1/Makefile.am10
-rw-r--r--Transceiver52M/device/usrp1/USRPDevice.cpp671
-rw-r--r--Transceiver52M/device/usrp1/USRPDevice.h209
-rw-r--r--Transceiver52M/inband-signaling-usb314
-rw-r--r--Transceiver52M/laurent.m83
-rw-r--r--Transceiver52M/osmo-trx.cpp605
-rw-r--r--Transceiver52M/pulseApproximate.m15
-rw-r--r--Transceiver52M/radioBuffer.cpp228
-rw-r--r--Transceiver52M/radioBuffer.h45
-rw-r--r--Transceiver52M/radioClock.cpp49
-rw-r--r--Transceiver52M/radioClock.h40
-rw-r--r--Transceiver52M/radioInterface.cpp360
-rw-r--r--Transceiver52M/radioInterface.h188
-rw-r--r--Transceiver52M/radioInterfaceMulti.cpp405
-rw-r--r--Transceiver52M/radioInterfaceResamp.cpp235
-rw-r--r--Transceiver52M/radioVector.cpp155
-rw-r--r--Transceiver52M/radioVector.h68
-rw-r--r--Transceiver52M/sigProcLib.cpp1892
-rw-r--r--Transceiver52M/sigProcLib.h130
-rw-r--r--Transceiver52M/signalVector.cpp107
-rw-r--r--Transceiver52M/signalVector.h55
-rwxr-xr-xTransceiver52M/std_inband.rbfbin0 -> 176507 bytes
-rwxr-xr-xautogen.sh1578
-rw-r--r--config/ax_check_compile_flag.m472
-rw-r--r--config/ax_cxx_compile_stdcxx.m4982
-rw-r--r--config/ax_cxx_compile_stdcxx_11.m439
-rw-r--r--config/ax_sse.m471
-rw-r--r--configure.ac270
-rw-r--r--contrib/Makefile.am1
-rwxr-xr-xcontrib/jenkins.sh87
-rw-r--r--contrib/systemd/Makefile.am18
-rw-r--r--contrib/systemd/osmo-trx-lms.service11
-rw-r--r--contrib/systemd/osmo-trx-uhd.service11
-rw-r--r--contrib/systemd/osmo-trx-usrp1.service11
-rw-r--r--debian/changelog300
-rw-r--r--debian/compat1
-rw-r--r--debian/control91
-rw-r--r--debian/copyright147
-rw-r--r--debian/osmo-trx-lms.install4
-rw-r--r--debian/osmo-trx-uhd.install6
-rw-r--r--debian/osmo-trx-usrp1.install4
-rw-r--r--debian/osmo-trx.install0
-rw-r--r--debian/patches/build-for-debian8.patch57
-rw-r--r--debian/patches/series1
-rwxr-xr-xdebian/rules15
-rw-r--r--debian/source/format1
-rw-r--r--doc/Makefile.am3
-rw-r--r--doc/examples/Makefile.am41
-rw-r--r--doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg22
l---------doc/examples/osmo-trx-lms/osmo-trx-lms.cfg1
-rw-r--r--doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg22
l---------doc/examples/osmo-trx-uhd/osmo-trx-uhd.cfg1
-rw-r--r--doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg23
-rw-r--r--doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg22
-rwxr-xr-xgit-version-gen151
-rw-r--r--tests/CommonLibs/BitVectorTest.cpp60
-rw-r--r--tests/CommonLibs/BitVectorTest.ok8
-rw-r--r--tests/CommonLibs/InterthreadTest.cpp137
-rw-r--r--tests/CommonLibs/InterthreadTest.ok1
-rw-r--r--tests/CommonLibs/LogTest.cpp69
-rw-r--r--tests/CommonLibs/LogTest.err3
-rw-r--r--tests/CommonLibs/LogTest.ok0
-rw-r--r--tests/CommonLibs/Makefile.am46
-rw-r--r--tests/CommonLibs/PRBSTest.cpp42
-rw-r--r--tests/CommonLibs/PRBSTest.ok4
-rw-r--r--tests/CommonLibs/SocketsTest.cpp101
-rw-r--r--tests/CommonLibs/SocketsTest.ok1
-rw-r--r--tests/CommonLibs/TimevalTest.cpp64
-rw-r--r--tests/CommonLibs/TimevalTest.ok1
-rw-r--r--tests/CommonLibs/VectorTest.cpp63
-rw-r--r--tests/CommonLibs/VectorTest.ok10
-rw-r--r--tests/Makefile.am41
-rw-r--r--tests/Transceiver52M/Makefile.am18
-rw-r--r--tests/Transceiver52M/convolve_test.c150
-rw-r--r--tests/Transceiver52M/convolve_test.ok72
-rw-r--r--tests/testsuite.at53
-rwxr-xr-xutils/clockdump.sh3
162 files changed, 24032 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..83c59d9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,53 @@
+# 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
+
+# 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?
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..4521e98
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,3 @@
+[gerrit]
+host=gerrit.osmocom.org
+project=osmo-trx
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..53a3c7d
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,127 @@
+#
+# Copyright 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+David A. Burgess, dburgess@kestrelsp.com:
+ CLI/CLI.cpp
+ CLI/CLI.h
+ CommonLibs/Assert.h
+ CommonLibs/BitVector.cpp
+ CommonLibs/Interthread.h
+ CommonLibs/LinkedLists.cpp
+ CommonLibs/LinkedLists.h
+ CommonLibs/Regexp.h
+ CommonLibs/Sockets.cpp
+ CommonLibs/Sockets.h
+ CommonLibs/Threads.cpp
+ CommonLibs/Threads.h
+ CommonLibs/Timeval.cpp
+ CommonLibs/Timeval.h
+ CommonLibs/Vector.h
+ GSM/GSM610Tables.cpp
+ GSM/GSM610Tables.h
+ GSM/GSMCommon.cpp
+ GSM/GSMCommon.h
+ GSM/GSMConfig.h
+ GSM/GSML1FEC.cpp
+ GSM/GSML1FEC.h
+ GSM/GSML2LAPDm.cpp
+ GSM/GSML2LAPDm.h
+ GSM/GSML3CCElements.cpp
+ GSM/GSML3CCElements.h
+ GSM/GSML3CCMessages.cpp
+ GSM/GSML3CCMessages.h
+ GSM/GSML3CommonElements.cpp
+ GSM/GSML3CommonElements.h
+ GSM/GSML3MMElements.cpp
+ GSM/GSML3MMElements.h
+ GSM/GSML3MMMessages.cpp
+ GSM/GSML3MMMessages.h
+ GSM/GSML3Message.cpp
+ GSM/GSML3Message.h
+ GSM/GSML3RRElements.cpp
+ GSM/GSML3RRElements.h
+ GSM/GSML3RRMessages.cpp
+ GSM/GSML3RRMessages.h
+ GSM/GSMLogicalChannel.h
+ GSM/GSMTDMA.cpp
+ GSM/GSMTDMA.h
+ GSM/GSMTransfer.cpp
+ GSM/GSMTransfer.h
+ LICENSEBLOCK
+ TRXManager/TRXManager.cpp
+ Transceiver/Complex.h
+ tests/CommonLibs/BitVectorTest.cpp
+ tests/CommonLibs/InterthreadTest.cpp
+ tests/CommonLibs/SocketsTest.cpp
+ tests/CommonLibs/TimevalTest.cpp
+ tests/CommonLibs/VectorTest.cpp
+
+Harvind S. Samra, hssamra@kestrelsp.com:
+ GSM/GSMConfig.h
+ GSM/GSMTransfer.h
+ LICENSEBLOCK
+ Transceiver/ComplexTest.cpp
+ Transceiver/Transceiver.cpp
+ Transceiver/Transceiver.h
+ Transceiver/USRPDevice.cpp
+ Transceiver/USRPDevice.h
+ Transceiver/USRPping.cpp
+ Transceiver/radioInterface.cpp
+ Transceiver/radioInterface.h
+ Transceiver/rcvLPF_651.h
+ Transceiver/runTransceiver.cpp
+ Transceiver/sendLPF_961.h
+ Transceiver/sigProcLib.cpp
+ Transceiver/sigProcLib.h
+ Transceiver/sigProcLibTest.cpp
+ Transceiver/sweepGenerator.cpp
+ Transceiver/testRadio.cpp
+
+Raffi Sevlian, raffisev@gmail.com:
+ GSM/GSMCommon.h
+ GSM/GSMConfig.h
+ GSM/GSML1FEC.h
+ GSM/GSML3CCElements.cpp
+ GSM/GSML3CCElements.h
+ GSM/GSML3CCMessages.cpp
+ GSM/GSML3CCMessages.h
+ GSM/GSML3CommonElements.cpp
+ GSM/GSML3CommonElements.h
+ GSM/GSML3MMElements.cpp
+ GSM/GSML3MMElements.h
+ GSM/GSML3MMMessages.cpp
+ GSM/GSML3MMMessages.h
+ GSM/GSML3Message.cpp
+ GSM/GSML3Message.h
+ GSM/GSML3RRElements.cpp
+ GSM/GSML3RRElements.h
+ GSM/GSML3RRMessages.cpp
+ GSM/GSML3RRMessages.h
+ GSM/GSMLogicalChannel.h
+ GSM/GSMSAPMux.cpp
+ GSM/GSMSAPMux.h
+ GSM/GSMTransfer.h
+ LICENSEBLOCK
+ TRXManager/TRXManager.h
+
+Alon Levy, alonlevy1@gmail.com
+ RRLPMessages.cpp
+ RRLPMessages.h
+ RRLPTest.cpp
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..879341e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,708 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
+
+
+
+
+=========================================================================
+
+This marks the end of the AGPLv3 text. The following text is appended to the
+same file for convience but constituting a distinct document, not part of the
+actual AGPL text and not part of an attempt to create a deriviative work based
+on the AGPLv3 text.
+
+=========================================================================
+
+
+ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX
+
+
+Permissive Terms Supplementing the License
+
+1. Remote Interaction Through IP Networks.
+
+OsmoTRX is an implementation of the GSM network cellular air interface,
+as well as other interfaces to IP networks. The interaction of cellular
+handsets with the OsmoTRX software is considered "remote network interaction"
+for the purposes of the Affero General Public License and cellular users are
+subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
+Network Interaction; Use with the GNU General Public License").
+
+Remote interactions through interfaces other than the GSM air interface are, at
+your option, exempted from the requirements of Section 13 ("Remote Network
+Interaction; Use with the GNU General Public License"). This exemption of
+interfaces other than the GSM air interface from the requirements of Section 13
+is an additional permission granted to you.
+
+
+END OF ADDITIONAL TERMS
+
+
+
+How to comply with Section 13 of the AGPLv3 license.
+
+The recommended method for compliance with Section 13 of the AGPLv3 license is
+to deliver a text message to each handset that attaches to the cellular
+network which uses OsmoTRX. At a minimum, that text message should include the string
+"OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This
+message need not be delivered to handsets that are denied registration with the
+network, since those handsets have been denied service.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/CommonLibs/BitVector.cpp b/CommonLibs/BitVector.cpp
new file mode 100644
index 0000000..cf408cd
--- /dev/null
+++ b/CommonLibs/BitVector.cpp
@@ -0,0 +1,369 @@
+/*
+* Copyright 2008, 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 "BitVector.h"
+#include <iostream>
+#include <stdio.h>
+#include <sstream>
+#include <math.h>
+
+using namespace std;
+
+
+/**
+ Apply a Galois polymonial to a binary seqeunce.
+ @param val The input sequence.
+ @param poly The polynomial.
+ @param order The order of the polynomial.
+ @return Single-bit result.
+*/
+unsigned applyPoly(uint64_t val, uint64_t poly, unsigned order)
+{
+ uint64_t prod = val & poly;
+ unsigned sum = prod;
+ for (unsigned i=1; i<order; i++) sum ^= prod>>i;
+ return sum & 0x01;
+}
+
+
+
+
+
+
+BitVector::BitVector(const char *valString)
+ :Vector<char>(strlen(valString))
+{
+ uint32_t accum = 0;
+ for (size_t i=0; i<size(); i++) {
+ accum <<= 1;
+ if (valString[i]=='1') accum |= 0x01;
+ mStart[i] = accum;
+ }
+}
+
+
+
+
+
+uint64_t BitVector::peekField(size_t readIndex, unsigned length) const
+{
+ uint64_t accum = 0;
+ char *dp = mStart + readIndex;
+ assert(dp+length <= mEnd);
+ for (unsigned i=0; i<length; i++) {
+ accum = (accum<<1) | ((*dp++) & 0x01);
+ }
+ return accum;
+}
+
+
+
+
+uint64_t BitVector::peekFieldReversed(size_t readIndex, unsigned length) const
+{
+ uint64_t accum = 0;
+ char *dp = mStart + readIndex + length - 1;
+ assert(dp<mEnd);
+ for (int i=(length-1); i>=0; i--) {
+ accum = (accum<<1) | ((*dp--) & 0x01);
+ }
+ return accum;
+}
+
+
+
+
+uint64_t BitVector::readField(size_t& readIndex, unsigned length) const
+{
+ const uint64_t retVal = peekField(readIndex,length);
+ readIndex += length;
+ return retVal;
+}
+
+
+uint64_t BitVector::readFieldReversed(size_t& readIndex, unsigned length) const
+{
+ const uint64_t retVal = peekFieldReversed(readIndex,length);
+ readIndex += length;
+ return retVal;
+}
+
+
+
+
+
+void BitVector::fillField(size_t writeIndex, uint64_t value, unsigned length)
+{
+ char *dpBase = mStart + writeIndex;
+ char *dp = dpBase + length - 1;
+ assert(dp < mEnd);
+ while (dp>=dpBase) {
+ *dp-- = value & 0x01;
+ value >>= 1;
+ }
+}
+
+
+void BitVector::fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length)
+{
+ char *dp = mStart + writeIndex;
+ char *dpEnd = dp + length - 1;
+ assert(dpEnd < mEnd);
+ while (dp<=dpEnd) {
+ *dp++ = value & 0x01;
+ value >>= 1;
+ }
+}
+
+
+
+
+void BitVector::writeField(size_t& writeIndex, uint64_t value, unsigned length)
+{
+ fillField(writeIndex,value,length);
+ writeIndex += length;
+}
+
+
+void BitVector::writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length)
+{
+ fillFieldReversed(writeIndex,value,length);
+ writeIndex += length;
+}
+
+
+void BitVector::invert()
+{
+ for (size_t i=0; i<size(); i++) {
+ mStart[i] = ~mStart[i];
+ }
+}
+
+
+
+
+void BitVector::reverse8()
+{
+ assert(size()>=8);
+
+ char tmp0 = mStart[0];
+ mStart[0] = mStart[7];
+ mStart[7] = tmp0;
+
+ char tmp1 = mStart[1];
+ mStart[1] = mStart[6];
+ mStart[6] = tmp1;
+
+ char tmp2 = mStart[2];
+ mStart[2] = mStart[5];
+ mStart[5] = tmp2;
+
+ char tmp3 = mStart[3];
+ mStart[3] = mStart[4];
+ mStart[4] = tmp3;
+}
+
+
+
+void BitVector::LSB8MSB()
+{
+ if (size()<8) return;
+ size_t size8 = 8*(size()/8);
+ size_t iTop = size8 - 8;
+ for (size_t i=0; i<=iTop; i+=8) segment(i,8).reverse8();
+}
+
+
+
+unsigned BitVector::sum() const
+{
+ unsigned sum = 0;
+ for (size_t i=0; i<size(); i++) sum += mStart[i] & 0x01;
+ return sum;
+}
+
+
+
+
+void BitVector::map(const unsigned *map, size_t mapSize, BitVector& dest) const
+{
+ for (unsigned i=0; i<mapSize; i++) {
+ dest.mStart[i] = mStart[map[i]];
+ }
+}
+
+
+
+
+void BitVector::unmap(const unsigned *map, size_t mapSize, BitVector& dest) const
+{
+ for (unsigned i=0; i<mapSize; i++) {
+ dest.mStart[map[i]] = mStart[i];
+ }
+}
+
+
+
+
+
+
+
+ostream& operator<<(ostream& os, const BitVector& hv)
+{
+ for (size_t i=0; i<hv.size(); i++) {
+ if (hv.bit(i)) os << '1';
+ else os << '0';
+ }
+ return os;
+}
+
+
+
+
+SoftVector::SoftVector(const BitVector& source)
+{
+ resize(source.size());
+ for (size_t i=0; i<size(); i++) {
+ if (source.bit(i)) mStart[i]=1.0F;
+ else mStart[i]=-1.0F;
+ }
+}
+
+
+BitVector SoftVector::sliced() const
+{
+ size_t sz = size();
+ BitVector newSig(sz);
+ for (size_t i=0; i<sz; i++) {
+ if (mStart[i]>0.0F) newSig[i]=1;
+ else newSig[i] = 0;
+ }
+ return newSig;
+}
+
+
+float SoftVector::getEnergy(float *plow) const
+{
+ const SoftVector &vec = *this;
+ int len = vec.size();
+ float avg = 0; float low = 1;
+ for (int i = 0; i < len; i++) {
+ float energy = fabsf(vec[i]);
+ if (energy < low) low = energy;
+ avg += energy/len;
+ }
+ if (plow) { *plow = low; }
+ return avg;
+}
+
+
+ostream& operator<<(ostream& os, const SoftVector& sv)
+{
+ for (size_t i=0; i<sv.size(); i++) {
+ if (sv[i]<-0.5) os << "0";
+ else if (sv[i]<-0.25) os << "o";
+ else if (sv[i]<0.0) os << ".";
+ else if (sv[i]>0.5) os << "1";
+ else if (sv[i]>0.25) os << "|";
+ else if (sv[i]>0.0) os << "'";
+ else os << "-";
+ }
+ return os;
+}
+
+
+
+void BitVector::pack(unsigned char* targ) const
+{
+ // Assumes MSB-first packing.
+ unsigned bytes = size()/8;
+ for (unsigned i=0; i<bytes; i++) {
+ targ[i] = peekField(i*8,8);
+ }
+ unsigned whole = bytes*8;
+ unsigned rem = size() - whole;
+ if (rem==0) return;
+ targ[bytes] = peekField(whole,rem) << (8-rem);
+}
+
+
+void BitVector::unpack(const unsigned char* src)
+{
+ // Assumes MSB-first packing.
+ unsigned bytes = size()/8;
+ for (unsigned i=0; i<bytes; i++) {
+ fillField(i*8,src[i],8);
+ }
+ unsigned whole = bytes*8;
+ unsigned rem = size() - whole;
+ if (rem==0) return;
+ fillField(whole,src[bytes] >> (8-rem),rem);
+}
+
+void BitVector::hex(ostream& os) const
+{
+ os << std::hex;
+ unsigned digits = size()/4;
+ size_t wp=0;
+ for (unsigned i=0; i<digits; i++) {
+ os << readField(wp,4);
+ }
+ os << std::dec;
+}
+
+std::string BitVector::hexstr() const
+{
+ std::ostringstream ss;
+ hex(ss);
+ return ss.str();
+}
+
+
+bool BitVector::unhex(const char* src)
+{
+ // Assumes MSB-first packing.
+ unsigned int val;
+ unsigned digits = size()/4;
+ for (unsigned i=0; i<digits; i++) {
+ if (sscanf(src+i, "%1x", &val) < 1) {
+ return false;
+ }
+ fillField(i*4,val,4);
+ }
+ unsigned whole = digits*4;
+ unsigned rem = size() - whole;
+ if (rem>0) {
+ if (sscanf(src+digits, "%1x", &val) < 1) {
+ return false;
+ }
+ fillField(whole,val,rem);
+ }
+ return true;
+}
+
+// vim: ts=4 sw=4
diff --git a/CommonLibs/BitVector.h b/CommonLibs/BitVector.h
new file mode 100644
index 0000000..559dd99
--- /dev/null
+++ b/CommonLibs/BitVector.h
@@ -0,0 +1,256 @@
+/*
+* Copyright 2008, 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 FECVECTORS_H
+#define FECVECTORS_H
+
+#include "Vector.h"
+#include <stdint.h>
+
+
+class BitVector : public Vector<char> {
+
+
+ public:
+
+ /**@name Constructors. */
+ //@{
+
+ /**@name Casts of Vector constructors. */
+ //@{
+ BitVector(char* wData, char* wStart, char* wEnd)
+ :Vector<char>(wData,wStart,wEnd)
+ { }
+ BitVector(size_t len=0):Vector<char>(len) {}
+ BitVector(const Vector<char>& source):Vector<char>(source) {}
+ BitVector(Vector<char>& source):Vector<char>(source) {}
+ BitVector(const Vector<char>& source1, const Vector<char> source2):Vector<char>(source1,source2) {}
+ //@}
+
+ /** Construct from a string of "0" and "1". */
+ BitVector(const char* valString);
+ //@}
+
+ /** Index a single bit. */
+ bool bit(size_t index) const
+ {
+ // We put this code in .h for fast inlining.
+ const char *dp = mStart+index;
+ assert(dp<mEnd);
+ return (*dp) & 0x01;
+ }
+
+ /**@name Casts and overrides of Vector operators. */
+ //@{
+ BitVector segment(size_t start, size_t span)
+ {
+ char* wStart = mStart + start;
+ char* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return BitVector(NULL,wStart,wEnd);
+ }
+
+ BitVector alias()
+ { return segment(0,size()); }
+
+ const BitVector segment(size_t start, size_t span) const
+ { return (BitVector)(Vector<char>::segment(start,span)); }
+
+ BitVector head(size_t span) { return segment(0,span); }
+ const BitVector head(size_t span) const { return segment(0,span); }
+ BitVector tail(size_t start) { return segment(start,size()-start); }
+ const BitVector tail(size_t start) const { return segment(start,size()-start); }
+ //@}
+
+
+ void zero() { fill(0); }
+
+
+ /** Invert 0<->1. */
+ void invert();
+
+ /**@name Byte-wise operations. */
+ //@{
+ /** Reverse an 8-bit vector. */
+ void reverse8();
+ /** Reverse groups of 8 within the vector (byte reversal). */
+ void LSB8MSB();
+ //@}
+
+ /**@name Serialization and deserialization. */
+ //@{
+ uint64_t peekField(size_t readIndex, unsigned length) const;
+ uint64_t peekFieldReversed(size_t readIndex, unsigned length) const;
+ uint64_t readField(size_t& readIndex, unsigned length) const;
+ uint64_t readFieldReversed(size_t& readIndex, unsigned length) const;
+ void fillField(size_t writeIndex, uint64_t value, unsigned length);
+ void fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length);
+ void writeField(size_t& writeIndex, uint64_t value, unsigned length);
+ void writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length);
+ void write0(size_t& writeIndex) { writeField(writeIndex,0,1); }
+ void write1(size_t& writeIndex) { writeField(writeIndex,1,1); }
+
+ //@}
+
+ /** Sum of bits. */
+ unsigned sum() const;
+
+ /** Reorder bits, dest[i] = this[map[i]]. */
+ void map(const unsigned *map, size_t mapSize, BitVector& dest) const;
+
+ /** Reorder bits, dest[map[i]] = this[i]. */
+ void unmap(const unsigned *map, size_t mapSize, BitVector& dest) const;
+
+ /** Pack into a char array. */
+ void pack(unsigned char*) const;
+
+ /** Unpack from a char array. */
+ void unpack(const unsigned char*);
+
+ /** Make a hexdump string. */
+ void hex(std::ostream&) const;
+ std::string hexstr() const;
+
+ /** Unpack from a hexdump string.
+ * @returns true on success, false on error. */
+ bool unhex(const char*);
+
+ void set(BitVector other) // That's right. No ampersand.
+ {
+ clear();
+ mData=other.mData;
+ mStart=other.mStart;
+ mEnd=other.mEnd;
+ other.mData=NULL;
+ }
+
+ void settfb(int i, int j) const
+ {
+ mStart[i] = j;
+ }
+
+};
+
+
+
+std::ostream& operator<<(std::ostream&, const BitVector&);
+
+
+
+
+
+
+/**
+ The SoftVector class is used to represent a soft-decision signal.
+ Values 0..1 represent probabilities that a bit is "true".
+ */
+class SoftVector: public Vector<float> {
+
+ public:
+
+ /** Build a SoftVector of a given length. */
+ SoftVector(size_t wSize=0):Vector<float>(wSize) {}
+
+ /** Construct a SoftVector from a C string of "0", "1", and "X". */
+ SoftVector(const char* valString);
+
+ /** Construct a SoftVector from a BitVector. */
+ SoftVector(const BitVector& source);
+
+ /**
+ Wrap a SoftVector around a block of floats.
+ The block will be delete[]ed upon desctuction.
+ */
+ SoftVector(float *wData, unsigned length)
+ :Vector<float>(wData,length)
+ {}
+
+ SoftVector(float* wData, float* wStart, float* wEnd)
+ :Vector<float>(wData,wStart,wEnd)
+ { }
+
+ /**
+ Casting from a Vector<float>.
+ Note that this is NOT pass-by-reference.
+ */
+ SoftVector(Vector<float> source)
+ :Vector<float>(source)
+ {}
+
+
+ /**@name Casts and overrides of Vector operators. */
+ //@{
+ SoftVector segment(size_t start, size_t span)
+ {
+ float* wStart = mStart + start;
+ float* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return SoftVector(NULL,wStart,wEnd);
+ }
+
+ SoftVector alias()
+ { return segment(0,size()); }
+
+ const SoftVector segment(size_t start, size_t span) const
+ { return (SoftVector)(Vector<float>::segment(start,span)); }
+
+ SoftVector head(size_t span) { return segment(0,span); }
+ const SoftVector head(size_t span) const { return segment(0,span); }
+ SoftVector tail(size_t start) { return segment(start,size()-start); }
+ const SoftVector tail(size_t start) const { return segment(start,size()-start); }
+ //@}
+
+ // How good is the SoftVector in the sense of the bits being solid?
+ // Result of 1 is perfect and 0 means all the bits were 0.0
+ // If plow is non-NULL, also return the lowest energy bit.
+ float getEnergy(float *low=0) const;
+
+ /** Fill with "unknown" values. */
+ void unknown() { fill(0.0F); }
+
+ /** Return a hard bit value from a given index by slicing. */
+ bool bit(size_t index) const
+ {
+ const float *dp = mStart+index;
+ assert(dp<mEnd);
+ return (*dp)>0.0F;
+ }
+
+ /** Slice the whole signal into bits. */
+ BitVector sliced() const;
+
+};
+
+
+
+std::ostream& operator<<(std::ostream&, const SoftVector&);
+
+
+
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Interthread.h b/CommonLibs/Interthread.h
new file mode 100644
index 0000000..42e6f7f
--- /dev/null
+++ b/CommonLibs/Interthread.h
@@ -0,0 +1,696 @@
+/*
+* Copyright 2008, 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/>.
+
+*/
+
+
+#ifndef INTERTHREAD_H
+#define INTERTHREAD_H
+
+#include "Timeval.h"
+#include "Threads.h"
+#include "LinkedLists.h"
+#include <map>
+#include <vector>
+#include <queue>
+
+
+
+
+
+/**@defgroup Templates for interthread mechanisms. */
+//@{
+
+
+/** Pointer FIFO for interthread operations. */
+// (pat) The elements in the queue are type T*, and
+// the Fifo class implements the underlying queue.
+// The default is class PointerFIFO, which does not place any restrictions on the type of T,
+// and is implemented by allocating auxilliary structures for the queue,
+// or SingleLinkedList, which implements the queue using an internal pointer in type T,
+// which must implement the functional interface of class SingleLinkListNode,
+// namely: functions T*next() and void setNext(T*).
+template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
+
+ protected:
+
+ Fifo mQ;
+ mutable Mutex mLock;
+ mutable Signal mWriteSignal;
+
+ public:
+
+ /** Delete contents. */
+ void clear()
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>0) delete (T*)mQ.get();
+ }
+
+ /** Empty the queue, but don't delete. */
+ void flushNoDelete()
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>0) mQ.get();
+ }
+
+
+ ~InterthreadQueue()
+ { clear(); }
+
+
+ size_t size() const
+ {
+ ScopedLock lock(mLock);
+ return mQ.size();
+ }
+
+ size_t totalSize() const // pat added
+ {
+ ScopedLock lock(mLock);
+ return mQ.totalSize();
+ }
+
+ /**
+ Blocking read.
+ @return Pointer to object (will not be NULL).
+ */
+ T* read()
+ {
+ ScopedLock lock(mLock);
+ T* retVal = (T*)mQ.get();
+ while (retVal==NULL) {
+ mWriteSignal.wait(mLock);
+ retVal = (T*)mQ.get();
+ }
+ return retVal;
+ }
+
+ /** Non-blocking peek at the first element; returns NULL if empty. */
+ T* front()
+ {
+ ScopedLock lock(mLock);
+ return (T*) mQ.front();
+ }
+
+ /**
+ Blocking read with a timeout.
+ @param timeout The read timeout in ms.
+ @return Pointer to object or NULL on timeout.
+ */
+ T* read(unsigned timeout)
+ {
+ if (timeout==0) return readNoBlock();
+ Timeval waitTime(timeout);
+ ScopedLock lock(mLock);
+ while ((mQ.size()==0) && (!waitTime.passed()))
+ mWriteSignal.wait(mLock,waitTime.remaining());
+ T* retVal = (T*)mQ.get();
+ return retVal;
+ }
+
+ /**
+ Non-blocking read.
+ @return Pointer to object or NULL if FIFO is empty.
+ */
+ T* readNoBlock()
+ {
+ ScopedLock lock(mLock);
+ return (T*)mQ.get();
+ }
+
+ /** Non-blocking write. */
+ void write(T* val)
+ {
+ ScopedLock lock(mLock);
+ mQ.put(val);
+ mWriteSignal.signal();
+ }
+
+ /** Non-block write to the front of the queue. */
+ void write_front(T* val) // pat added
+ {
+ ScopedLock lock(mLock);
+ mQ.push_front(val);
+ mWriteSignal.signal();
+ }
+};
+
+// (pat) Identical to above but with the threading problem fixed.
+template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
+
+ protected:
+
+ Fifo mQ;
+ mutable Mutex mLock;
+ mutable Signal mWriteSignal;
+
+ public:
+
+ /** Delete contents. */
+ void clear()
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>0) delete (T*)mQ.get();
+ }
+
+ /** Empty the queue, but don't delete. */
+ void flushNoDelete()
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>0) mQ.get();
+ }
+
+
+ ~InterthreadQueue2()
+ { clear(); }
+
+
+ size_t size() const
+ {
+ ScopedLock lock(mLock);
+ return mQ.size();
+ }
+
+ size_t totalSize() const // pat added
+ {
+ ScopedLock lock(mLock);
+ return mQ.totalSize();
+ }
+
+ /**
+ Blocking read.
+ @return Pointer to object (will not be NULL).
+ */
+ T* read()
+ {
+ ScopedLock lock(mLock);
+ T* retVal = (T*)mQ.get();
+ while (retVal==NULL) {
+ mWriteSignal.wait(mLock);
+ retVal = (T*)mQ.get();
+ }
+ return retVal;
+ }
+
+ /** Non-blocking peek at the first element; returns NULL if empty. */
+ T* front()
+ {
+ ScopedLock lock(mLock);
+ return (T*) mQ.front();
+ }
+
+ /**
+ Blocking read with a timeout.
+ @param timeout The read timeout in ms.
+ @return Pointer to object or NULL on timeout.
+ */
+ T* read(unsigned timeout)
+ {
+ if (timeout==0) return readNoBlock();
+ Timeval waitTime(timeout);
+ ScopedLock lock(mLock);
+ while ((mQ.size()==0) && (!waitTime.passed()))
+ mWriteSignal.wait(mLock,waitTime.remaining());
+ T* retVal = (T*)mQ.get();
+ return retVal;
+ }
+
+ /**
+ Non-blocking read.
+ @return Pointer to object or NULL if FIFO is empty.
+ */
+ T* readNoBlock()
+ {
+ ScopedLock lock(mLock);
+ return (T*)mQ.get();
+ }
+
+ /** Non-blocking write. */
+ void write(T* val)
+ {
+ // (pat) The Mutex mLock must be released before signaling the mWriteSignal condition.
+ // This is an implicit requirement of pthread_cond_wait() called from signal().
+ // If you do not do that, the InterthreadQueue read() function cannot start
+ // because the mutex is still locked by the thread calling the write(),
+ // so the read() thread yields its immediate execution opportunity.
+ // This recurs (and the InterthreadQueue fills up with data)
+ // until the read thread's accumulated temporary priority causes it to
+ // get a second pre-emptive activation over the writing thread,
+ // resulting in bursts of activity by the read thread.
+ { ScopedLock lock(mLock);
+ mQ.put(val);
+ }
+ mWriteSignal.signal();
+ }
+
+ /** Non-block write to the front of the queue. */
+ void write_front(T* val) // pat added
+ {
+ // (pat) See comments above.
+ { ScopedLock lock(mLock);
+ mQ.push_front(val);
+ }
+ mWriteSignal.signal();
+ }
+};
+
+
+
+/** Pointer FIFO for interthread operations. */
+template <class T> class InterthreadQueueWithWait {
+
+ protected:
+
+ PointerFIFO mQ;
+ mutable Mutex mLock;
+ mutable Signal mWriteSignal;
+ mutable Signal mReadSignal;
+
+ virtual void freeElement(T* element) const { delete element; };
+
+ public:
+
+ /** Delete contents. */
+ void clear()
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>0) freeElement((T*)mQ.get());
+ mReadSignal.signal();
+ }
+
+
+
+ virtual ~InterthreadQueueWithWait()
+ { clear(); }
+
+
+ size_t size() const
+ {
+ ScopedLock lock(mLock);
+ return mQ.size();
+ }
+
+ /**
+ Blocking read.
+ @return Pointer to object (will not be NULL).
+ */
+ T* read()
+ {
+ ScopedLock lock(mLock);
+ T* retVal = (T*)mQ.get();
+ while (retVal==NULL) {
+ mWriteSignal.wait(mLock);
+ retVal = (T*)mQ.get();
+ }
+ mReadSignal.signal();
+ return retVal;
+ }
+
+ /**
+ Blocking read with a timeout.
+ @param timeout The read timeout in ms.
+ @return Pointer to object or NULL on timeout.
+ */
+ T* read(unsigned timeout)
+ {
+ if (timeout==0) return readNoBlock();
+ Timeval waitTime(timeout);
+ ScopedLock lock(mLock);
+ while ((mQ.size()==0) && (!waitTime.passed()))
+ mWriteSignal.wait(mLock,waitTime.remaining());
+ T* retVal = (T*)mQ.get();
+ if (retVal!=NULL) mReadSignal.signal();
+ return retVal;
+ }
+
+ /**
+ Non-blocking read.
+ @return Pointer to object or NULL if FIFO is empty.
+ */
+ T* readNoBlock()
+ {
+ ScopedLock lock(mLock);
+ T* retVal = (T*)mQ.get();
+ if (retVal!=NULL) mReadSignal.signal();
+ return retVal;
+ }
+
+ /** Non-blocking write. */
+ void write(T* val)
+ {
+ // (pat) 8-14: Taking out the threading problem fix temporarily for David to use in the field.
+ ScopedLock lock(mLock);
+ mQ.put(val);
+ mWriteSignal.signal();
+ }
+
+ /** Wait until the queue falls below a low water mark. */
+ // (pat) This function suffers from the same problem as documented
+ // at InterthreadQueue.write(), but I am not fixing it because I cannot test it.
+ // The caller of this function will eventually get to run, just not immediately
+ // after the mReadSignal condition is fulfilled.
+ void wait(size_t sz=0)
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>sz) mReadSignal.wait(mLock);
+ }
+
+};
+
+
+
+
+
+/** Thread-safe map of pointers to class D, keyed by class K. */
+template <class K, class D > class InterthreadMap {
+
+protected:
+
+ typedef std::map<K,D*> Map;
+ Map mMap;
+ mutable Mutex mLock;
+ Signal mWriteSignal;
+
+public:
+
+ void clear()
+ {
+ // Delete everything in the map.
+ ScopedLock lock(mLock);
+ typename Map::iterator iter = mMap.begin();
+ while (iter != mMap.end()) {
+ delete iter->second;
+ ++iter;
+ }
+ mMap.clear();
+ }
+
+ ~InterthreadMap() { clear(); }
+
+ /**
+ Non-blocking write.
+ @param key The index to write to.
+ @param wData Pointer to data, not to be deleted until removed from the map.
+ */
+ void write(const K &key, D * wData)
+ {
+ ScopedLock lock(mLock);
+ typename Map::iterator iter = mMap.find(key);
+ if (iter!=mMap.end()) {
+ delete iter->second;
+ iter->second = wData;
+ } else {
+ mMap[key] = wData;
+ }
+ mWriteSignal.broadcast();
+ }
+
+ /**
+ Non-blocking read with element removal.
+ @param key Key to read from.
+ @return Pointer at key or NULL if key not found, to be deleted by caller.
+ */
+ D* getNoBlock(const K& key)
+ {
+ ScopedLock lock(mLock);
+ typename Map::iterator iter = mMap.find(key);
+ if (iter==mMap.end()) return NULL;
+ D* retVal = iter->second;
+ mMap.erase(iter);
+ return retVal;
+ }
+
+ /**
+ Blocking read with a timeout and element removal.
+ @param key The key to read from.
+ @param timeout The blocking timeout in ms.
+ @return Pointer at key or NULL on timeout, to be deleted by caller.
+ */
+ D* get(const K &key, unsigned timeout)
+ {
+ if (timeout==0) return getNoBlock(key);
+ Timeval waitTime(timeout);
+ ScopedLock lock(mLock);
+ typename Map::iterator iter = mMap.find(key);
+ while ((iter==mMap.end()) && (!waitTime.passed())) {
+ mWriteSignal.wait(mLock,waitTime.remaining());
+ iter = mMap.find(key);
+ }
+ if (iter==mMap.end()) return NULL;
+ D* retVal = iter->second;
+ mMap.erase(iter);
+ return retVal;
+ }
+
+ /**
+ Blocking read with and element removal.
+ @param key The key to read from.
+ @return Pointer at key, to be deleted by caller.
+ */
+ D* get(const K &key)
+ {
+ ScopedLock lock(mLock);
+ typename Map::iterator iter = mMap.find(key);
+ while (iter==mMap.end()) {
+ mWriteSignal.wait(mLock);
+ iter = mMap.find(key);
+ }
+ D* retVal = iter->second;
+ mMap.erase(iter);
+ return retVal;
+ }
+
+
+ /**
+ Remove an entry and delete it.
+ @param key The key of the entry to delete.
+ @return True if it was actually found and deleted.
+ */
+ bool remove(const K &key )
+ {
+ D* val = getNoBlock(key);
+ if (!val) return false;
+ delete val;
+ return true;
+ }
+
+
+ /**
+ Non-blocking read.
+ @param key Key to read from.
+ @return Pointer at key or NULL if key not found.
+ */
+ D* readNoBlock(const K& key) const
+ {
+ D* retVal=NULL;
+ ScopedLock lock(mLock);
+ typename Map::const_iterator iter = mMap.find(key);
+ if (iter!=mMap.end()) retVal = iter->second;
+ return retVal;
+ }
+
+ /**
+ Blocking read with a timeout.
+ @param key The key to read from.
+ @param timeout The blocking timeout in ms.
+ @return Pointer at key or NULL on timeout.
+ */
+ D* read(const K &key, unsigned timeout) const
+ {
+ if (timeout==0) return readNoBlock(key);
+ ScopedLock lock(mLock);
+ Timeval waitTime(timeout);
+ typename Map::const_iterator iter = mMap.find(key);
+ while ((iter==mMap.end()) && (!waitTime.passed())) {
+ mWriteSignal.wait(mLock,waitTime.remaining());
+ iter = mMap.find(key);
+ }
+ if (iter==mMap.end()) return NULL;
+ D* retVal = iter->second;
+ return retVal;
+ }
+
+ /**
+ Blocking read.
+ @param key The key to read from.
+ @return Pointer at key.
+ */
+ D* read(const K &key) const
+ {
+ ScopedLock lock(mLock);
+ typename Map::const_iterator iter = mMap.find(key);
+ while (iter==mMap.end()) {
+ mWriteSignal.wait(mLock);
+ iter = mMap.find(key);
+ }
+ D* retVal = iter->second;
+ return retVal;
+ }
+
+};
+
+
+
+
+
+
+
+/** This class is used to provide pointer-based comparison in priority_queues. */
+template <class T> class PointerCompare {
+
+ public:
+
+ /** Compare the objects pointed to, not the pointers themselves. */
+ bool operator()(const T *v1, const T *v2)
+ { return (*v1)>(*v2); }
+
+};
+
+
+
+/**
+ Priority queue for interthread operations.
+ Passes pointers to objects.
+*/
+template <class T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > class InterthreadPriorityQueue {
+
+ protected:
+
+ std::priority_queue<T*,C,Cmp> mQ;
+ mutable Mutex mLock;
+ mutable Signal mWriteSignal;
+
+ public:
+
+
+ /** Clear the FIFO. */
+ void clear()
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>0) {
+ T* ptr = mQ.top();
+ mQ.pop();
+ delete ptr;
+ }
+ }
+
+
+ ~InterthreadPriorityQueue()
+ {
+ clear();
+ }
+
+ size_t size() const
+ {
+ ScopedLock lock(mLock);
+ return mQ.size();
+ }
+
+
+ /** Non-blocking read. */
+ T* readNoBlock()
+ {
+ ScopedLock lock(mLock);
+ T* retVal = NULL;
+ if (mQ.size()!=0) {
+ retVal = mQ.top();
+ mQ.pop();
+ }
+ return retVal;
+ }
+
+ /** Blocking read. */
+ T* read()
+ {
+ ScopedLock lock(mLock);
+ T* retVal;
+ while (mQ.size()==0) mWriteSignal.wait(mLock);
+ retVal = mQ.top();
+ mQ.pop();
+ return retVal;
+ }
+
+ /** Non-blocking write. */
+ void write(T* val)
+ {
+ // (pat) 8-14: Taking out the threading problem fix temporarily for David to use in the field.
+ ScopedLock lock(mLock);
+ mQ.push(val);
+ mWriteSignal.signal();
+ }
+
+};
+
+
+
+
+
+class Semaphore {
+
+ private:
+
+ bool mFlag;
+ Signal mSignal;
+ mutable Mutex mLock;
+
+ public:
+
+ Semaphore()
+ :mFlag(false)
+ { }
+
+ void post()
+ {
+ ScopedLock lock(mLock);
+ mFlag=true;
+ mSignal.signal();
+ }
+
+ void get()
+ {
+ ScopedLock lock(mLock);
+ while (!mFlag) mSignal.wait(mLock);
+ mFlag=false;
+ }
+
+ bool semtry()
+ {
+ ScopedLock lock(mLock);
+ bool retVal = mFlag;
+ mFlag = false;
+ return retVal;
+ }
+
+};
+
+
+
+
+
+//@}
+
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/CommonLibs/LinkedLists.cpp b/CommonLibs/LinkedLists.cpp
new file mode 100644
index 0000000..35a8541
--- /dev/null
+++ b/CommonLibs/LinkedLists.cpp
@@ -0,0 +1,83 @@
+/*
+* 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 "LinkedLists.h"
+
+
+void PointerFIFO::push_front(void* val) // by pat
+{
+ // Pat added this routine for completeness, but never used or tested.
+ // The first person to use this routine should remove this assert.
+ ListNode *node = allocate();
+ node->data(val);
+ node->next(mHead);
+ mHead = node;
+ if (!mTail) mTail=node;
+ mSize++;
+}
+
+void PointerFIFO::put(void* val)
+{
+ ListNode *node = allocate();
+ node->data(val);
+ node->next(NULL);
+ if (mTail!=NULL) mTail->next(node);
+ mTail=node;
+ if (mHead==NULL) mHead=node;
+ mSize++;
+}
+
+/** Take an item from the FIFO. */
+void* PointerFIFO::get()
+{
+ // empty list?
+ if (mHead==NULL) return NULL;
+ // normal case
+ ListNode* next = mHead->next();
+ void* retVal = mHead->data();
+ release(mHead);
+ mHead = next;
+ if (next==NULL) mTail=NULL;
+ mSize--;
+ return retVal;
+}
+
+
+ListNode *PointerFIFO::allocate()
+{
+ if (mFreeList==NULL) return new ListNode;
+ ListNode* retVal = mFreeList;
+ mFreeList = mFreeList->next();
+ return retVal;
+}
+
+void PointerFIFO::release(ListNode* wNode)
+{
+ wNode->next(mFreeList);
+ mFreeList = wNode;
+}
diff --git a/CommonLibs/LinkedLists.h b/CommonLibs/LinkedLists.h
new file mode 100644
index 0000000..31fb9c5
--- /dev/null
+++ b/CommonLibs/LinkedLists.h
@@ -0,0 +1,175 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
+*
+* 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 LINKEDLISTS_H
+#define LINKEDLISTS_H
+
+#include <stdlib.h>
+#include <assert.h>
+
+
+
+/** This node class is used to build singly-linked lists. */
+class ListNode {
+
+ private:
+
+ ListNode* mNext;
+ void* mData;
+
+ public:
+
+ ListNode* next() { return mNext; }
+ void next(ListNode* wNext) { mNext=wNext; }
+
+ void* data() { return mData; }
+ void data(void* wData) { mData=wData; }
+};
+
+
+
+
+/** A fast FIFO for pointer-based storage. */
+class PointerFIFO {
+
+ protected:
+
+ ListNode* mHead; ///< points to next item out
+ ListNode* mTail; ///< points to last item in
+ ListNode* mFreeList; ///< pool of previously-allocated nodes
+ unsigned mSize; ///< number of items in the FIFO
+
+ public:
+
+ PointerFIFO()
+ :mHead(NULL),mTail(NULL),mFreeList(NULL),
+ mSize(0)
+ {}
+
+ unsigned size() const { return mSize; }
+ unsigned totalSize() const { return 0; } // Not used in this version.
+
+ /** Put an item into the FIFO at the back of the queue. */
+ void put(void* val);
+ /** Push an item on the front of the FIFO. */
+ void push_front(void*val); // pat added.
+
+ /**
+ Take an item from the FIFO.
+ Returns NULL for empty list.
+ */
+ void* get();
+
+ /** Peek at front item without removal. */
+ void *front() { return mHead ? mHead->data() : 0; } // pat added
+
+
+ private:
+
+ /** Allocate a new node to extend the FIFO. */
+ ListNode *allocate();
+
+ /** Release a node to the free pool after removal from the FIFO. */
+ void release(ListNode* wNode);
+
+};
+
+// This is the default type for SingleLinkList Node element;
+// You can derive your class directly from this, but then you must add type casts
+// all over the place.
+class SingleLinkListNode
+{ public:
+ SingleLinkListNode *mNext;
+ SingleLinkListNode *next() {return mNext;}
+ void setNext(SingleLinkListNode *item) {mNext=item;}
+ SingleLinkListNode() : mNext(0) {}
+ virtual unsigned size() { return 0; }
+};
+
+// A single-linked lists of elements with internal pointers.
+// The methods must match those from SingleLinkListNode.
+// This class also assumes the Node has a size() method, and accumulates
+// the total size of elements in the list in totalSize().
+template<class Node=SingleLinkListNode>
+class SingleLinkList
+{
+ Node *mHead, *mTail;
+ unsigned mSize; // Number of elements in list.
+ unsigned mTotalSize; // Total of size() method of elements in list.
+
+ public:
+ SingleLinkList() : mHead(0), mTail(0), mSize(0), mTotalSize(0) {}
+ unsigned size() const { return mSize; }
+ unsigned totalSize() const { return mTotalSize; }
+
+ Node *pop_back() { assert(0); } // Not efficient with this type of list.
+
+ Node *pop_front()
+ {
+ if (!mHead) return NULL;
+ Node *result = mHead;
+ mHead = mHead->next();
+ if (mTail == result) { mTail = NULL; assert(mHead == NULL); }
+ result->setNext(NULL); // be neat
+ mSize--;
+ mTotalSize -= result->size();
+ return result;
+ }
+
+ void push_front(Node *item)
+ {
+ item->setNext(mHead);
+ mHead = item;
+ if (!mTail) { mTail = item; }
+ mSize++;
+ mTotalSize += item->size();
+ }
+
+ void push_back(Node *item)
+ {
+ item->setNext(NULL);
+ if (mTail) { mTail->setNext(item); }
+ mTail = item;
+ if (!mHead) mHead = item;
+ mSize++;
+ mTotalSize += item->size();
+ }
+ Node *front() { return mHead; }
+ Node *back() { return mTail; }
+
+ // Interface to InterthreadQueue so it can used SingleLinkList as the Fifo.
+ void put(void *val) { push_back((Node*)val); }
+ void *get() { return pop_front(); }
+};
+
+
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Logger.cpp b/CommonLibs/Logger.cpp
new file mode 100644
index 0000000..171c635
--- /dev/null
+++ b/CommonLibs/Logger.cpp
@@ -0,0 +1,64 @@
+/*
+* Copyright (C) 2018 sysmocom - s.f.m.c. GmbH
+*
+*
+* 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.h>
+#include <cstdio>
+#include <fstream>
+#include <string>
+#include <stdarg.h>
+#include <sys/time.h> // For gettimeofday
+
+#include "Logger.h"
+#include "Threads.h" // pat added
+
+using namespace std;
+
+Mutex gLogToLock;
+
+std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
+{
+ return os << ss.str();
+}
+
+Log::~Log()
+{
+ int old_state;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+ int mlen = mStream.str().size();
+ int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
+ const char *fmt = neednl ? "%s\n" : "%s";
+ ScopedLock lock(gLogToLock);
+ // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
+ // so just use std::cout.
+ LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
+ pthread_setcancelstate(old_state, NULL);
+}
+
+ostringstream& Log::get()
+{
+ return mStream;
+}
+
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Logger.h b/CommonLibs/Logger.h
new file mode 100644
index 0000000..e18ecfb
--- /dev/null
+++ b/CommonLibs/Logger.h
@@ -0,0 +1,94 @@
+/*
+* 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/>.
+
+*/
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <sstream>
+#include <string>
+
+extern "C" {
+#include <osmocom/core/logging.h>
+#include "debug.h"
+}
+
+/* Translation for old log statements */
+#ifndef LOGL_ALERT
+#define LOGL_ALERT LOGL_FATAL
+#endif
+#ifndef LOGL_ERR
+#define LOGL_ERR LOGL_ERROR
+#endif
+#ifndef LOGL_WARNING
+#define LOGL_WARNING LOGL_NOTICE
+#endif
+
+#define LOG(level) \
+ Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
+
+#define LOGC(category, level) \
+ Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
+
+#define LOGLV(category, level) \
+ Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
+
+/**
+ A C++ stream-based thread-safe logger.
+ This object is NOT the global logger;
+ every log record is an object of this class.
+*/
+class Log {
+
+ public:
+
+ protected:
+
+ std::ostringstream mStream; ///< This is where we buffer up the log entry.
+ int mCategory; ///< Priority of current report.
+ int mPriority; ///< Category of current report.
+ const char *filename; ///< Source File Name of current report.
+ int line; ///< Line number in source file of current report.
+
+ public:
+
+ Log(int wCategory, int wPriority, const char* filename, int line)
+ : mCategory(wCategory), mPriority(wPriority),
+ filename(filename), line(line)
+ { }
+
+ // Most of the work is in the destructor.
+ /** The destructor actually generates the log entry. */
+ ~Log();
+
+ std::ostringstream& get();
+};
+
+std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
+
+#endif
+
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Makefile.am b/CommonLibs/Makefile.am
new file mode 100644
index 0000000..9fabcf1
--- /dev/null
+++ b/CommonLibs/Makefile.am
@@ -0,0 +1,54 @@
+#
+# Copyright 2008, 2009 Free Software Foundation, Inc.
+# Copyright 2011, 2012 Range Networks, Inc.
+#
+# This software is distributed under the terms of the GNU Public License.
+# See the COPYING file in the main directory for details.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
+AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
+AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
+
+noinst_LTLIBRARIES = libcommon.la
+
+libcommon_la_SOURCES = \
+ BitVector.cpp \
+ LinkedLists.cpp \
+ Sockets.cpp \
+ Threads.cpp \
+ Timeval.cpp \
+ Logger.cpp \
+ trx_vty.c \
+ debug.c
+libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
+
+noinst_HEADERS = \
+ BitVector.h \
+ PRBS.h \
+ Interthread.h \
+ LinkedLists.h \
+ Sockets.h \
+ Threads.h \
+ Timeval.h \
+ Vector.h \
+ Logger.h \
+ trx_vty.h \
+ debug.h \
+ osmo_signal.h \
+ config_defs.h
diff --git a/CommonLibs/PRBS.h b/CommonLibs/PRBS.h
new file mode 100644
index 0000000..0b7bbc3
--- /dev/null
+++ b/CommonLibs/PRBS.h
@@ -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
diff --git a/CommonLibs/Sockets.cpp b/CommonLibs/Sockets.cpp
new file mode 100644
index 0000000..ce8e3d5
--- /dev/null
+++ b/CommonLibs/Sockets.cpp
@@ -0,0 +1,287 @@
+/*
+* Copyright 2008, 2010 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 <config.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <cstdio>
+#include <sys/select.h>
+
+#include "Threads.h"
+#include "Sockets.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+
+
+
+
+
+bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort)
+{
+ assert(address);
+ assert(hostAndPort);
+ char *copy = strdup(hostAndPort);
+ char *colon = strchr(copy,':');
+ if (!colon) return false;
+ *colon = '\0';
+ char *host = copy;
+ unsigned port = strtol(colon+1,NULL,10);
+ bool retVal = resolveAddress(address,host,port);
+ free(copy);
+ return retVal;
+}
+
+bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port)
+{
+ assert(address);
+ assert(host);
+ // FIXME -- Need to ignore leading/trailing spaces in hostname.
+ struct hostent *hp;
+ int h_errno_local;
+#ifdef HAVE_GETHOSTBYNAME2_R
+ struct hostent hostData;
+ char tmpBuffer[2048];
+
+ // There are different flavors of gethostbyname_r(), but
+ // latest Linux use the following form:
+ if (gethostbyname2_r(host, AF_INET, &hostData, tmpBuffer, sizeof(tmpBuffer), &hp, &h_errno_local)!=0) {
+ CERR("WARNING -- gethostbyname2_r() failed for " << host << ", " << hstrerror(h_errno_local));
+ return false;
+ }
+#else
+ static Mutex sGethostbynameMutex;
+ // gethostbyname() is NOT thread-safe, so we should use a mutex here.
+ // Ideally it should be a global mutex for all non thread-safe socket
+ // operations and it should protect access to variables such as
+ // global h_errno.
+ sGethostbynameMutex.lock();
+ hp = gethostbyname(host);
+ h_errno_local = h_errno;
+ sGethostbynameMutex.unlock();
+#endif
+ if (hp==NULL) {
+ CERR("WARNING -- gethostbyname() failed for " << host << ", " << hstrerror(h_errno_local));
+ return false;
+ }
+ if (hp->h_addrtype != AF_INET) {
+ CERR("WARNING -- gethostbyname() resolved " << host << " to something other then AF_INET");
+ return false;
+ }
+ address->sin_family = hp->h_addrtype;
+ assert(sizeof(address->sin_addr) == hp->h_length);
+ memcpy(&(address->sin_addr), hp->h_addr_list[0], hp->h_length);
+ address->sin_port = htons(port);
+ return true;
+}
+
+
+
+DatagramSocket::DatagramSocket()
+{
+ memset(mDestination, 0, sizeof(mDestination));
+}
+
+
+
+
+
+void DatagramSocket::nonblocking()
+{
+ fcntl(mSocketFD,F_SETFL,O_NONBLOCK);
+}
+
+void DatagramSocket::blocking()
+{
+ fcntl(mSocketFD,F_SETFL,0);
+}
+
+void DatagramSocket::close()
+{
+ ::close(mSocketFD);
+}
+
+
+DatagramSocket::~DatagramSocket()
+{
+ close();
+}
+
+
+
+
+
+int DatagramSocket::write( const char * message, size_t length )
+{
+ assert(length<=MAX_UDP_LENGTH);
+ int retVal = sendto(mSocketFD, message, length, 0,
+ (struct sockaddr *)mDestination, addressSize());
+ if (retVal == -1 ) perror("DatagramSocket::write() failed");
+ return retVal;
+}
+
+int DatagramSocket::writeBack( const char * message, size_t length )
+{
+ assert(length<=MAX_UDP_LENGTH);
+ int retVal = sendto(mSocketFD, message, length, 0,
+ (struct sockaddr *)mSource, addressSize());
+ if (retVal == -1 ) perror("DatagramSocket::write() failed");
+ return retVal;
+}
+
+
+
+int DatagramSocket::write( const char * message)
+{
+ size_t length=strlen(message)+1;
+ return write(message,length);
+}
+
+int DatagramSocket::writeBack( const char * message)
+{
+ size_t length=strlen(message)+1;
+ return writeBack(message,length);
+}
+
+
+
+int DatagramSocket::send(const struct sockaddr* dest, const char * message, size_t length )
+{
+ assert(length<=MAX_UDP_LENGTH);
+ int retVal = sendto(mSocketFD, message, length, 0, dest, addressSize());
+ if (retVal == -1 ) perror("DatagramSocket::send() failed");
+ return retVal;
+}
+
+int DatagramSocket::send(const struct sockaddr* dest, const char * message)
+{
+ size_t length=strlen(message)+1;
+ return send(dest,message,length);
+}
+
+int DatagramSocket::read(char* buffer, size_t length)
+{
+ socklen_t addr_len = sizeof(mSource);
+ int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
+ (struct sockaddr*) &mSource, &addr_len);
+
+ if ((rd_length==-1) && (errno!=EAGAIN)) {
+ perror("DatagramSocket::read() failed");
+ throw SocketError();
+ }
+ return rd_length;
+}
+
+int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
+{
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(mSocketFD,&fds);
+ struct timeval tv;
+ tv.tv_sec = timeout/1000;
+ tv.tv_usec = (timeout%1000)*1000;
+ int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv);
+ if (sel<0) {
+ perror("DatagramSocket::read() select() failed");
+ throw SocketError();
+ }
+ if (sel==0) return -1;
+ if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
+ return -1;
+}
+
+
+
+
+
+
+UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
+ :DatagramSocket()
+{
+ open(wSrcPort, wSrcIP);
+}
+
+
+UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
+ const char *wDestIP, unsigned short wDestPort)
+ :DatagramSocket()
+{
+ open(wSrcPort, wSrcIP);
+ destination(wDestPort, wDestIP);
+}
+
+
+
+void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
+{
+ resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort );
+}
+
+
+void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
+{
+ // create
+ mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
+ if (mSocketFD<0) {
+ perror("socket() failed");
+ throw SocketError();
+ }
+
+ // pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes.
+ int on = 1;
+ setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+
+ // bind
+ struct sockaddr_in address;
+ size_t length = sizeof(address);
+ bzero(&address,length);
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = inet_addr(wlocalIP);
+ address.sin_port = htons(localPort);
+ if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
+ perror("bind() failed");
+ throw SocketError();
+ }
+}
+
+
+
+unsigned short UDPSocket::port() const
+{
+ struct sockaddr_in name;
+ socklen_t nameSize = sizeof(name);
+ int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize);
+ if (retVal==-1) throw SocketError();
+ return ntohs(name.sin_port);
+}
+
+// vim:ts=4:sw=4
diff --git a/CommonLibs/Sockets.h b/CommonLibs/Sockets.h
new file mode 100644
index 0000000..71b8b22
--- /dev/null
+++ b/CommonLibs/Sockets.h
@@ -0,0 +1,173 @@
+/*
+* Copyright 2008, 2010 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 SOCKETS_H
+#define SOCKETS_H
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <list>
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+
+
+
+
+
+#define MAX_UDP_LENGTH 1500
+
+/** A function to resolve IP host names. */
+bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port);
+
+/** Resolve an address of the form "<host>:<port>". */
+bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort);
+
+/** An exception to throw when a critical socket operation fails. */
+class SocketError {};
+#define SOCKET_ERROR {throw SocketError(); }
+
+/** Abstract class for connectionless sockets. */
+class DatagramSocket {
+
+protected:
+
+ int mSocketFD; ///< underlying file descriptor
+ char mDestination[256]; ///< address to which packets are sent
+ char mSource[256]; ///< return address of most recent received packet
+
+public:
+
+ /** An almost-does-nothing constructor. */
+ DatagramSocket();
+
+ virtual ~DatagramSocket();
+
+ /** Return the address structure size for this socket type. */
+ virtual size_t addressSize() const = 0;
+
+ /**
+ Send a binary packet.
+ @param buffer The data bytes to send to mDestination.
+ @param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
+ @return number of bytes written, or -1 on error.
+ */
+ int write( const char * buffer, size_t length);
+
+ /**
+ Send a C-style string packet.
+ @param buffer The data bytes to send to mDestination.
+ @return number of bytes written, or -1 on error.
+ */
+ int write( const char * buffer);
+
+ /**
+ Send a binary packet.
+ @param buffer The data bytes to send to mSource.
+ @param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
+ @return number of bytes written, or -1 on error.
+ */
+ int writeBack(const char * buffer, size_t length);
+
+ /**
+ Send a C-style string packet.
+ @param buffer The data bytes to send to mSource.
+ @return number of bytes written, or -1 on error.
+ */
+ int writeBack(const char * buffer);
+
+
+ /**
+ Receive a packet.
+ @param buffer A char[MAX_UDP_LENGTH] procured by the caller.
+ @return The number of bytes received or -1 on non-blocking pass.
+ */
+ int read(char* buffer, size_t length);
+
+ /**
+ Receive a packet with a timeout.
+ @param buffer A char[MAX_UDP_LENGTH] procured by the caller.
+ @param maximum wait time in milliseconds
+ @return The number of bytes received or -1 on timeout.
+ */
+ int read(char* buffer, size_t length, unsigned timeout);
+
+
+ /** Send a packet to a given destination, other than the default. */
+ int send(const struct sockaddr *dest, const char * buffer, size_t length);
+
+ /** Send a C-style string to a given destination, other than the default. */
+ int send(const struct sockaddr *dest, const char * buffer);
+
+ /** Make the socket non-blocking. */
+ void nonblocking();
+
+ /** Make the socket blocking (the default). */
+ void blocking();
+
+ /** Close the socket. */
+ void close();
+
+};
+
+
+
+/** UDP/IP User Datagram Socket */
+class UDPSocket : public DatagramSocket {
+
+public:
+
+ /** Open a USP socket with an OS-assigned port and no default destination. */
+ UDPSocket(const char *localIP, unsigned short localPort);
+
+ /** Given a full specification, open the socket and set the dest address. */
+ UDPSocket(const char *localIP, unsigned short localPort,
+ const char *remoteIP, unsigned short remotePort);
+
+ /** Set the destination port. */
+ void destination( unsigned short wDestPort, const char * wDestIP );
+
+ /** Return the actual port number in use. */
+ unsigned short port() const;
+
+ /** Open and bind the UDP socket to a local port. */
+ void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
+
+ /** Give the return address of the most recently received packet. */
+ const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
+
+ size_t addressSize() const { return sizeof(struct sockaddr_in); }
+
+};
+
+#endif
+
+
+
+// vim:ts=4:sw=4
diff --git a/CommonLibs/Threads.cpp b/CommonLibs/Threads.cpp
new file mode 100644
index 0000000..2988e12
--- /dev/null
+++ b/CommonLibs/Threads.cpp
@@ -0,0 +1,140 @@
+/*
+* 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 <string.h>
+#include <sys/types.h>
+
+#include "Threads.h"
+#include "Timeval.h"
+#include "Logger.h"
+
+#ifndef gettid
+#include <sys/syscall.h>
+#define gettid() syscall(SYS_gettid)
+#endif
+
+
+using namespace std;
+
+
+
+
+Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
+
+void lockCout()
+{
+ gStreamLock.lock();
+ Timeval entryTime;
+ cout << entryTime << " " << pthread_self() << ": ";
+}
+
+
+void unlockCout()
+{
+ cout << dec << endl << flush;
+ gStreamLock.unlock();
+}
+
+
+void lockCerr()
+{
+ gStreamLock.lock();
+ Timeval entryTime;
+ cerr << entryTime << " " << pthread_self() << ": ";
+}
+
+void unlockCerr()
+{
+ cerr << dec << endl << flush;
+ gStreamLock.unlock();
+}
+
+
+
+
+
+
+
+Mutex::Mutex()
+{
+ bool res;
+ res = pthread_mutexattr_init(&mAttribs);
+ assert(!res);
+ res = pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE);
+ assert(!res);
+ res = pthread_mutex_init(&mMutex,&mAttribs);
+ assert(!res);
+}
+
+
+Mutex::~Mutex()
+{
+ pthread_mutex_destroy(&mMutex);
+ bool res = pthread_mutexattr_destroy(&mAttribs);
+ assert(!res);
+}
+
+
+
+
+/** Block for the signal up to the cancellation timeout. */
+void Signal::wait(Mutex& wMutex, unsigned timeout) const
+{
+ Timeval then(timeout);
+ struct timespec waitTime = then.timespec();
+ pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
+}
+
+void set_selfthread_name(const char *name)
+{
+ pthread_t selfid = pthread_self();
+ pid_t tid = gettid();
+ if (pthread_setname_np(selfid, name) == 0) {
+ LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
+ } else {
+ char buf[256];
+ int err = errno;
+ char* err_str = strerror_r(err, buf, sizeof(buf));
+ LOG(NOTICE) << "Thread "<< selfid << " (task " << tid << ") set name \"" << name << "\" failed: (" << err << ") " << err_str;
+ }
+}
+
+void Thread::start(void *(*task)(void*), void *arg)
+{
+ assert(mThread==((pthread_t)0));
+ bool res;
+ // (pat) Moved initialization to constructor to avoid crash in destructor.
+ //res = pthread_attr_init(&mAttrib);
+ //assert(!res);
+ res = pthread_attr_setstacksize(&mAttrib, mStackSize);
+ assert(!res);
+ res = pthread_create(&mThread, &mAttrib, task, arg);
+ assert(!res);
+}
+
+
+
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Threads.h b/CommonLibs/Threads.h
new file mode 100644
index 0000000..857c5d9
--- /dev/null
+++ b/CommonLibs/Threads.h
@@ -0,0 +1,192 @@
+/*
+* Copyright 2008, 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/>.
+
+*/
+
+
+#ifndef THREADS_H
+#define THREADS_H
+
+#include <pthread.h>
+#include <iostream>
+#include <assert.h>
+#include <unistd.h>
+
+class Mutex;
+
+
+/**@name Multithreaded access for standard streams. */
+//@{
+
+/**@name Functions for gStreamLock. */
+//@{
+extern Mutex gStreamLock; ///< global lock for cout and cerr
+void lockCerr(); ///< call prior to writing cerr
+void unlockCerr(); ///< call after writing cerr
+void lockCout(); ///< call prior to writing cout
+void unlockCout(); ///< call after writing cout
+//@}
+
+/**@name Macros for standard messages. */
+//@{
+#define COUT(text) { lockCout(); std::cout << text; unlockCout(); }
+#define CERR(text) { lockCerr(); std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; unlockCerr(); }
+#ifdef NDEBUG
+#define DCOUT(text) {}
+#define OBJDCOUT(text) {}
+#else
+#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
+#define OBJDCOUT(text) { DCOUT(this << " " << text); }
+#endif
+//@}
+//@}
+
+
+
+/**@defgroup C++ wrappers for pthread mechanisms. */
+//@{
+
+/** A class for recursive mutexes based on pthread_mutex. */
+class Mutex {
+
+ private:
+
+ pthread_mutex_t mMutex;
+ pthread_mutexattr_t mAttribs;
+
+ public:
+
+ Mutex();
+
+ ~Mutex();
+
+ void lock() { pthread_mutex_lock(&mMutex); }
+
+ bool trylock() { return pthread_mutex_trylock(&mMutex)==0; }
+
+ void unlock() { pthread_mutex_unlock(&mMutex); }
+
+ friend class Signal;
+
+};
+
+
+class ScopedLock {
+
+ private:
+ Mutex& mMutex;
+
+ public:
+ ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); }
+ ~ScopedLock() { mMutex.unlock(); }
+
+};
+
+
+
+
+/** A C++ interthread signal based on pthread condition variables. */
+class Signal {
+
+ private:
+
+ mutable pthread_cond_t mSignal;
+
+ public:
+
+ Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); }
+
+ ~Signal() { pthread_cond_destroy(&mSignal); }
+
+ /**
+ Block for the signal up to the cancellation timeout.
+ Under Linux, spurious returns are possible.
+ */
+ void wait(Mutex& wMutex, unsigned timeout) const;
+
+ /**
+ Block for the signal.
+ Under Linux, spurious returns are possible.
+ */
+ void wait(Mutex& wMutex) const
+ { pthread_cond_wait(&mSignal,&wMutex.mMutex); }
+
+ void signal() { pthread_cond_signal(&mSignal); }
+
+ void broadcast() { pthread_cond_broadcast(&mSignal); }
+
+};
+
+
+
+#define START_THREAD(thread,function,argument) \
+ thread.start((void *(*)(void*))function, (void*)argument);
+
+void set_selfthread_name(const char *name);
+
+/** A C++ wrapper for pthread threads. */
+class Thread {
+
+ private:
+
+ pthread_t mThread;
+ pthread_attr_t mAttrib;
+ // FIXME -- Can this be reduced now?
+ size_t mStackSize;
+
+
+ public:
+
+ /** Create a thread in a non-running state. */
+ Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
+ pthread_attr_init(&mAttrib); // (pat) moved this here.
+ mStackSize=wStackSize;
+ }
+
+ /**
+ Destroy the Thread.
+ It should be stopped and joined.
+ */
+ // (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
+ ~Thread() { pthread_attr_destroy(&mAttrib); }
+
+
+ /** Start the thread on a task. */
+ void start(void *(*task)(void*), void *arg);
+
+ /** Join a thread that will stop on its own. */
+ void join() {
+ if (mThread) {
+ int s = pthread_join(mThread, NULL);
+ assert(!s);
+ }
+ }
+
+ /** Send cancelation to thread */
+ void cancel() { pthread_cancel(mThread); }
+};
+
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Timeval.cpp b/CommonLibs/Timeval.cpp
new file mode 100644
index 0000000..50ce05d
--- /dev/null
+++ b/CommonLibs/Timeval.cpp
@@ -0,0 +1,98 @@
+/*
+* 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"
+
+using namespace std;
+
+void Timeval::future(unsigned offset)
+{
+ now();
+ unsigned sec = offset/1000;
+ unsigned msec = offset%1000;
+ mTimeval.tv_usec += msec*1000;
+ mTimeval.tv_sec += sec;
+ if (mTimeval.tv_usec>1000000) {
+ mTimeval.tv_usec -= 1000000;
+ mTimeval.tv_sec += 1;
+ }
+}
+
+
+struct timespec Timeval::timespec() const
+{
+ struct timespec retVal;
+ retVal.tv_sec = mTimeval.tv_sec;
+ retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
+ return retVal;
+}
+
+
+bool Timeval::passed() const
+{
+ Timeval nowTime;
+ if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
+ if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
+ if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
+ return false;
+}
+
+double Timeval::seconds() const
+{
+ return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
+}
+
+
+
+long Timeval::delta(const Timeval& other) const
+{
+ // 2^31 milliseconds is just over 4 years.
+ int32_t deltaS = other.sec() - sec();
+ int32_t deltaUs = other.usec() - usec();
+ return 1000*deltaS + deltaUs/1000;
+}
+
+
+
+
+ostream& operator<<(ostream& os, const Timeval& tv)
+{
+ os.setf( ios::fixed, ios::floatfield );
+ os << tv.seconds();
+ return os;
+}
+
+
+ostream& operator<<(ostream& os, const struct timespec& ts)
+{
+ os << ts.tv_sec << "," << ts.tv_nsec;
+ return os;
+}
+
+
+
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Timeval.h b/CommonLibs/Timeval.h
new file mode 100644
index 0000000..c497864
--- /dev/null
+++ b/CommonLibs/Timeval.h
@@ -0,0 +1,105 @@
+/*
+* 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 TIMEVAL_H
+#define TIMEVAL_H
+
+#include <stdint.h>
+#include "sys/time.h"
+#include <iostream>
+#include <unistd.h>
+
+
+
+/** A wrapper on usleep to sleep for milliseconds. */
+inline void msleep(long v) { usleep(v*1000); }
+
+
+/** A C++ wrapper for struct timeval. */
+class Timeval {
+
+ private:
+
+ struct timeval mTimeval;
+
+ public:
+
+ /** Set the value to gettimeofday. */
+ void now() { gettimeofday(&mTimeval,NULL); }
+
+ /** Set the value to gettimeofday plus an offset. */
+ void future(unsigned ms);
+
+ //@{
+ Timeval(unsigned sec, unsigned usec)
+ {
+ mTimeval.tv_sec = sec;
+ mTimeval.tv_usec = usec;
+ }
+
+ Timeval(const struct timeval& wTimeval)
+ :mTimeval(wTimeval)
+ {}
+
+ /**
+ Create a Timeval offset into the future.
+ @param offset milliseconds
+ */
+ Timeval(unsigned offset=0) { future(offset); }
+ //@}
+
+ /** Convert to a struct timespec. */
+ struct timespec timespec() const;
+
+ /** Return total seconds. */
+ double seconds() const;
+
+ uint32_t sec() const { return mTimeval.tv_sec; }
+ uint32_t usec() const { return mTimeval.tv_usec; }
+
+ /** Return differnce from other (other-self), in ms. */
+ long delta(const Timeval& other) const;
+
+ /** Elapsed time in ms. */
+ long elapsed() const { return delta(Timeval()); }
+
+ /** Remaining time in ms. */
+ long remaining() const { return -elapsed(); }
+
+ /** Return true if the time has passed, as per gettimeofday. */
+ bool passed() const;
+
+ /** Add a given number of minutes to the time. */
+ void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; }
+
+};
+
+std::ostream& operator<<(std::ostream& os, const Timeval&);
+
+std::ostream& operator<<(std::ostream& os, const struct timespec&);
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Vector.h b/CommonLibs/Vector.h
new file mode 100644
index 0000000..9119683
--- /dev/null
+++ b/CommonLibs/Vector.h
@@ -0,0 +1,308 @@
+/**@file Simplified Vector template with aliases. */
+/*
+* 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 VECTOR_H
+#define VECTOR_H
+
+#include <string.h>
+#include <iostream>
+#include <assert.h>
+// We cant use Logger.h in this file...
+extern int gVectorDebug;
+#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
+
+
+
+/**
+ A simplified Vector template with aliases.
+ Unlike std::vector, this class does not support dynamic resizing.
+ Unlike std::vector, this class does support "aliases" and subvectors.
+*/
+template <class T> class Vector {
+
+ // TODO -- Replace memcpy calls with for-loops.
+
+ public:
+
+ /**@name Iterator types. */
+ //@{
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ //@}
+
+ protected:
+
+ T* mData; ///< allocated data block, if any
+ T* mStart; ///< start of useful data
+ T* mEnd; ///< end of useful data + 1
+
+ public:
+
+ /****
+ char *inspect() {
+ static char buf[100];
+ sprintf(buf," mData=%p mStart=%p mEnd=%p ",mData,mStart,mEnd);
+ return buf;
+ }
+ ***/
+
+ /** Return the size of the Vector. */
+ size_t size() const
+ {
+ assert(mStart>=mData);
+ assert(mEnd>=mStart);
+ return mEnd - mStart;
+ }
+
+ /** Return size in bytes. */
+ size_t bytes() const { return size()*sizeof(T); }
+
+ /** Change the size of the Vector, discarding content. */
+ void resize(size_t newSize)
+ {
+ if (mData!=NULL) delete[] mData;
+ if (newSize==0) mData=NULL;
+ else mData = new T[newSize];
+ mStart = mData;
+ mEnd = mStart + newSize;
+ }
+
+ /** Reduce addressable size of the Vector, keeping content. */
+ void shrink(size_t newSize)
+ {
+ assert(newSize <= mEnd - mStart);
+ mEnd = mStart + newSize;
+ }
+
+ /** Release memory and clear pointers. */
+ void clear() { resize(0); }
+
+
+ /** Copy data from another vector. */
+ void clone(const Vector<T>& other)
+ {
+ resize(other.size());
+ other.copyTo(*this);
+ }
+
+
+
+
+ //@{
+
+ /** Build an empty Vector of a given size. */
+ Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
+
+ /** Build a Vector by moving another. */
+ Vector(Vector<T>&& other)
+ :mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
+ { other.mData=NULL; }
+
+ /** Build a Vector by copying another. */
+ Vector(const Vector<T>& other):mData(NULL) { clone(other); }
+
+ /** Build a Vector with explicit values. */
+ Vector(T* wData, T* wStart, T* wEnd)
+ :mData(wData),mStart(wStart),mEnd(wEnd)
+ { }
+
+ /** Build a vector from an existing block, NOT to be deleted upon destruction. */
+ Vector(T* wStart, size_t span)
+ :mData(NULL),mStart(wStart),mEnd(wStart+span)
+ { }
+
+ /** Build a Vector by concatenation. */
+ Vector(const Vector<T>& other1, const Vector<T>& other2)
+ :mData(NULL)
+ {
+ resize(other1.size()+other2.size());
+ memcpy(mStart, other1.mStart, other1.bytes());
+ memcpy(mStart+other1.size(), other2.mStart, other2.bytes());
+ }
+
+ //@}
+
+ /** Destroy a Vector, deleting held memory. */
+ ~Vector() { clear(); }
+
+
+
+
+ //@{
+
+ /** Assign from another Vector, shifting ownership. */
+ void operator=(Vector<T>& other)
+ {
+ clear();
+ mData=other.mData;
+ mStart=other.mStart;
+ mEnd=other.mEnd;
+ other.mData=NULL;
+ }
+
+ /** Assign from another Vector, copying. */
+ void operator=(const Vector<T>& other) { clone(other); }
+
+ //@}
+
+
+ //@{
+
+ /** Return an alias to a segment of this Vector. */
+ Vector<T> segment(size_t start, size_t span)
+ {
+ T* wStart = mStart + start;
+ T* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return Vector<T>(NULL,wStart,wEnd);
+ }
+
+ /** Return an alias to a segment of this Vector. */
+ const Vector<T> segment(size_t start, size_t span) const
+ {
+ T* wStart = mStart + start;
+ T* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return Vector<T>(NULL,wStart,wEnd);
+ }
+
+ Vector<T> head(size_t span) { return segment(0,span); }
+ const Vector<T> head(size_t span) const { return segment(0,span); }
+ Vector<T> tail(size_t start) { return segment(start,size()-start); }
+ const Vector<T> tail(size_t start) const { return segment(start,size()-start); }
+
+ /**
+ Copy part of this Vector to a segment of another Vector.
+ @param other The other vector.
+ @param start The start point in the other vector.
+ @param span The number of elements to copy.
+ */
+ void copyToSegment(Vector<T>& other, size_t start, size_t span) const
+ {
+ unsigned int i;
+ T* dst = other.mStart + start;
+ T* src = mStart;
+ assert(dst+span<=other.mEnd);
+ assert(mStart+span<=mEnd);
+ for (i = 0; i < span; i++, src++, dst++)
+ *dst = *src;
+ /*TODO if not non-trivially copyable type class, optimize:
+ memcpy(dst,mStart,span*sizeof(T)); */
+ }
+
+ /** Copy all of this Vector to a segment of another Vector. */
+ void copyToSegment(Vector<T>& other, size_t start=0) const { copyToSegment(other,start,size()); }
+
+ void copyTo(Vector<T>& other) const { copyToSegment(other,0,size()); }
+
+ /**
+ Copy a segment of this vector into another.
+ @param other The other vector (to copt into starting at 0.)
+ @param start The start point in this vector.
+ @param span The number of elements to copy.
+ */
+ void segmentCopyTo(Vector<T>& other, size_t start, size_t span) const
+ {
+ const T* base = mStart + start;
+ assert(base+span<=mEnd);
+ assert(other.mStart+span<=other.mEnd);
+ memcpy(other.mStart,base,span*sizeof(T));
+ }
+
+ /**
+ Move (copy) a segment of this vector into a different position in the vector
+ @param from Start point from which to copy.
+ @param to Start point to which to copy.
+ @param span The number of elements to copy.
+ */
+ void segmentMove(size_t from, size_t to, size_t span)
+ {
+ const T* baseFrom = mStart + from;
+ T* baseTo = mStart + to;
+ assert(baseFrom+span<=mEnd);
+ assert(baseTo+span<=mEnd);
+ memmove(baseTo,baseFrom,span*sizeof(T));
+ }
+
+ void fill(const T& val)
+ {
+ T* dp=mStart;
+ while (dp<mEnd) *dp++=val;
+ }
+
+ void fill(const T& val, unsigned start, unsigned length)
+ {
+ T* dp=mStart+start;
+ T* end=dp+length;
+ assert(end<=mEnd);
+ while (dp<end) *dp++=val;
+ }
+
+
+ //@}
+
+
+ //@{
+
+ T& operator[](size_t index)
+ {
+ assert(mStart+index<mEnd);
+ return mStart[index];
+ }
+
+ const T& operator[](size_t index) const
+ {
+ assert(mStart+index<mEnd);
+ return mStart[index];
+ }
+
+ const T* begin() const { return mStart; }
+ T* begin() { return mStart; }
+ const T* end() const { return mEnd; }
+ T* end() { return mEnd; }
+ bool isOwner() { return !!mData; } // Do we own any memory ourselves?
+ //@}
+
+
+};
+
+
+
+
+/** Basic print operator for Vector objects. */
+template <class T>
+std::ostream& operator<<(std::ostream& os, const Vector<T>& v)
+{
+ for (unsigned i=0; i<v.size(); i++) os << v[i] << " ";
+ return os;
+}
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/CommonLibs/config_defs.h b/CommonLibs/config_defs.h
new file mode 100644
index 0000000..8626166
--- /dev/null
+++ b/CommonLibs/config_defs.h
@@ -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,
+};
diff --git a/CommonLibs/debug.c b/CommonLibs/debug.c
new file mode 100644
index 0000000..c6de21a
--- /dev/null
+++ b/CommonLibs/debug.c
@@ -0,0 +1,30 @@
+#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,
+ },
+ [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),
+};
diff --git a/CommonLibs/debug.h b/CommonLibs/debug.h
new file mode 100644
index 0000000..f8f6239
--- /dev/null
+++ b/CommonLibs/debug.h
@@ -0,0 +1,10 @@
+#pragma once
+
+extern const struct log_info log_info;
+
+/* Debug Areas of the code */
+enum {
+ DMAIN,
+ DDEV,
+ DLMS,
+};
diff --git a/CommonLibs/osmo_signal.h b/CommonLibs/osmo_signal.h
new file mode 100644
index 0000000..00b8097
--- /dev/null
+++ b/CommonLibs/osmo_signal.h
@@ -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 */
+};
diff --git a/CommonLibs/trx_vty.c b/CommonLibs/trx_vty.c
new file mode 100644
index 0000000..45b58eb
--- /dev/null
+++ b/CommonLibs/trx_vty.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/rate_ctr.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/misc.h>
+
+#include "trx_vty.h"
+#include "../config.h"
+
+static struct trx_ctx* g_trx_ctx;
+
+static const struct value_string clock_ref_names[] = {
+ { REF_INTERNAL, "internal" },
+ { REF_EXTERNAL, "external" },
+ { REF_GPS, "gpsdo" },
+ { 0, NULL }
+};
+
+static const struct value_string filler_names[] = {
+ { FILLER_DUMMY, "Dummy bursts" },
+ { FILLER_ZERO, "Disabled" },
+ { FILLER_NORM_RAND, "Normal bursts with random payload" },
+ { FILLER_EDGE_RAND, "EDGE bursts with random payload" },
+ { FILLER_ACCESS_RAND, "Access bursts with random payload" },
+ { 0, NULL }
+};
+
+struct trx_ctx *trx_from_vty(struct vty *v)
+{
+ /* It can't hurt to force callers to continue to pass the vty instance
+ * to this function, in case we'd like to retrieve the global
+ * trx instance from the vty at some point in the future. But
+ * until then, just return the global pointer, which should have been
+ * initialized by trx_vty_init().
+ */
+ OSMO_ASSERT(g_trx_ctx);
+ return g_trx_ctx;
+}
+
+enum trx_vty_node {
+ TRX_NODE = _LAST_OSMOVTY_NODE + 1,
+ CHAN_NODE,
+};
+
+static struct cmd_node trx_node = {
+ TRX_NODE,
+ "%s(config-trx)# ",
+ 1,
+};
+
+static struct cmd_node chan_node = {
+ CHAN_NODE,
+ "%s(config-trx-chan)# ",
+ 1,
+};
+
+DEFUN(cfg_trx, cfg_trx_cmd,
+ "trx",
+ "Configure the TRX\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ if (!trx)
+ return CMD_WARNING;
+
+ vty->node = TRX_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
+ "bind-ip A.B.C.D",
+ "Set the IP address for the local bind\n"
+ "IPv4 Address\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
+ "remote-ip A.B.C.D",
+ "Set the IP address for the remote BTS\n"
+ "IPv4 Address\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_base_port, cfg_base_port_cmd,
+ "base-port <1-65535>",
+ "Set the TRX Base Port\n"
+ "TRX Base Port\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.base_port = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_dev_args, cfg_dev_args_cmd,
+ "dev-args DESC",
+ "Set the device-specific arguments to pass to the device\n"
+ "Device-specific arguments\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
+ "tx-sps (1|4)",
+ "Set the Tx Samples-per-Symbol\n"
+ "Tx Samples-per-Symbol\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.tx_sps = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
+ "rx-sps (1|4)",
+ "Set the Rx Samples-per-Symbol\n"
+ "Rx Samples-per-Symbol\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.rx_sps = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd,
+ "test rtsc <0-7>",
+ "Set the Random Normal Burst test mode with TSC\n"
+ "TSC\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ if (trx->cfg.rach_delay_set) {
+ vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ trx->cfg.rtsc_set = true;
+ trx->cfg.rtsc = atoi(argv[0]);
+ if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
+ trx->cfg.filler = FILLER_NORM_RAND;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd,
+ "test rach-delay <0-68>",
+ "Set the Random Access Burst test mode with delay\n"
+ "RACH delay\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ if (trx->cfg.rtsc_set) {
+ vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (trx->cfg.egprs) {
+ vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ trx->cfg.rach_delay_set = true;
+ trx->cfg.rach_delay = atoi(argv[0]);
+ trx->cfg.filler = FILLER_ACCESS_RAND;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
+ "clock-ref (internal|external|gpsdo)",
+ "Set the Reference Clock\n"
+ "Enable internal referece (default)\n"
+ "Enable external 10 MHz reference\n"
+ "Enable GPSDO reference\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
+ "multi-arfcn (disable|enable)",
+ "Enable multi-ARFCN transceiver (default=disable)\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ if (strcmp("disable", argv[0]) == 0) {
+ trx->cfg.multi_arfcn = false;
+ } else if (strcmp("enable", argv[0]) == 0) {
+ trx->cfg.multi_arfcn = true;
+ } else {
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_offset, cfg_offset_cmd,
+ "offset FLOAT",
+ "Set the baseband frequency offset (default=0, auto)\n"
+ "Baseband Frequency Offset\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.offset = atof(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd,
+ "rssi-offset FLOAT",
+ "Set the RSSI to dBm offset in dB (default=0)\n"
+ "RSSI to dBm offset in dB\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.rssi_offset = atof(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
+ "swap-channels (disable|enable)",
+ "Swap channels (default=disable)\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ if (strcmp("disable", argv[0]) == 0) {
+ trx->cfg.swap_channels = false;
+ } else if (strcmp("enable", argv[0]) == 0) {
+ trx->cfg.swap_channels = true;
+ } else {
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_egprs, cfg_egprs_cmd,
+ "egprs (disable|enable)",
+ "Enable EDGE receiver (default=disable)\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ if (strcmp("disable", argv[0]) == 0) {
+ trx->cfg.egprs = false;
+ } else if (strcmp("enable", argv[0]) == 0) {
+ trx->cfg.egprs = true;
+ trx->cfg.filler = FILLER_EDGE_RAND;
+ } else {
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
+ "rt-prio <1-32>",
+ "Set the SCHED_RR real-time priority\n"
+ "Real time priority\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.sched_rr = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_filler, cfg_filler_cmd,
+ "filler dummy",
+ "Enable C0 filler table\n"
+ "Dummy method\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.filler = FILLER_DUMMY;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_chan, cfg_chan_cmd,
+ "chan <0-100>",
+ "Select a channel to configure\n"
+ "Channel index\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+ int idx = atoi(argv[0]);
+
+ if (idx >= TRX_CHAN_MAX) {
+ vty_out(vty, "Chan list full.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */
+ vty_out(vty, "Non-existent or non-consecutive chan %d.%s",
+ idx, VTY_NEWLINE);
+ return CMD_WARNING;
+ } else if (trx->cfg.num_chans == idx) { /* creating it */
+ trx->cfg.num_chans++;
+ trx->cfg.chans[idx].trx = trx;
+ trx->cfg.chans[idx].idx = idx;
+ }
+
+ vty->node = CHAN_NODE;
+ vty->index = &trx->cfg.chans[idx];
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd,
+ "rx-path NAME",
+ "Set the Rx Path\n"
+ "Rx Path name\n")
+{
+ struct trx_chan *chan = vty->index;
+
+ osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd,
+ "tx-path NAME",
+ "Set the Tx Path\n"
+ "Tx Path name\n")
+{
+ struct trx_chan *chan = vty->index;
+
+ osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+static int dummy_config_write(struct vty *v)
+{
+ return CMD_SUCCESS;
+}
+
+static int config_write_trx(struct vty *vty)
+{
+ struct trx_chan *chan;
+ int i;
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ vty_out(vty, "trx%s", VTY_NEWLINE);
+ if (trx->cfg.bind_addr)
+ vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
+ if (trx->cfg.remote_addr)
+ vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
+ if (trx->cfg.base_port != DEFAULT_TRX_PORT)
+ vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
+ if (trx->cfg.dev_args)
+ vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
+ if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
+ vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
+ if (trx->cfg.rx_sps != DEFAULT_RX_SPS)
+ vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
+ if (trx->cfg.rtsc_set)
+ vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
+ if (trx->cfg.rach_delay_set)
+ vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
+ if (trx->cfg.clock_ref != REF_INTERNAL)
+ vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
+ vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE);
+ if (trx->cfg.offset != 0)
+ vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE);
+ if (trx->cfg.rssi_offset != 0)
+ vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
+ vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE);
+ vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE);
+ if (trx->cfg.sched_rr != 0)
+ vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE);
+
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ chan = &trx->cfg.chans[i];
+ vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE);
+ if (chan->rx_path)
+ vty_out(vty, " rx-path %s%s", chan->rx_path, VTY_NEWLINE);
+ if (chan->tx_path)
+ vty_out(vty, " tx-path %s%s", chan->tx_path, VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
+{
+ struct trx_chan *chan;
+ int i;
+ vty_out(vty, "TRX Config:%s", VTY_NEWLINE);
+ vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
+ vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
+ vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE);
+ vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE);
+ vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
+ vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
+ vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc,
+ trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE);
+ vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay,
+ trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE);
+ vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
+ vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
+ vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE);
+ vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE);
+ vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
+ vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE);
+ vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE);
+ vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr,
+ trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE);
+ vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE);
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ chan = &trx->cfg.chans[i];
+ vty_out(vty, " Channel %u:%s", chan->idx, VTY_NEWLINE);
+ if (chan->rx_path)
+ vty_out(vty, " Rx Path: %s%s", chan->rx_path, VTY_NEWLINE);
+ if (chan->tx_path)
+ vty_out(vty, " Tx Path: %s%s", chan->tx_path, VTY_NEWLINE);
+ }
+}
+
+DEFUN(show_trx, show_trx_cmd,
+ "show trx",
+ SHOW_STR "Display information on the TRX\n")
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx_dump_vty(vty, trx);
+
+ return CMD_SUCCESS;
+}
+
+static int trx_vty_is_config_node(struct vty *vty, int node)
+{
+ switch (node) {
+ case TRX_NODE:
+ case CHAN_NODE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int trx_vty_go_parent(struct vty *vty)
+{
+ switch (vty->node) {
+ case TRX_NODE:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
+ break;
+ case CHAN_NODE:
+ vty->node = TRX_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
+ break;
+ default:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
+ }
+
+ return vty->node;
+}
+
+static const char trx_copyright[] =
+ "Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n"
+ "Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n"
+ "Copyright (C) 2015 Ettus Research LLC\r\n"
+ "Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
+ "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+struct vty_app_info g_vty_info = {
+ .name = "OsmoTRX",
+ .version = PACKAGE_VERSION,
+ .copyright = trx_copyright,
+ .go_parent_cb = trx_vty_go_parent,
+ .is_config_node = trx_vty_is_config_node,
+};
+
+struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
+{
+ struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx);
+
+ trx->cfg.bind_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
+ trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
+ trx->cfg.base_port = DEFAULT_TRX_PORT;
+ trx->cfg.tx_sps = DEFAULT_TX_SPS;
+ trx->cfg.rx_sps = DEFAULT_RX_SPS;
+ trx->cfg.filler = FILLER_ZERO;
+
+ return trx;
+}
+
+int trx_vty_init(struct trx_ctx* trx)
+{
+ g_trx_ctx = trx;
+ install_element_ve(&show_trx_cmd);
+
+ install_element(CONFIG_NODE, &cfg_trx_cmd);
+
+ install_node(&trx_node, config_write_trx);
+ install_element(TRX_NODE, &cfg_bind_ip_cmd);
+ install_element(TRX_NODE, &cfg_remote_ip_cmd);
+ install_element(TRX_NODE, &cfg_base_port_cmd);
+ install_element(TRX_NODE, &cfg_dev_args_cmd);
+ install_element(TRX_NODE, &cfg_tx_sps_cmd);
+ install_element(TRX_NODE, &cfg_rx_sps_cmd);
+ install_element(TRX_NODE, &cfg_test_rtsc_cmd);
+ install_element(TRX_NODE, &cfg_test_rach_delay_cmd);
+ install_element(TRX_NODE, &cfg_clock_ref_cmd);
+ install_element(TRX_NODE, &cfg_multi_arfcn_cmd);
+ install_element(TRX_NODE, &cfg_offset_cmd);
+ install_element(TRX_NODE, &cfg_rssi_offset_cmd);
+ install_element(TRX_NODE, &cfg_swap_channels_cmd);
+ install_element(TRX_NODE, &cfg_egprs_cmd);
+ install_element(TRX_NODE, &cfg_rt_prio_cmd);
+ install_element(TRX_NODE, &cfg_filler_cmd);
+
+ install_element(TRX_NODE, &cfg_chan_cmd);
+ install_node(&chan_node, dummy_config_write);
+ install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
+ install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
+
+ return 0;
+}
diff --git a/CommonLibs/trx_vty.h b/CommonLibs/trx_vty.h
new file mode 100644
index 0000000..c921722
--- /dev/null
+++ b/CommonLibs/trx_vty.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <osmocom/vty/command.h>
+
+#include "config_defs.h"
+
+extern struct vty_app_info g_vty_info;
+
+#define TRX_CHAN_MAX 8
+
+/* Samples-per-symbol for downlink path
+ * 4 - Uses precision modulator (more computation, less distortion)
+ * 1 - Uses minimized modulator (less computation, more distortion)
+ *
+ * Other values are invalid. Receive path (uplink) is always
+ * downsampled to 1 sps. Default to 4 sps for all cases.
+ */
+#define DEFAULT_TX_SPS 4
+
+/*
+ * Samples-per-symbol for uplink (receiver) path
+ * Do not modify this value. EDGE configures 4 sps automatically on
+ * B200/B210 devices only. Use of 4 sps on the receive path for other
+ * configurations is not supported.
+ */
+#define DEFAULT_RX_SPS 1
+
+/* Default configuration parameters */
+#define DEFAULT_TRX_PORT 5700
+#define DEFAULT_TRX_IP "127.0.0.1"
+#define DEFAULT_CHANS 1
+
+struct trx_ctx;
+
+struct trx_chan {
+ struct trx_ctx *trx; /* backpointer */
+ unsigned int idx; /* channel index */
+ char *rx_path;
+ char *tx_path;
+};
+
+struct trx_ctx {
+ struct {
+ char *bind_addr;
+ char *remote_addr;
+ char *dev_args;
+ unsigned int base_port;
+ unsigned int tx_sps;
+ unsigned int rx_sps;
+ unsigned int rtsc;
+ bool rtsc_set;
+ unsigned int rach_delay;
+ bool rach_delay_set;
+ enum ReferenceType clock_ref;
+ enum FillerType filler;
+ bool multi_arfcn;
+ double offset;
+ double rssi_offset;
+ bool swap_channels;
+ bool egprs;
+ unsigned int sched_rr;
+ unsigned int num_chans;
+ struct trx_chan chans[TRX_CHAN_MAX];
+ } cfg;
+};
+
+int trx_vty_init(struct trx_ctx* trx);
+struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx);
diff --git a/GSM/GSMCommon.cpp b/GSM/GSMCommon.cpp
new file mode 100644
index 0000000..711ca70
--- /dev/null
+++ b/GSM/GSMCommon.cpp
@@ -0,0 +1,93 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+* Copyright 2011 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 "GSMCommon.h"
+
+using namespace GSM;
+using namespace std;
+
+
+const BitVector GSM::gTrainingSequence[] = {
+ BitVector("00100101110000100010010111"),
+ BitVector("00101101110111100010110111"),
+ BitVector("01000011101110100100001110"),
+ BitVector("01000111101101000100011110"),
+ BitVector("00011010111001000001101011"),
+ BitVector("01001110101100000100111010"),
+ BitVector("10100111110110001010011111"),
+ BitVector("11101111000100101110111100"),
+};
+
+const BitVector GSM::gEdgeTrainingSequence[] = {
+ BitVector("111111001111111001111001001001111111111111001111111111001111111001111001001001"),
+ BitVector("111111001111001001111001001001111001001001001111111111001111001001111001001001"),
+ BitVector("111001111111111111001001001111001001001111001111111001111111111111001001001111"),
+ BitVector("111001111111111001001001001111001001111001111111111001111111111001001001001111"),
+ BitVector("111111111001001111001111001001001111111001111111111111111001001111001111001001"),
+ BitVector("111001111111001001001111001111001001111111111111111001111111001001001111001111"),
+ BitVector("001111001111111001001001001001111001001111111111001111001111111001001001001001"),
+ BitVector("001001001111001001001001111111111001111111001111001001001111001001001001111111"),
+};
+
+const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
+
+/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
+const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
+const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
+const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
+
+// |-head-||---------midamble----------------------||--------------data----------------||t|
+const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
+
+
+int32_t GSM::FNDelta(int32_t v1, int32_t v2)
+{
+ static const int32_t halfModulus = gHyperframe/2;
+ int32_t delta = v1-v2;
+ if (delta>=halfModulus) delta -= gHyperframe;
+ else if (delta<-halfModulus) delta += gHyperframe;
+ return (int32_t) delta;
+}
+
+int GSM::FNCompare(int32_t v1, int32_t v2)
+{
+ int32_t delta = FNDelta(v1,v2);
+ if (delta>0) return 1;
+ if (delta<0) return -1;
+ return 0;
+}
+
+
+
+
+ostream& GSM::operator<<(ostream& os, const Time& t)
+{
+ os << t.TN() << ":" << t.FN();
+ return os;
+}
+
+
+// vim: ts=4 sw=4
diff --git a/GSM/GSMCommon.h b/GSM/GSMCommon.h
new file mode 100644
index 0000000..f703c30
--- /dev/null
+++ b/GSM/GSMCommon.h
@@ -0,0 +1,253 @@
+/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
+/*
+* Copyright 2008-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/>.
+
+*/
+
+
+
+#ifndef GSMCOMMON_H
+#define GSMCOMMON_H
+
+#include <stdlib.h>
+#include <sys/time.h>
+#include <ostream>
+#include <vector>
+
+#include <Threads.h>
+#include <Timeval.h>
+#include <BitVector.h>
+
+
+
+
+namespace GSM {
+
+/**@namespace GSM This namespace covers L1 FEC, L2 and L3 message translation. */
+
+/** GSM Training sequences from GSM 05.02 5.2.3. */
+extern const BitVector gTrainingSequence[];
+extern const BitVector gEdgeTrainingSequence[];
+
+/** C0T0 filler burst, GSM 05.02, 5.2.6 */
+extern const BitVector gDummyBurst;
+
+/** Random access burst synch. sequence */
+extern const BitVector gRACHSynchSequenceTS0;
+extern const BitVector gRACHSynchSequenceTS1;
+extern const BitVector gRACHSynchSequenceTS2;
+/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
+extern const BitVector gRACHBurst;
+
+
+/**@name Modulus operations for frame numbers. */
+//@{
+/** The GSM hyperframe is largest time period in the GSM system, GSM 05.02 4.3.3. */
+const uint32_t gHyperframe = 2048UL * 26UL * 51UL;
+
+/** Get a clock difference, within the modulus, v1-v2. */
+int32_t FNDelta(int32_t v1, int32_t v2);
+
+/**
+ Compare two frame clock values.
+ @return 1 if v1>v2, -1 if v1<v2, 0 if v1==v2
+*/
+int FNCompare(int32_t v1, int32_t v2);
+
+
+//@}
+
+
+/**
+ GSM frame clock value. GSM 05.02 4.3
+ No internal thread sync.
+*/
+class Time {
+
+ private:
+
+ int mFN; ///< frame number in the hyperframe
+ int mTN; ///< timeslot number
+
+ public:
+
+ Time(int wFN=0, int wTN=0)
+ :mFN(wFN),mTN(wTN)
+ { }
+
+
+ /** Move the time forward to a given position in a given modulus. */
+ void rollForward(unsigned wFN, unsigned modulus)
+ {
+ assert(modulus<gHyperframe);
+ while ((mFN % modulus) != wFN) mFN=(mFN+1)%gHyperframe;
+ }
+
+ /**@name Accessors. */
+ //@{
+ int FN() const { return mFN; }
+ void FN(unsigned wFN) { mFN = wFN; }
+ unsigned TN() const { return mTN; }
+ void TN(unsigned wTN) { mTN=wTN; }
+ //@}
+
+ /**@name Arithmetic. */
+ //@{
+
+ Time& operator++()
+ {
+ mFN = (mFN+1) % gHyperframe;
+ return *this;
+ }
+
+ Time& decTN(unsigned step=1)
+ {
+ assert(step<=8);
+ mTN -= step;
+ if (mTN<0) {
+ mTN+=8;
+ mFN-=1;
+ if (mFN<0) mFN+=gHyperframe;
+ }
+ return *this;
+ }
+
+ Time& incTN(unsigned step=1)
+ {
+ assert(step<=8);
+ mTN += step;
+ if (mTN>7) {
+ mTN-=8;
+ mFN = (mFN+1) % gHyperframe;
+ }
+ return *this;
+ }
+
+ Time& operator+=(int step)
+ {
+ // Remember the step might be negative.
+ mFN += step;
+ if (mFN<0) mFN+=gHyperframe;
+ mFN = mFN % gHyperframe;
+ return *this;
+ }
+
+ Time operator-(int step) const
+ { return operator+(-step); }
+
+ Time operator+(int step) const
+ {
+ Time newVal = *this;
+ newVal += step;
+ return newVal;
+ }
+
+ Time operator+(const Time& other) const
+ {
+ unsigned newTN = (mTN + other.mTN) % 8;
+ uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe;
+ return Time(newFN,newTN);
+ }
+
+ int operator-(const Time& other) const
+ {
+ return FNDelta(mFN,other.mFN);
+ }
+
+ //@}
+
+
+ /**@name Comparisons. */
+ //@{
+
+ bool operator<(const Time& other) const
+ {
+ if (mFN==other.mFN) return (mTN<other.mTN);
+ return FNCompare(mFN,other.mFN)<0;
+ }
+
+ bool operator>(const Time& other) const
+ {
+ if (mFN==other.mFN) return (mTN>other.mTN);
+ return FNCompare(mFN,other.mFN)>0;
+ }
+
+ bool operator<=(const Time& other) const
+ {
+ if (mFN==other.mFN) return (mTN<=other.mTN);
+ return FNCompare(mFN,other.mFN)<=0;
+ }
+
+ bool operator>=(const Time& other) const
+ {
+ if (mFN==other.mFN) return (mTN>=other.mTN);
+ return FNCompare(mFN,other.mFN)>=0;
+ }
+
+ bool operator==(const Time& other) const
+ {
+ return (mFN == other.mFN) && (mTN==other.mTN);
+ }
+
+ //@}
+
+
+
+ /**@name Standard derivations. */
+ //@{
+
+ /** GSM 05.02 3.3.2.2.1 */
+ unsigned SFN() const { return mFN / (26*51); }
+
+ /** GSM 05.02 3.3.2.2.1 */
+ unsigned T1() const { return SFN() % 2048; }
+
+ /** GSM 05.02 3.3.2.2.1 */
+ unsigned T2() const { return mFN % 26; }
+
+ /** GSM 05.02 3.3.2.2.1 */
+ unsigned T3() const { return mFN % 51; }
+
+ /** GSM 05.02 3.3.2.2.1. */
+ unsigned T3p() const { return (T3()-1)/10; }
+
+ /** GSM 05.02 6.3.1.3. */
+ unsigned TC() const { return (FN()/51) % 8; }
+
+ /** GSM 04.08 10.5.2.30. */
+ unsigned T1p() const { return SFN() % 32; }
+
+ /** GSM 05.02 6.2.3 */
+ unsigned T1R() const { return T1() % 64; }
+
+ //@}
+};
+
+
+std::ostream& operator<<(std::ostream& os, const Time& ts);
+
+}; // namespace GSM
+
+
+#endif
+
+// vim: ts=4 sw=4
diff --git a/GSM/Makefile.am b/GSM/Makefile.am
new file mode 100644
index 0000000..a2f5db0
--- /dev/null
+++ b/GSM/Makefile.am
@@ -0,0 +1,32 @@
+#
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This software is distributed under the terms of the GNU Public License.
+# See the COPYING file in the main directory for details.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
+#AM_CXXFLAGS = -O2 -g
+
+noinst_LTLIBRARIES = libGSM.la
+
+libGSM_la_SOURCES = \
+ GSMCommon.cpp
+
+noinst_HEADERS = \
+ GSMCommon.h
diff --git a/INSTALLATION b/INSTALLATION
new file mode 100644
index 0000000..f87b6cc
--- /dev/null
+++ b/INSTALLATION
@@ -0,0 +1,19 @@
+Installation Requirements
+
+
+
+osmo-trx compiles to a simple Unix binary and does not require special
+installation.
+
+One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
+running configure.
+
+To run osmo-trx, the following should be installed:
+ libuhd (https://gnuradio.org).
+ This is part of the GNURadio installation.
+
+For information on specific executables, see tests/README.tests and
+apps/README.apps.
+
+See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
+information.
diff --git a/LEGAL b/LEGAL
new file mode 100644
index 0000000..a8b75f2
--- /dev/null
+++ b/LEGAL
@@ -0,0 +1,60 @@
+OpenBTS
+
+The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details.
+
+The related copyrights:
+Most parts copyright 2008-2011 Free Software Foundation.
+Some parts copyright 2010 Kestrel Signal Processing, Inc.
+Some parts copyright 2011 Range Networks, Inc.
+
+
+Patent Laws
+
+The use of this software to provide GSM services may result in the use of
+patented technologies. The user of this software is required to take whatever
+actions are necessary to avoid patent infringement.
+
+
+Telecom and Radio Spectrum Laws
+
+The primary function of OsmoTRX is the provision of telecommunications service
+over a radio link. This activity is heavily regulated nearly everywhere in
+the world. Users of this software are expected to comply with local and national
+regulations in the jurisdictions where this sortware is used with radio equipment.
+
+
+Legal Summary
+
+The user of this software is expected to comply with all applicable laws and
+regulations, including patent laws, copyright laws, and telecommunications
+regulations.
+
+The legal restrictions listed here are not necessarily exhaustive.
+
+
+Note to US Government Users
+
+The OsmoTRX software applications and associated documentation are "Commercial
+Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of
+"Commercial Computer Software" and "Commercial Computer Software Documentation,"
+as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
+applicable. Consistent with 48 C.F.R. 12.212 or 48 C.F.R. Sections 227.7202-1
+through 227.7202-4, as applicable, the Commercial Computer Software and
+Commercial Computer Software Documentation are being licensed to U.S. Government
+end users (a) only as Commercial Items and (b) with only those rights as are
+granted to all other end users pursuant to the terms and conditions of GPLv3
+and AGPLv3.
+
+
+Note to US Government Contractors
+
+GPL is not compatible with "government purpose rights" (GPR). If you receive
+OsmoTRX software under a GPL and deliver it under GPR, you will be in violation
+of GPL and possibly subject to enforcement actions by the original authors and
+copyright holders, including the Free Software Foundation, Inc.
+
+
+Software Licensing and Distribution
+
+The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file
+for more information on the license for this distribution.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..38cd88d
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,55 @@
+#
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This software is distributed under the terms of the GNU Public License.
+# See the COPYING file in the main directory for details.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+include $(top_srcdir)/Makefile.common
+
+ACLOCAL_AMFLAGS = -I config
+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
+AM_CXXFLAGS = -Wall -pthread
+#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
+#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
+
+# Order must be preserved
+SUBDIRS = \
+ doc \
+ CommonLibs \
+ GSM \
+ Transceiver52M \
+ contrib \
+ tests
+
+EXTRA_DIST = \
+ autogen.sh \
+ INSTALLATION \
+ LEGAL \
+ COPYING \
+ README
+
+DISTCHECK_CONFIGURE_FLAGS = \
+ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
+
+.PHONY: release
+
+@RELMAKE@
+
+dox: FORCE
+ doxygen doxconfig
+
+FORCE:
diff --git a/Makefile.common b/Makefile.common
new file mode 100644
index 0000000..1de9733
--- /dev/null
+++ b/Makefile.common
@@ -0,0 +1,38 @@
+#
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This software is distributed under the terms of the GNU Public License.
+# See the COPING file in the main directory for details.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
+GSM_INCLUDEDIR = $(top_srcdir)/GSM
+
+STD_DEFINES_AND_INCLUDES = \
+ $(SVNDEV) \
+ -I$(COMMON_INCLUDEDIR) \
+ -I$(GSM_INCLUDEDIR)
+
+COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
+GSM_LA = $(top_builddir)/GSM/libGSM.la
+
+if ARCH_ARM
+ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la
+else
+ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la
+endif
+
+MOSTLYCLEANFILES = *~
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..502d661
--- /dev/null
+++ b/README
@@ -0,0 +1,116 @@
+This is the interface to the transcevier.
+
+Each TRX Manager UDP socket interface represents a single ARFCN.
+Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
+Give a base port B (5700), the master clock interface is at port P=B.
+The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
+The corresponding core-side interface for every socket is at P+100.
+For any given build, the number of ARFCN interfaces can be fixed.
+
+
+
+Indications on the Master Clock Interface
+
+The master clock interface is output only (from the radio).
+Messages are "indications".
+
+CLOCK gives the current value of the transceiver clock to be used by the core.
+This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
+IND CLOCK <totalFrames>
+
+
+
+Commands on the Per-ARFCN Control Interface
+
+The per-ARFCN control interface uses a command-reponse protocol.
+Commands are NULL-terminated ASCII strings, one per UDP socket.
+Each command has a corresponding response.
+Every command is of the form:
+
+CMD <cmdtype> [params]
+
+The <cmdtype> is the actual command.
+Parameters are optional depending on the commands type.
+Every response is of the form:
+
+RSP <cmdtype> <status> [result]
+
+The <status> is 0 for success and a non-zero error code for failure.
+Successful responses may include results, depending on the command type.
+
+
+Power Control
+
+POWEROFF shuts off transmitter power and stops the demodulator.
+CMD POWEROFF
+RSP POWEROFF <status>
+
+POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
+This command fails if the transmitter and receiver are not yet tuned.
+This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
+If the transceiver is already on, it response with success to this command.
+CMD POWERON
+RSP POWERON <status>
+
+SETPOWER sets output power in dB wrt full scale.
+This command fails if the transmitter and receiver are not running.
+CMD SETPOWER <dB>
+RSP SETPOWER <status> <dB>
+
+ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
+This command fails if the transmitter and receiver are not running.
+CMD ADJPOWER <dBStep>
+RSP ADJPOWER <status> <dBLevel>
+
+
+Tuning Control
+
+RXTUNE tunes the receiver to a given frequency in kHz.
+This command fails if the receiver is already running.
+(To re-tune you stop the radio, re-tune, and restart.)
+This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
+CMD RXTUNE <kHz>
+RSP RXTUNE <status> <kHz>
+
+TXTUNE tunes the transmitter to a given frequency in kHz.
+This command fails if the transmitter is already running.
+(To re-tune you stop the radio, re-tune, and restart.)
+This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
+CMD TXTUNE <kHz>
+RSP TXTUNE <status> <kHz>
+
+
+Timeslot Control
+
+SETSLOT sets the format of the uplink timeslots in the ARFCN.
+The <timeslot> indicates the timeslot of interest.
+The <chantype> indicates the type of channel that occupies the timeslot.
+A chantype of zero indicates the timeslot is off.
+CMD SETSLOT <timeslot> <chantype>
+RSP SETSLOT <status> <timeslot> <chantype>
+
+
+Messages on the per-ARFCN Data Interface
+
+Messages on the data interface carry one radio burst per UDP message.
+
+
+Received Data Burst
+
+1 byte timeslot index
+4 bytes GSM frame number, big endian
+1 byte RSSI in -dBm
+2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
+148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
+
+
+Transmit Data Burst
+
+1 byte timeslot index
+4 bytes GSM frame number, big endian
+1 byte transmit level wrt ARFCN max, -dB (attenuation)
+148 bytes output symbol values, 0 & 1
+
+
+
+
diff --git a/Transceiver52M/Channelizer.cpp b/Transceiver52M/Channelizer.cpp
new file mode 100644
index 0000000..2d817b0
--- /dev/null
+++ b/Transceiver52M/Channelizer.cpp
@@ -0,0 +1,107 @@
+/*
+ * Polyphase channelizer
+ *
+ * Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include <cstdio>
+
+#include "Channelizer.h"
+
+extern "C" {
+#include "fft.h"
+#include "convolve.h"
+}
+
+static void deinterleave(const float *in, size_t ilen,
+ float **out, size_t olen, size_t m)
+{
+ size_t i, n;
+
+ for (i = 0; i < olen; i++) {
+ for (n = 0; n < m; n++) {
+ out[m - 1 - n][2 * i + 0] = in[2 * (i * m + n) + 0];
+ out[m - 1 - n][2 * i + 1] = in[2 * (i * m + n) + 1];
+ }
+ }
+}
+
+size_t Channelizer::inputLen() const
+{
+ return blockLen * m;
+}
+
+size_t Channelizer::outputLen() const
+{
+ return blockLen;
+}
+
+float *Channelizer::outputBuffer(size_t chan) const
+{
+ if (chan >= m)
+ return NULL;
+
+ return hInputs[chan];
+}
+
+/*
+ * Implementation based on material found in:
+ *
+ * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
+ * Prentice Hall, 2006."
+ */
+bool Channelizer::rotate(const float *in, size_t len)
+{
+ size_t hSize = 2 * hLen * sizeof(float);
+
+ if (!checkLen(blockLen, len))
+ return false;
+
+ deinterleave(in, len, hInputs, blockLen, m);
+
+ /*
+ * Convolve through filterbank while applying and saving sample history
+ */
+ for (size_t i = 0; i < m; i++) {
+ memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
+ memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
+
+ convolve_real(hInputs[i], blockLen,
+ subFilters[i], hLen,
+ hOutputs[i], blockLen,
+ 0, blockLen, 1, 0);
+ }
+
+ cxvec_fft(fftHandle);
+
+ return true;
+}
+
+/* Setup channelizer paramaters */
+Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
+ : ChannelizerBase(m, blockLen, hLen)
+{
+}
+
+Channelizer::~Channelizer()
+{
+}
diff --git a/Transceiver52M/Channelizer.h b/Transceiver52M/Channelizer.h
new file mode 100644
index 0000000..fdd6779
--- /dev/null
+++ b/Transceiver52M/Channelizer.h
@@ -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_ */
diff --git a/Transceiver52M/ChannelizerBase.cpp b/Transceiver52M/ChannelizerBase.cpp
new file mode 100644
index 0000000..430e260
--- /dev/null
+++ b/Transceiver52M/ChannelizerBase.cpp
@@ -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);
+}
diff --git a/Transceiver52M/ChannelizerBase.h b/Transceiver52M/ChannelizerBase.h
new file mode 100644
index 0000000..7da506b
--- /dev/null
+++ b/Transceiver52M/ChannelizerBase.h
@@ -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_ */
diff --git a/Transceiver52M/Complex.h b/Transceiver52M/Complex.h
new file mode 100644
index 0000000..d02944b
--- /dev/null
+++ b/Transceiver52M/Complex.h
@@ -0,0 +1,266 @@
+/**@file templates for Complex classes
+unlike the built-in complex<> templates, these inline most operations for speed
+*/
+
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* 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 COMPLEXCPP_H
+#define COMPLEXCPP_H
+
+#include <math.h>
+#include <ostream>
+
+
+template<class Real> class Complex {
+
+public:
+
+ Real r, i;
+
+ /**@name constructors */
+ //@{
+ /**@name from real */
+ //@{
+ Complex(Real real, Real imag) {r=real; i=imag;} // x=complex(a,b)
+ Complex(Real real) {r=real; i=0;} // x=complex(a)
+ //@}
+ /**@name from nothing */
+ //@{
+ Complex() {r=(Real)0; i=(Real)0;} // x=complex()
+ //@}
+ /**@name from other complex */
+ //@{
+ Complex(const Complex<float>& z) {r=z.r; i=z.i;} // x=complex(z)
+ Complex(const Complex<double>& z) {r=z.r; i=z.i;} // x=complex(z)
+ Complex(const Complex<long double>& z) {r=z.r; i=z.i;} // x=complex(z)
+ //@}
+ //@}
+
+ /**@name casting up from basic numeric types */
+ //@{
+ Complex& operator=(char a) { r=(Real)a; i=(Real)0; return *this; }
+ Complex& operator=(int a) { r=(Real)a; i=(Real)0; return *this; }
+ Complex& operator=(long int a) { r=(Real)a; i=(Real)0; return *this; }
+ Complex& operator=(short a) { r=(Real)a; i=(Real)0; return *this; }
+ Complex& operator=(float a) { r=(Real)a; i=(Real)0; return *this; }
+ Complex& operator=(double a) { r=(Real)a; i=(Real)0; return *this; }
+ Complex& operator=(long double a) { r=(Real)a; i=(Real)0; return *this; }
+ //@}
+
+ /**@name arithmetic */
+ //@{
+ /**@ binary operators */
+ //@{
+ Complex operator+(const Complex<Real>& a) const { return Complex<Real>(r+a.r, i+a.i); }
+ Complex operator+(Real a) const { return Complex<Real>(r+a,i); }
+ Complex operator-(const Complex<Real>& a) const { return Complex<Real>(r-a.r, i-a.i); }
+ Complex operator-(Real a) const { return Complex<Real>(r-a,i); }
+ Complex operator*(const Complex<Real>& a) const { return Complex<Real>(r*a.r-i*a.i, r*a.i+i*a.r); }
+ Complex operator*(Real a) const { return Complex<Real>(r*a, i*a); }
+ Complex operator/(const Complex<Real>& a) const { return operator*(a.inv()); }
+ Complex operator/(Real a) const { return Complex<Real>(r/a, i/a); }
+ //@}
+ /*@name component-wise product */
+ //@{
+ Complex operator&(const Complex<Real>& a) const { return Complex<Real>(r*a.r, i*a.i); }
+ //@}
+ /*@name inplace operations */
+ //@{
+ Complex& operator+=(const Complex<Real>&);
+ Complex& operator-=(const Complex<Real>&);
+ Complex& operator*=(const Complex<Real>&);
+ Complex& operator/=(const Complex<Real>&);
+ Complex& operator+=(Real);
+ Complex& operator-=(Real);
+ Complex& operator*=(Real);
+ Complex& operator/=(Real);
+ //@}
+ //@}
+
+ /**@name comparisons */
+ //@{
+ bool operator==(const Complex<Real>& a) const { return ((i==a.i)&&(r==a.r)); }
+ bool operator!=(const Complex<Real>& a) const { return ((i!=a.i)||(r!=a.r)); }
+ bool operator<(const Complex<Real>& a) const { return norm2()<a.norm2(); }
+ bool operator>(const Complex<Real>& a) const { return norm2()>a.norm2(); }
+ //@}
+
+ /// reciprocation
+ Complex inv() const;
+
+ // unary functions -- inlined
+ /**@name unary functions */
+ //@{
+ /**@name inlined */
+ //@{
+ Complex conj() const { return Complex<Real>(r,-i); }
+ Real norm2() const { return i*i+r*r; }
+ Complex flip() const { return Complex<Real>(i,r); }
+ Real real() const { return r;}
+ Real imag() const { return i;}
+ Complex neg() const { return Complex<Real>(-r, -i); }
+ bool isZero() const { return ((r==(Real)0) && (i==(Real)0)); }
+ //@}
+ /**@name not inlined due to outside calls */
+ //@{
+ Real abs() const { return ::sqrt(norm2()); }
+ Real arg() const { return ::atan2(i,r); }
+ float dB() const { return 10.0*log10(norm2()); }
+ Complex exp() const { return expj(i)*(::exp(r)); }
+ Complex unit() const; ///< unit phasor with same angle
+ Complex log() const { return Complex(::log(abs()),arg()); }
+ Complex pow(double n) const { return expj(arg()*n)*(::pow(abs(),n)); }
+ Complex sqrt() const { return pow(0.5); }
+ //@}
+ //@}
+
+};
+
+
+/**@name standard Complex manifestations */
+//@{
+typedef Complex<float> complex;
+typedef Complex<double> dcomplex;
+typedef Complex<short> complex16;
+typedef Complex<long> complex32;
+//@}
+
+
+template<class Real> inline Complex<Real> Complex<Real>::inv() const
+{
+ Real nVal;
+
+ nVal = norm2();
+ return Complex<Real>(r/nVal, -i/nVal);
+}
+
+template<class Real> Complex<Real>& Complex<Real>::operator+=(const Complex<Real>& a)
+{
+ r += a.r;
+ i += a.i;
+ return *this;
+}
+
+template<class Real> Complex<Real>& Complex<Real>::operator*=(const Complex<Real>& a)
+{
+ operator*(a);
+ return *this;
+}
+
+template<class Real> Complex<Real>& Complex<Real>::operator-=(const Complex<Real>& a)
+{
+ r -= a.r;
+ i -= a.i;
+ return *this;
+}
+
+template<class Real> Complex<Real>& Complex<Real>::operator/=(const Complex<Real>& a)
+{
+ operator/(a);
+ return *this;
+}
+
+
+/* op= style operations with reals */
+
+template<class Real> Complex<Real>& Complex<Real>::operator+=(Real a)
+{
+ r += a;
+ return *this;
+}
+
+template<class Real> Complex<Real>& Complex<Real>::operator*=(Real a)
+{
+ r *=a;
+ i *=a;
+ return *this;
+}
+
+template<class Real> Complex<Real>& Complex<Real>::operator-=(Real a)
+{
+ r -= a;
+ return *this;
+}
+
+template<class Real> Complex<Real>& Complex<Real>::operator/=(Real a)
+{
+ r /= a;
+ i /= a;
+ return *this;
+}
+
+
+template<class Real> Complex<Real> Complex<Real>::unit() const
+{
+ Real absVal = abs();
+ return (Complex<Real>(r/absVal, i/absVal));
+}
+
+
+
+/**@name complex functions outside of the Complex<> class. */
+//@{
+
+/** this allows type-commutative multiplication */
+template<class Real> Complex<Real> operator*(Real a, const Complex<Real>& z)
+{
+ return Complex<Real>(z.r*a, z.i*a);
+}
+
+
+/** this allows type-commutative addition */
+template<class Real> Complex<Real> operator+(Real a, const Complex<Real>& z)
+{
+ return Complex<Real>(z.r+a, z.i);
+}
+
+
+/** this allows type-commutative subtraction */
+template<class Real> Complex<Real> operator-(Real a, const Complex<Real>& z)
+{
+ return Complex<Real>(z.r-a, z.i);
+}
+
+
+
+/// e^jphi
+template<class Real> Complex<Real> expj(Real phi)
+{
+ return Complex<Real>(cos(phi),sin(phi));
+}
+
+/// phasor expression of a complex number
+template<class Real> Complex<Real> phasor(Real C, Real phi)
+{
+ return (expj(phi)*C);
+}
+
+/// formatted stream output
+template<class Real> std::ostream& operator<<(std::ostream& os, const Complex<Real>& z)
+{
+ os << z.r << ' ';
+ //os << z.r << ", ";
+ //if (z.i>=0) { os << "+"; }
+ os << z.i << "j";
+ return os;
+}
+
+//@}
+
+
+#endif
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am
new file mode 100644
index 0000000..28c47ab
--- /dev/null
+++ b/Transceiver52M/Makefile.am
@@ -0,0 +1,111 @@
+#
+# Copyright 2008 Free Software Foundation, Inc.
+# Copyright 2010 Range Networks, Inc.
+#
+# This software is distributed under the terms of the GNU Public License.
+# See the COPYING file in the main directory for details.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS = arch device
+
+AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device
+AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
+
+rev2dir = $(datadir)/usrp/rev2
+rev4dir = $(datadir)/usrp/rev4
+
+dist_rev2_DATA = std_inband.rbf
+dist_rev4_DATA = std_inband.rbf
+
+EXTRA_DIST = README
+
+noinst_LTLIBRARIES = libtransceiver_common.la
+
+COMMON_SOURCES = \
+ radioInterface.cpp \
+ radioVector.cpp \
+ radioClock.cpp \
+ radioBuffer.cpp \
+ sigProcLib.cpp \
+ signalVector.cpp \
+ Transceiver.cpp \
+ ChannelizerBase.cpp \
+ Channelizer.cpp \
+ Synthesis.cpp
+
+libtransceiver_common_la_SOURCES = \
+ $(COMMON_SOURCES) \
+ Resampler.cpp \
+ radioInterfaceResamp.cpp \
+ radioInterfaceMulti.cpp
+
+noinst_HEADERS = \
+ Complex.h \
+ radioInterface.h \
+ radioVector.h \
+ radioClock.h \
+ radioBuffer.h \
+ sigProcLib.h \
+ signalVector.h \
+ Transceiver.h \
+ Resampler.h \
+ ChannelizerBase.h \
+ Channelizer.h \
+ Synthesis.h
+
+COMMON_LDADD = \
+ libtransceiver_common.la \
+ $(ARCH_LA) \
+ $(GSM_LA) \
+ $(COMMON_LA) \
+ $(FFTWF_LIBS) \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOCTRL_LIBS) \
+ $(LIBOSMOVTY_LIBS)
+
+bin_PROGRAMS =
+
+if DEVICE_UHD
+bin_PROGRAMS += osmo-trx-uhd
+osmo_trx_uhd_SOURCES = osmo-trx.cpp
+osmo_trx_uhd_LDADD = \
+ $(builddir)/device/uhd/libdevice.la \
+ $(COMMON_LDADD) \
+ $(UHD_LIBS)
+osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
+endif
+
+if DEVICE_USRP1
+bin_PROGRAMS += osmo-trx-usrp1
+osmo_trx_usrp1_SOURCES = osmo-trx.cpp
+osmo_trx_usrp1_LDADD = \
+ $(builddir)/device/usrp1/libdevice.la \
+ $(COMMON_LDADD) \
+ $(USRP_LIBS)
+osmo_trx_usrp1_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS)
+endif
+
+if DEVICE_LMS
+bin_PROGRAMS += osmo-trx-lms
+osmo_trx_lms_SOURCES = osmo-trx.cpp
+osmo_trx_lms_LDADD = \
+ $(builddir)/device/lms/libdevice.la \
+ $(COMMON_LDADD) \
+ $(LMS_LIBS)
+osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
+endif
diff --git a/Transceiver52M/README b/Transceiver52M/README
new file mode 100644
index 0000000..491693c
--- /dev/null
+++ b/Transceiver52M/README
@@ -0,0 +1,35 @@
+The Transceiver
+
+The transceiver consists of three modules:
+ --- transceiver
+ --- radioInterface
+ --- USRPDevice
+
+The USRPDevice module is basically a driver that reads/writes
+packets to a USRP with two RFX900 daughterboards, board
+A is the Tx chain and board B is the Rx chain.
+
+The radioInterface module is basically an interface b/w the
+transceiver and the USRP. It operates the basestation clock
+based upon the sample count of received USRP samples. Packets
+from the USRP are queued and segmented into GSM bursts that are
+passed up to the transceiver; bursts from the transceiver are
+passed down to the USRP.
+
+The transceiver basically operates "layer 0" of the GSM stack,
+performing the modulation, detection, and demodulation of GSM
+bursts. It communicates with the GSM stack via three UDP sockets,
+one socket for data, one for control messages, and one socket to
+pass clocking information. The transceiver contains a priority
+queue to sort to-be-transmitted bursts, and a filler table to fill
+in timeslots that do not have bursts in the priority queue. The
+transceiver tries to stay ahead of the basestation clock, adapting
+its latency when underruns are reported by the radioInterface/USRP.
+Received bursts (from the radioInterface) pass through a simple
+energy detector, a RACH or midamble correlator, and a DFE-based demodulator.
+
+NOTE: There's a SWLOOPBACK #define statement, where the USRP is replaced
+with a memory buffer. In this mode, data written to the USRP is actually stored
+in a buffer, and read commands to the USRP simply pull data from this buffer.
+This was very useful in early testing, and still may be useful in testing basic
+Transceiver and radioInterface functionality.
diff --git a/Transceiver52M/README.DFEsymbolspaced b/Transceiver52M/README.DFEsymbolspaced
new file mode 100644
index 0000000..f1ab479
--- /dev/null
+++ b/Transceiver52M/README.DFEsymbolspaced
@@ -0,0 +1,14 @@
+signalVectors G0, G1. i.e. G0(D) = 1 +2D + 3D^2 = [1 2 3]
+G0(D) = 1/sqrt(SNR).
+G1(D) = [h0 h1D .. h_(N-1)D^(N-1)]
+for i = 0,1,...,N_f-1,
+ d = |G0(0)|^2+|G1(0)|^2
+ l_i(D) = D^i ( G0(D)*G0'(0) + G1(D)*G1'(0) )/d
+ k = G1(0)/G0(0)
+ G0n(D) = G0(D)+k'*G1(D)
+ G1n(D) = (-G0(D)*k+G1(D))/D
+ G0(D) = G0n(D)/sqrt(1+k*k')
+ G1(D) = G1n(D)/sqrt(1+k*k')
+end
+
+
diff --git a/Transceiver52M/Resampler.cpp b/Transceiver52M/Resampler.cpp
new file mode 100644
index 0000000..6b9621c
--- /dev/null
+++ b/Transceiver52M/Resampler.cpp
@@ -0,0 +1,187 @@
+/*
+ * Rational Sample Rate Conversion
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <malloc.h>
+#include <iostream>
+#include <algorithm>
+
+#include "Resampler.h"
+
+extern "C" {
+#include "convolve.h"
+}
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846264338327f
+#endif
+
+#define MAX_OUTPUT_LEN 4096
+
+using namespace std;
+
+static float sinc(float x)
+{
+ if (x == 0.0)
+ return 0.9999999999;
+
+ return sin(M_PI * x) / (M_PI * x);
+}
+
+void Resampler::initFilters(float bw)
+{
+ float cutoff;
+ float sum = 0.0f, scale = 0.0f;
+
+ /*
+ * Allocate partition filters and the temporary prototype filter
+ * according to numerator of the rational rate. Coefficients are
+ * real only and must be 16-byte memory aligned for SSE usage.
+ */
+ auto proto = vector<float>(p * filt_len);
+ for (auto &part : partitions)
+ part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
+
+ /*
+ * Generate the prototype filter with a Blackman-harris window.
+ * Scale coefficients with DC filter gain set to unity divided
+ * by the number of filter partitions.
+ */
+ float a0 = 0.35875;
+ float a1 = 0.48829;
+ float a2 = 0.14128;
+ float a3 = 0.01168;
+
+ if (p > q)
+ cutoff = (float) p;
+ else
+ cutoff = (float) q;
+
+ float midpt = (proto.size() - 1) / 2.0;
+ for (size_t i = 0; i < proto.size(); i++) {
+ proto[i] = sinc(((float) i - midpt) / cutoff * bw);
+ proto[i] *= a0 -
+ a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
+ a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
+ a3 * cos(6 * M_PI * i / (proto.size() - 1));
+ sum += proto[i];
+ }
+ scale = p / sum;
+
+ /* Populate filter partitions from the prototype filter */
+ for (size_t i = 0; i < filt_len; i++) {
+ for (size_t n = 0; n < p; n++)
+ partitions[n][i] = complex<float>(proto[i * p + n] * scale);
+ }
+
+ /* Store filter taps in reverse */
+ for (auto &part : partitions)
+ reverse(&part[0], &part[filt_len]);
+}
+
+static bool check_vec_len(int in_len, int out_len, int p, int q)
+{
+ if (in_len % q) {
+ std::cerr << "Invalid input length " << in_len
+ << " is not multiple of " << q << std::endl;
+ return false;
+ }
+
+ if (out_len % p) {
+ std::cerr << "Invalid output length " << out_len
+ << " is not multiple of " << p << std::endl;
+ return false;
+ }
+
+ if ((in_len / q) != (out_len / p)) {
+ std::cerr << "Input/output block length mismatch" << std::endl;
+ std::cerr << "P = " << p << ", Q = " << q << std::endl;
+ std::cerr << "Input len: " << in_len << std::endl;
+ std::cerr << "Output len: " << out_len << std::endl;
+ return false;
+ }
+
+ if (out_len > MAX_OUTPUT_LEN) {
+ std::cerr << "Block length of " << out_len
+ << " exceeds max of " << MAX_OUTPUT_LEN << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
+{
+ int n, path;
+
+ if (!check_vec_len(in_len, out_len, p, q))
+ return -1;
+
+ /* Generate output from precomputed input/output paths */
+ for (size_t i = 0; i < out_len; i++) {
+ n = in_index[i];
+ path = out_path[i];
+
+ convolve_real(in, in_len,
+ reinterpret_cast<float *>(partitions[path]),
+ filt_len, &out[2 * i], out_len - i,
+ n, 1, 1, 0);
+ }
+
+ return out_len;
+}
+
+bool Resampler::init(float bw)
+{
+ if (p == 0 || q == 0 || filt_len == 0) return false;
+
+ /* Filterbank filter internals */
+ initFilters(bw);
+
+ /* Precompute filterbank paths */
+ int i = 0;
+ for (auto &index : in_index)
+ index = (q * i++) / p;
+ i = 0;
+ for (auto &path : out_path)
+ path = (q * i++) % p;
+
+ return true;
+}
+
+size_t Resampler::len()
+{
+ return filt_len;
+}
+
+Resampler::Resampler(size_t p, size_t q, size_t filt_len)
+ : in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
+{
+ this->p = p;
+ this->q = q;
+ this->filt_len = filt_len;
+}
+
+Resampler::~Resampler()
+{
+ for (auto &part : partitions)
+ free(part);
+}
diff --git a/Transceiver52M/Resampler.h b/Transceiver52M/Resampler.h
new file mode 100644
index 0000000..caffc08
--- /dev/null
+++ b/Transceiver52M/Resampler.h
@@ -0,0 +1,76 @@
+/*
+ * Rational Sample Rate Conversion
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _RESAMPLER_H_
+#define _RESAMPLER_H_
+
+#include <vector>
+#include <complex>
+
+class Resampler {
+public:
+ /* Constructor for rational sample rate conversion
+ * @param p numerator of resampling ratio
+ * @param q denominator of resampling ratio
+ * @param filt_len length of each polyphase subfilter
+ */
+ Resampler(size_t p, size_t q, size_t filt_len = 16);
+ ~Resampler();
+
+ /* Initilize resampler filterbank.
+ * @param bw bandwidth factor on filter generation (pre-window)
+ * @return false on error, zero otherwise
+ *
+ * Automatic setting is to compute the filter to prevent aliasing with
+ * a Blackman-Harris window. Adjustment is made through a bandwith
+ * factor to shift the cutoff and/or the constituent filter lengths.
+ * Calculation of specific rolloff factors or 3-dB cutoff points is
+ * left as an excersize for the reader.
+ */
+ bool init(float bw = 1.0f);
+
+ /* Rotate "commutator" and drive samples through filterbank
+ * @param in continuous buffer of input complex float values
+ * @param in_len input buffer length
+ * @param out continuous buffer of output complex float values
+ * @param out_len output buffer length
+ * @return number of samples outputted, negative on error
+ *
+ * Input and output vector lengths must of be equal multiples of the
+ * rational conversion rate denominator and numerator respectively.
+ */
+ int rotate(const float *in, size_t in_len, float *out, size_t out_len);
+
+ /* Get filter length
+ * @return number of taps in each filter partition
+ */
+ size_t len();
+
+private:
+ size_t p;
+ size_t q;
+ size_t filt_len;
+ std::vector<size_t> in_index;
+ std::vector<size_t> out_path;
+ std::vector<std::complex<float> *> partitions;
+
+ void initFilters(float bw);
+};
+
+#endif /* _RESAMPLER_H_ */
diff --git a/Transceiver52M/Synthesis.cpp b/Transceiver52M/Synthesis.cpp
new file mode 100644
index 0000000..262c638
--- /dev/null
+++ b/Transceiver52M/Synthesis.cpp
@@ -0,0 +1,121 @@
+/*
+ * Polyphase synthesis filter
+ *
+ * Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include <cstdio>
+#include <iostream>
+
+#include "Synthesis.h"
+
+extern "C" {
+#include "fft.h"
+#include "convolve.h"
+}
+
+static void interleave(float **in, size_t ilen,
+ float *out, size_t m)
+{
+ size_t i, n;
+
+ for (i = 0; i < ilen; i++) {
+ for (n = 0; n < m; n++) {
+ out[2 * (i * m + n) + 0] = in[n][2 * i + 0];
+ out[2 * (i * m + n) + 1] = in[n][2 * i + 1];
+ }
+ }
+}
+
+size_t Synthesis::inputLen() const
+{
+ return blockLen;
+}
+
+size_t Synthesis::outputLen() const
+{
+ return blockLen * m;
+}
+
+float *Synthesis::inputBuffer(size_t chan) const
+{
+ if (chan >= m)
+ return NULL;
+
+ return hOutputs[chan];
+}
+
+bool Synthesis::resetBuffer(size_t chan)
+{
+ if (chan >= m)
+ return false;
+
+ memset(hOutputs[chan], 0, blockLen * 2 * sizeof(float));
+
+ return true;
+}
+
+/*
+ * Implementation based on material found in:
+ *
+ * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
+ * Prentice Hall, 2006."
+ */
+bool Synthesis::rotate(float *out, size_t len)
+{
+ size_t hSize = 2 * hLen * sizeof(float);
+
+ if (!checkLen(blockLen, len)) {
+ std::cout << "Length fail" << std::endl;
+ exit(1);
+ return false;
+ }
+
+ cxvec_fft(fftHandle);
+
+ /*
+ * Convolve through filterbank while applying and saving sample history
+ */
+ for (size_t i = 0; i < m; i++) {
+ memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
+ memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
+
+ convolve_real(hInputs[i], blockLen,
+ subFilters[i], hLen,
+ hOutputs[i], blockLen,
+ 0, blockLen, 1, 0);
+ }
+
+ /* Interleave into output vector */
+ interleave(hOutputs, blockLen, out, m);
+
+ return true;
+}
+
+Synthesis::Synthesis(size_t m, size_t blockLen, size_t hLen)
+ : ChannelizerBase(m, blockLen, hLen)
+{
+}
+
+Synthesis::~Synthesis()
+{
+}
diff --git a/Transceiver52M/Synthesis.h b/Transceiver52M/Synthesis.h
new file mode 100644
index 0000000..4100283
--- /dev/null
+++ b/Transceiver52M/Synthesis.h
@@ -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_ */
diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp
new file mode 100644
index 0000000..d6ddce8
--- /dev/null
+++ b/Transceiver52M/Transceiver.cpp
@@ -0,0 +1,1128 @@
+/*
+* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <iomanip> // std::setprecision
+#include <fstream>
+#include "Transceiver.h"
+#include <Logger.h>
+
+extern "C" {
+#include "osmo_signal.h"
+}
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+using namespace GSM;
+
+#define USB_LATENCY_INTRVL 10,0
+
+/* Number of running values use in noise average */
+#define NOISE_CNT 20
+
+TransceiverState::TransceiverState()
+ : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
+{
+ for (int i = 0; i < 8; i++) {
+ chanType[i] = Transceiver::NONE;
+ fillerModulus[i] = 26;
+ chanResponse[i] = NULL;
+ DFEForward[i] = NULL;
+ DFEFeedback[i] = NULL;
+
+ for (int n = 0; n < 102; n++)
+ fillerTable[n][i] = NULL;
+ }
+}
+
+TransceiverState::~TransceiverState()
+{
+ for (int i = 0; i < 8; i++) {
+ delete chanResponse[i];
+ delete DFEForward[i];
+ delete DFEFeedback[i];
+
+ for (int n = 0; n < 102; n++)
+ delete fillerTable[n][i];
+ }
+}
+
+bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
+{
+ signalVector *burst;
+
+ if ((sps != 1) && (sps != 4))
+ return false;
+
+ for (size_t n = 0; n < 8; n++) {
+ for (size_t i = 0; i < 102; i++) {
+ switch (filler) {
+ case FILLER_DUMMY:
+ burst = generateDummyBurst(sps, n);
+ break;
+ case FILLER_NORM_RAND:
+ burst = genRandNormalBurst(rtsc, sps, n);
+ break;
+ case FILLER_EDGE_RAND:
+ burst = generateEdgeBurst(rtsc);
+ break;
+ case FILLER_ACCESS_RAND:
+ burst = genRandAccessBurst(rach_delay, sps, n);
+ break;
+ case FILLER_ZERO:
+ default:
+ burst = generateEmptyBurst(sps, n);
+ }
+
+ scaleVector(*burst, scale);
+ fillerTable[i][n] = burst;
+ }
+
+ if ((filler == FILLER_NORM_RAND) ||
+ (filler == FILLER_EDGE_RAND)) {
+ chanType[n] = TSC;
+ }
+ }
+
+ return false;
+}
+
+Transceiver::Transceiver(int wBasePort,
+ const char *TRXAddress,
+ const char *GSMcoreAddress,
+ size_t tx_sps, size_t rx_sps, size_t chans,
+ GSM::Time wTransmitLatency,
+ RadioInterface *wRadioInterface,
+ double wRssiOffset)
+ : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
+ mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
+ mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
+ rssiOffset(wRssiOffset), sig_cbfn(NULL),
+ mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
+ mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
+ mWriteBurstToDiskMask(0)
+{
+ txFullScale = mRadioInterface->fullScaleInputValue();
+ rxFullScale = mRadioInterface->fullScaleOutputValue();
+
+ for (int i = 0; i < 8; i++) {
+ for (int j = 0; j < 8; j++)
+ mHandover[i][j] = false;
+ }
+}
+
+Transceiver::~Transceiver()
+{
+ stop();
+
+ sigProcLibDestroy();
+
+ for (size_t i = 0; i < mChans; i++) {
+ mControlServiceLoopThreads[i]->cancel();
+ mControlServiceLoopThreads[i]->join();
+ delete mControlServiceLoopThreads[i];
+
+ mTxPriorityQueues[i].clear();
+ delete mCtrlSockets[i];
+ delete mDataSockets[i];
+ }
+}
+
+/*
+ * Initialize transceiver
+ *
+ * Start or restart the control loop. Any further control is handled through the
+ * socket API. Randomize the central radio clock set the downlink burst
+ * counters. Note that the clock will not update until the radio starts, but we
+ * are still expected to report clock indications through control channel
+ * activity.
+ */
+bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge)
+{
+ int d_srcport, d_dstport, c_srcport, c_dstport;
+
+ if (!mChans) {
+ LOG(ALERT) << "No channels assigned";
+ return false;
+ }
+
+ if (!sigProcLibSetup()) {
+ LOG(ALERT) << "Failed to initialize signal processing library";
+ return false;
+ }
+
+ mEdge = edge;
+
+ mDataSockets.resize(mChans);
+ mCtrlSockets.resize(mChans);
+ mControlServiceLoopThreads.resize(mChans);
+ mTxPriorityQueueServiceLoopThreads.resize(mChans);
+ mRxServiceLoopThreads.resize(mChans);
+
+ mTxPriorityQueues.resize(mChans);
+ mReceiveFIFO.resize(mChans);
+ mStates.resize(mChans);
+
+ /* Filler table retransmissions - support only on channel 0 */
+ if (filler == FILLER_DUMMY)
+ mStates[0].mRetrans = true;
+
+ /* Setup sockets */
+ for (size_t i = 0; i < mChans; i++) {
+ c_srcport = mBasePort + 2 * i + 1;
+ c_dstport = mBasePort + 2 * i + 101;
+ d_srcport = mBasePort + 2 * i + 2;
+ d_dstport = mBasePort + 2 * i + 102;
+
+ mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
+ mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
+ }
+
+ /* Randomize the central clock */
+ GSM::Time startTime(random() % gHyperframe, 0);
+ mRadioInterface->getClock()->set(startTime);
+ mTransmitDeadlineClock = startTime;
+ mLastClockUpdateTime = startTime;
+ mLatencyUpdateTime = startTime;
+
+ /* Start control threads */
+ for (size_t i = 0; i < mChans; i++) {
+ TransceiverChannel *chan = new TransceiverChannel(this, i);
+ mControlServiceLoopThreads[i] = new Thread(32768);
+ mControlServiceLoopThreads[i]->start((void * (*)(void*))
+ ControlServiceLoopAdapter, (void*) chan);
+
+ if (i && filler == FILLER_DUMMY)
+ filler = FILLER_ZERO;
+
+ mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
+ }
+
+ 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
+ *
+ * Submit command(s) to the radio device to commence streaming samples and
+ * launch threads to handle sample I/O. Re-synchronize the transmit burst
+ * counters to the central radio clock here as well.
+ */
+bool Transceiver::start()
+{
+ ScopedLock lock(mLock);
+
+ if (mOn) {
+ LOG(ERR) << "Transceiver already running";
+ return true;
+ }
+
+ LOG(NOTICE) << "Starting the transceiver";
+
+ GSM::Time time = mRadioInterface->getClock()->get();
+ mTransmitDeadlineClock = time;
+ mLastClockUpdateTime = time;
+ mLatencyUpdateTime = time;
+
+ if (!mRadioInterface->start()) {
+ LOG(ALERT) << "Device failed to start";
+ return false;
+ }
+
+ /* Device is running - launch I/O threads */
+ mRxLowerLoopThread = new Thread(32768);
+ mTxLowerLoopThread = new Thread(32768);
+ mTxLowerLoopThread->start((void * (*)(void*))
+ TxLowerLoopAdapter,(void*) this);
+ mRxLowerLoopThread->start((void * (*)(void*))
+ RxLowerLoopAdapter,(void*) this);
+
+ /* Launch uplink and downlink burst processing threads */
+ for (size_t i = 0; i < mChans; i++) {
+ TransceiverChannel *chan = new TransceiverChannel(this, i);
+ mRxServiceLoopThreads[i] = new Thread(32768);
+ mRxServiceLoopThreads[i]->start((void * (*)(void*))
+ RxUpperLoopAdapter, (void*) chan);
+
+ chan = new TransceiverChannel(this, i);
+ mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
+ mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
+ TxUpperLoopAdapter, (void*) chan);
+ }
+
+ mForceClockInterface = true;
+ mOn = true;
+ return true;
+}
+
+/*
+ * Stop the transceiver
+ *
+ * Perform stopping by disabling receive streaming and issuing cancellation
+ * requests to running threads. Most threads will timeout and terminate once
+ * device is disabled, but the transmit loop may block waiting on the central
+ * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
+ * makes it to the thread cancellation point.
+ */
+void Transceiver::stop()
+{
+ ScopedLock lock(mLock);
+
+ if (!mOn)
+ return;
+
+ LOG(NOTICE) << "Stopping the transceiver";
+ mTxLowerLoopThread->cancel();
+ mRxLowerLoopThread->cancel();
+ mTxLowerLoopThread->join();
+ mRxLowerLoopThread->join();
+ delete mTxLowerLoopThread;
+ delete mRxLowerLoopThread;
+
+ for (size_t i = 0; i < mChans; i++) {
+ mRxServiceLoopThreads[i]->cancel();
+ mTxPriorityQueueServiceLoopThreads[i]->cancel();
+ }
+
+ LOG(INFO) << "Stopping the device";
+ mRadioInterface->stop();
+
+ for (size_t i = 0; i < mChans; i++) {
+ mRxServiceLoopThreads[i]->join();
+ mTxPriorityQueueServiceLoopThreads[i]->join();
+ delete mRxServiceLoopThreads[i];
+ delete mTxPriorityQueueServiceLoopThreads[i];
+
+ mTxPriorityQueues[i].clear();
+ }
+
+ mOn = false;
+ LOG(NOTICE) << "Transceiver stopped";
+}
+
+void Transceiver::addRadioVector(size_t chan, BitVector &bits,
+ int RSSI, GSM::Time &wTime)
+{
+ signalVector *burst;
+ radioVector *radio_burst;
+
+ if (chan >= mTxPriorityQueues.size()) {
+ LOG(ALERT) << "Invalid channel " << chan;
+ return;
+ }
+
+ if (wTime.TN() > 7) {
+ LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
+ return;
+ }
+
+ /* Use the number of bits as the EDGE burst indicator */
+ if (bits.size() == EDGE_BURST_NBITS)
+ burst = modulateEdgeBurst(bits, mSPSTx);
+ else
+ burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
+
+ scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
+
+ radio_burst = new radioVector(wTime, burst);
+
+ mTxPriorityQueues[chan].write(radio_burst);
+}
+
+void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
+{
+ int TN, modFN;
+ TransceiverState *state = &mStates[chan];
+
+ TN = burst->getTime().TN();
+ modFN = burst->getTime().FN() % state->fillerModulus[TN];
+
+ delete state->fillerTable[modFN][TN];
+ state->fillerTable[modFN][TN] = burst->getVector();
+ burst->setVector(NULL);
+}
+
+void Transceiver::pushRadioVector(GSM::Time &nowTime)
+{
+ int TN, modFN;
+ radioVector *burst;
+ TransceiverState *state;
+ std::vector<signalVector *> bursts(mChans);
+ std::vector<bool> zeros(mChans);
+ std::vector<bool> filler(mChans, true);
+
+ for (size_t i = 0; i < mChans; i ++) {
+ state = &mStates[i];
+
+ while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
+ LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->USRP interface ("
+ << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
+ if (state->mRetrans)
+ updateFillerTable(i, burst);
+ delete burst;
+ }
+
+ TN = nowTime.TN();
+ modFN = nowTime.FN() % state->fillerModulus[TN];
+
+ bursts[i] = state->fillerTable[modFN][TN];
+ zeros[i] = state->chanType[TN] == NONE;
+
+ if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
+ bursts[i] = burst->getVector();
+
+ if (state->mRetrans) {
+ updateFillerTable(i, burst);
+ } else {
+ burst->setVector(NULL);
+ filler[i] = false;
+ }
+
+ delete burst;
+ }
+ }
+
+ mRadioInterface->driveTransmitRadio(bursts, zeros);
+
+ for (size_t i = 0; i < mChans; i++) {
+ if (!filler[i])
+ delete bursts[i];
+ }
+}
+
+void Transceiver::setModulus(size_t timeslot, size_t chan)
+{
+ TransceiverState *state = &mStates[chan];
+
+ switch (state->chanType[timeslot]) {
+ case NONE:
+ case I:
+ case II:
+ case III:
+ case FILL:
+ state->fillerModulus[timeslot] = 26;
+ break;
+ case IV:
+ case VI:
+ case V:
+ state->fillerModulus[timeslot] = 51;
+ break;
+ //case V:
+ case VII:
+ state->fillerModulus[timeslot] = 102;
+ break;
+ case XIII:
+ state->fillerModulus[timeslot] = 52;
+ break;
+ default:
+ break;
+ }
+}
+
+
+CorrType Transceiver::expectedCorrType(GSM::Time currTime,
+ 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 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,
+ 3,3,3,3,0,0,0,0,0,0,1,1,1,1,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 sdcch8_subslot[102] = { 5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,
+ 1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,4,4,4,4 };
+ TransceiverState *state = &mStates[chan];
+ unsigned burstTN = currTime.TN();
+ unsigned burstFN = currTime.FN();
+ int subch;
+
+ switch (state->chanType[burstTN]) {
+ case NONE:
+ return OFF;
+ break;
+ case FILL:
+ return IDLE;
+ break;
+ case I:
+ // TODO: Are we expecting RACH on an IDLE frame?
+/* if (burstFN % 26 == 25)
+ return IDLE;*/
+ if (mHandover[burstTN][0])
+ return RACH;
+ return TSC;
+ break;
+ case II:
+ subch = tchh_subslot[burstFN % 26];
+ if (subch == 1)
+ return IDLE;
+ if (mHandover[burstTN][0])
+ return RACH;
+ return TSC;
+ break;
+ case III:
+ subch = tchh_subslot[burstFN % 26];
+ if (mHandover[burstTN][subch])
+ return RACH;
+ return TSC;
+ break;
+ case IV:
+ case VI:
+ return RACH;
+ break;
+ case V: {
+ int mod51 = burstFN % 51;
+ if ((mod51 <= 36) && (mod51 >= 14))
+ return RACH;
+ else if ((mod51 == 4) || (mod51 == 5))
+ return RACH;
+ else if ((mod51 == 45) || (mod51 == 46))
+ return RACH;
+ else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
+ return RACH;
+ else
+ return TSC;
+ break;
+ }
+ case VII:
+ if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
+ return IDLE;
+ else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
+ return RACH;
+ else
+ return TSC;
+ break;
+ case XIII: {
+ int mod52 = burstFN % 52;
+ if ((mod52 == 12) || (mod52 == 38))
+ return RACH;
+ else if ((mod52 == 25) || (mod52 == 51))
+ return IDLE;
+ else
+ return TSC;
+ break;
+ }
+ case LOOPBACK:
+ if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
+ return IDLE;
+ else
+ return TSC;
+ break;
+ default:
+ return OFF;
+ break;
+ }
+}
+
+void writeToFile(radioVector *radio_burst, size_t chan)
+{
+ GSM::Time time = radio_burst->getTime();
+ std::ostringstream fname;
+ fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
+ std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
+ outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
+ outfile.close();
+}
+
+/*
+ * Pull bursts from the FIFO and handle according to the slot
+ * and burst correlation type. Equalzation is currently disabled.
+ */
+SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
+ double &timingOffset, double &noise,
+ size_t chan)
+{
+ int rc;
+ complex amp;
+ float toa, max = -1.0, avg = 0.0;
+ int max_i = -1;
+ signalVector *burst;
+ SoftVector *bits = NULL;
+ TransceiverState *state = &mStates[chan];
+ isRssiValid = false;
+
+ /* Blocking FIFO read */
+ radioVector *radio_burst = mReceiveFIFO[chan]->read();
+ if (!radio_burst)
+ return NULL;
+
+ /* Set time and determine correlation type */
+ GSM::Time time = radio_burst->getTime();
+ 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 */
+ /* bits 0-7 - chan 0 timeslots
+ * bits 8-15 - chan 1 timeslots */
+ if (mWriteBurstToDiskMask & ((1<<time.TN()) << (8*chan)))
+ writeToFile(radio_burst, chan);
+
+ /* No processing if the timeslot is off.
+ * Not even power level or noise calculation. */
+ if (type == OFF) {
+ delete radio_burst;
+ return NULL;
+ }
+
+ /* Select the diversity channel with highest energy */
+ for (size_t i = 0; i < radio_burst->chans(); i++) {
+ float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
+ if (pow > max) {
+ max = pow;
+ max_i = i;
+ }
+ avg += pow;
+ }
+
+ if (max_i < 0) {
+ LOG(ALERT) << "Received empty burst";
+ delete radio_burst;
+ return NULL;
+ }
+
+ /* Average noise on diversity paths and update global levels */
+ burst = radio_burst->getVector(max_i);
+ avg = sqrt(avg / radio_burst->chans());
+
+ wTime = time;
+ RSSI = 20.0 * log10(rxFullScale / avg);
+
+ /* RSSI estimation are valid */
+ isRssiValid = true;
+
+ if (type == IDLE) {
+ /* Update noise levels */
+ state->mNoises.insert(avg);
+ state->mNoiseLev = state->mNoises.avg();
+ noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
+
+ delete radio_burst;
+ return NULL;
+ } else {
+ /* Do not update noise levels */
+ noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
+ }
+
+ /* Detect normal or RACH bursts */
+ rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa,
+ (type==RACH)?mMaxExpectedDelayAB:mMaxExpectedDelayNB);
+
+ if (rc > 0) {
+ type = (CorrType) rc;
+ } else if (rc <= 0) {
+ if (rc == -SIGERR_CLIP) {
+ LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
+ } else if (rc != SIGERR_NONE) {
+ LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
+ }
+
+ delete radio_burst;
+ return NULL;
+ }
+
+ timingOffset = toa;
+
+ bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
+
+ delete radio_burst;
+ return bits;
+}
+
+void Transceiver::reset()
+{
+ for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
+ mTxPriorityQueues[i].clear();
+}
+
+
+#define MAX_PACKET_LENGTH 100
+
+/**
+ * Matches a buffer with a command.
+ * @param buf a buffer to look command in
+ * @param cmd a command to look in buffer
+ * @param params pointer to arguments, or NULL
+ * @return true if command matches, otherwise false
+ */
+static bool match_cmd(char *buf,
+ const char *cmd, char **params)
+{
+ size_t cmd_len = strlen(cmd);
+
+ /* Check a command itself */
+ if (strncmp(buf, cmd, cmd_len))
+ return false;
+
+ /* A command has arguments */
+ if (params != NULL) {
+ /* Make sure there is a space */
+ if (buf[cmd_len] != ' ')
+ return false;
+
+ /* Update external pointer */
+ *params = buf + cmd_len + 1;
+ }
+
+ return true;
+}
+
+void Transceiver::driveControl(size_t chan)
+{
+ char buffer[MAX_PACKET_LENGTH + 1];
+ char response[MAX_PACKET_LENGTH + 1];
+ char *command, *params;
+ int msgLen;
+
+ /* Attempt to read from control socket */
+ msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
+ if (msgLen < 1)
+ return;
+
+ /* Zero-terminate received string */
+ buffer[msgLen] = '\0';
+
+ /* Verify a command signature */
+ if (strncmp(buffer, "CMD ", 4)) {
+ LOG(WARNING) << "bogus message on control interface";
+ return;
+ }
+
+ /* Set command pointer */
+ command = buffer + 4;
+ LOG(INFO) << "command is " << command;
+
+ if (match_cmd(command, "POWEROFF", NULL)) {
+ stop();
+ sprintf(response,"RSP POWEROFF 0");
+ } else if (match_cmd(command, "POWERON", NULL)) {
+ if (!start()) {
+ sprintf(response,"RSP POWERON 1");
+ } else {
+ sprintf(response,"RSP POWERON 0");
+ for (int i = 0; i < 8; i++) {
+ for (int j = 0; j < 8; j++)
+ mHandover[i][j] = false;
+ }
+ }
+ } else if (match_cmd(command, "HANDOVER", &params)) {
+ unsigned ts = 0, ss = 0;
+ sscanf(params, "%u %u", &ts, &ss);
+ if (ts > 7 || ss > 7) {
+ sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
+ } else {
+ mHandover[ts][ss] = true;
+ sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
+ }
+ } else if (match_cmd(command, "NOHANDOVER", &params)) {
+ unsigned ts = 0, ss = 0;
+ sscanf(params, "%u %u", &ts, &ss);
+ if (ts > 7 || ss > 7) {
+ sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
+ } else {
+ mHandover[ts][ss] = false;
+ sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
+ }
+ } else if (match_cmd(command, "SETMAXDLY", &params)) {
+ //set expected maximum time-of-arrival
+ int maxDelay;
+ sscanf(params, "%d", &maxDelay);
+ mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
+ sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
+ } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
+ //set expected maximum time-of-arrival
+ int maxDelay;
+ sscanf(params, "%d", &maxDelay);
+ mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
+ sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
+ } else if (match_cmd(command, "SETRXGAIN", &params)) {
+ //set expected maximum time-of-arrival
+ int newGain;
+ sscanf(params, "%d", &newGain);
+ newGain = mRadioInterface->setRxGain(newGain, chan);
+ sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
+ } else if (match_cmd(command, "NOISELEV", NULL)) {
+ if (mOn) {
+ float lev = mStates[chan].mNoiseLev;
+ sprintf(response,"RSP NOISELEV 0 %d",
+ (int) round(20.0 * log10(rxFullScale / lev)));
+ }
+ else {
+ sprintf(response,"RSP NOISELEV 1 0");
+ }
+ } else if (match_cmd(command, "SETPOWER", &params)) {
+ int power;
+ sscanf(params, "%d", &power);
+ power = mRadioInterface->setPowerAttenuation(power, chan);
+ mStates[chan].mPower = power;
+ sprintf(response, "RSP SETPOWER 0 %d", power);
+ } else if (match_cmd(command, "ADJPOWER", &params)) {
+ int power, step;
+ sscanf(params, "%d", &step);
+ power = mStates[chan].mPower + step;
+ power = mRadioInterface->setPowerAttenuation(power, chan);
+ mStates[chan].mPower = power;
+ sprintf(response, "RSP ADJPOWER 0 %d", power);
+ } else if (match_cmd(command, "RXTUNE", &params)) {
+ // tune receiver
+ int freqKhz;
+ sscanf(params, "%d", &freqKhz);
+ mRxFreq = freqKhz * 1e3;
+ if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
+ LOG(ALERT) << "RX failed to tune";
+ sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
+ }
+ else
+ sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
+ } else if (match_cmd(command, "TXTUNE", &params)) {
+ // tune txmtr
+ int freqKhz;
+ sscanf(params, "%d", &freqKhz);
+ mTxFreq = freqKhz * 1e3;
+ if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
+ LOG(ALERT) << "TX failed to tune";
+ sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
+ }
+ else
+ sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
+ } else if (match_cmd(command, "SETTSC", &params)) {
+ // set TSC
+ unsigned TSC;
+ sscanf(params, "%u", &TSC);
+ if (TSC > 7) {
+ sprintf(response, "RSP SETTSC 1 %d", TSC);
+ } else {
+ LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
+ mTSC = TSC;
+ sprintf(response,"RSP SETTSC 0 %d", TSC);
+ }
+ } else if (match_cmd(command, "SETSLOT", &params)) {
+ // set slot type
+ int corrCode;
+ int timeslot;
+ sscanf(params, "%d %d", &timeslot, &corrCode);
+ if ((timeslot < 0) || (timeslot > 7)) {
+ LOG(WARNING) << "bogus message on control interface";
+ sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
+ return;
+ }
+ mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
+ setModulus(timeslot, chan);
+ sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
+ } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
+ // debug command! may change or disapear without notice
+ // set a mask which bursts to dump to disk
+ int mask;
+ sscanf(params, "%d", &mask);
+ mWriteBurstToDiskMask = mask;
+ sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
+ } else {
+ LOG(WARNING) << "bogus command " << command << " on control interface.";
+ sprintf(response,"RSP ERR 1");
+ }
+
+ mCtrlSockets[chan]->write(response, strlen(response) + 1);
+}
+
+bool Transceiver::driveTxPriorityQueue(size_t chan)
+{
+ int burstLen;
+ char buffer[EDGE_BURST_NBITS + 50];
+
+ // check data socket
+ size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
+
+ 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";
+ return false;
+ }
+
+ int timeSlot = (int) buffer[0];
+ uint64_t frameNum = 0;
+ for (int i = 0; i < 4; i++)
+ frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
+
+ LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
+
+ int RSSI = (int) buffer[5];
+ BitVector newBurst(burstLen);
+ BitVector::iterator itr = newBurst.begin();
+ char *bufferItr = buffer+6;
+ while (itr < newBurst.end())
+ *itr++ = *bufferItr++;
+
+ GSM::Time currTime = GSM::Time(frameNum,timeSlot);
+
+ addRadioVector(chan, newBurst, RSSI, currTime);
+
+ return true;
+
+
+}
+
+void Transceiver::driveReceiveRadio()
+{
+ int rc = mRadioInterface->driveReceiveRadio();
+ if (rc == 0) {
+ usleep(100000);
+ } else if (rc < 0) {
+ LOG(FATAL) << "radio Interface receive failed, requesting stop.";
+ osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this);
+ } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
+ mForceClockInterface = false;
+ writeClockInterface();
+ }
+}
+
+void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
+ double rssi, double noise, double toa)
+{
+ LOG(DEBUG) << std::fixed << std::right
+ << " chan: " << chan
+ << " time: " << time
+ << " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
+ << "dBFS/" << std::setw(6) << -dbm << "dBm"
+ << " noise: " << std::setw(5) << std::setprecision(1) << noise
+ << "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm"
+ << " TOA: " << std::setw(5) << std::setprecision(2) << toa
+ << " bits: " << *burst;
+}
+
+void Transceiver::driveReceiveFIFO(size_t chan)
+{
+ SoftVector *rxBurst = NULL;
+ double RSSI; // in dBFS
+ double dBm; // in dBm
+ double TOA; // in symbols
+ int TOAint; // in 1/256 symbols
+ double noise; // noise level in dBFS
+ GSM::Time burstTime;
+ bool isRssiValid; // are RSSI, noise and burstTime valid
+ unsigned nbits = gSlotLen;
+
+ rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
+ if (!rxBurst)
+ return;
+
+ // Convert -1..+1 soft bits to 0..1 soft bits
+ vectorSlicer(rxBurst);
+
+ /*
+ * EDGE demodulator returns 444 (148 * 3) bits
+ */
+ if (rxBurst->size() == gSlotLen * 3)
+ nbits = gSlotLen * 3;
+
+ dBm = RSSI + rssiOffset;
+ logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
+
+ TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
+
+ char burstString[nbits + 10];
+ burstString[0] = burstTime.TN();
+ for (int i = 0; i < 4; i++)
+ burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
+ burstString[5] = (int)dBm;
+ burstString[6] = (TOAint >> 8) & 0x0ff;
+ burstString[7] = TOAint & 0x0ff;
+ SoftVector::iterator burstItr = rxBurst->begin();
+
+ for (unsigned i = 0; i < nbits; i++)
+ burstString[8 + i] = (char) round((*burstItr++) * 255.0);
+
+ burstString[nbits + 9] = '\0';
+ delete rxBurst;
+
+ mDataSockets[chan]->write(burstString, nbits + 10);
+}
+
+void Transceiver::driveTxFIFO()
+{
+
+ /**
+ Features a carefully controlled latency mechanism, to
+ assure that transmit packets arrive at the radio/USRP
+ before they need to be transmitted.
+
+ Deadline clock indicates the burst that needs to be
+ pushed into the FIFO right NOW. If transmit queue does
+ not have a burst, stick in filler data.
+ */
+
+
+ RadioClock *radioClock = (mRadioInterface->getClock());
+
+ if (mOn) {
+ //radioClock->wait(); // wait until clock updates
+ LOG(DEBUG) << "radio clock " << radioClock->get();
+ while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
+ // if underrun, then we're not providing bursts to radio/USRP fast
+ // enough. Need to increase latency by one GSM frame.
+ if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
+ if (mRadioInterface->isUnderrun()) {
+ // only update latency at the defined frame interval
+ if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
+ mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
+ LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
+ << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
+ mLatencyUpdateTime = radioClock->get();
+ }
+ }
+ else {
+ // if underrun hasn't occurred in the last sec (216 frames) drop
+ // transmit latency by a timeslot
+ if (mTransmitLatency > mRadioInterface->minLatency()) {
+ if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
+ mTransmitLatency.decTN();
+ LOG(INFO) << "reduced latency: " << mTransmitLatency;
+ mLatencyUpdateTime = radioClock->get();
+ }
+ }
+ }
+ }
+ // time to push burst to transmit FIFO
+ pushRadioVector(mTransmitDeadlineClock);
+ mTransmitDeadlineClock.incTN();
+ }
+ }
+
+ radioClock->wait();
+}
+
+
+
+void Transceiver::writeClockInterface()
+{
+ char command[50];
+ // FIXME -- This should be adaptive.
+ sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
+
+ LOG(INFO) << "ClockInterface: sending " << command;
+
+ mClockSocket.write(command, strlen(command) + 1);
+
+ mLastClockUpdateTime = mTransmitDeadlineClock;
+
+}
+
+void *RxUpperLoopAdapter(TransceiverChannel *chan)
+{
+ char thread_name[16];
+ Transceiver *trx = chan->trx;
+ size_t num = chan->num;
+
+ delete chan;
+
+ snprintf(thread_name, 16, "RxUpper%zu", num);
+ set_selfthread_name(thread_name);
+
+ trx->setPriority(0.42);
+
+ while (1) {
+ trx->driveReceiveFIFO(num);
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+void *RxLowerLoopAdapter(Transceiver *transceiver)
+{
+ set_selfthread_name("RxLower");
+
+ transceiver->setPriority(0.45);
+
+ while (1) {
+ transceiver->driveReceiveRadio();
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+void *TxLowerLoopAdapter(Transceiver *transceiver)
+{
+ set_selfthread_name("TxLower");
+
+ transceiver->setPriority(0.44);
+
+ while (1) {
+ transceiver->driveTxFIFO();
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+void *ControlServiceLoopAdapter(TransceiverChannel *chan)
+{
+ char thread_name[16];
+ Transceiver *trx = chan->trx;
+ size_t num = chan->num;
+
+ delete chan;
+
+ snprintf(thread_name, 16, "CtrlService%zu", num);
+ set_selfthread_name(thread_name);
+
+ while (1) {
+ trx->driveControl(num);
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+void *TxUpperLoopAdapter(TransceiverChannel *chan)
+{
+ char thread_name[16];
+ Transceiver *trx = chan->trx;
+ size_t num = chan->num;
+
+ delete chan;
+
+ snprintf(thread_name, 16, "TxUpper%zu", num);
+ set_selfthread_name(thread_name);
+
+ trx->setPriority(0.40);
+
+ while (1) {
+ trx->driveTxPriorityQueue(num);
+ pthread_testcancel();
+ }
+ return NULL;
+}
diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h
new file mode 100644
index 0000000..e250adc
--- /dev/null
+++ b/Transceiver52M/Transceiver.h
@@ -0,0 +1,282 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "radioInterface.h"
+#include "Interthread.h"
+#include "GSMCommon.h"
+#include "Sockets.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+extern "C" {
+#include <osmocom/core/signal.h>
+#include "config_defs.h"
+}
+
+class Transceiver;
+
+/** Channel descriptor for transceiver object and channel number pair */
+struct TransceiverChannel {
+ TransceiverChannel(Transceiver *trx, int num)
+ {
+ this->trx = trx;
+ this->num = num;
+ }
+
+ ~TransceiverChannel()
+ {
+ }
+
+ Transceiver *trx;
+ size_t num;
+};
+
+/** Internal transceiver state variables */
+struct TransceiverState {
+ TransceiverState();
+ ~TransceiverState();
+
+ /* Initialize a multiframe slot in the filler table */
+ bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
+
+ int chanType[8];
+
+ /* Last timestamp of each timeslot's channel estimate */
+ GSM::Time chanEstimateTime[8];
+
+ /* The filler table */
+ signalVector *fillerTable[102][8];
+ int fillerModulus[8];
+ bool mRetrans;
+
+ /* Most recent channel estimate of all timeslots */
+ signalVector *chanResponse[8];
+
+ /* Most recent DFE feedback filter of all timeslots */
+ signalVector *DFEForward[8];
+ signalVector *DFEFeedback[8];
+
+ /* Most recent SNR, timing, and channel amplitude estimates */
+ float SNRestimate[8];
+ float chanRespOffset[8];
+ complex chanRespAmplitude[8];
+
+ /* Received noise energy levels */
+ float mNoiseLev;
+ noiseVector mNoises;
+
+ /* Shadowed downlink attenuation */
+ int mPower;
+};
+
+/** The Transceiver class, responsible for physical layer of basestation */
+class Transceiver {
+public:
+ /** Transceiver constructor
+ @param wBasePort base port number of UDP sockets
+ @param TRXAddress IP address of the TRX, as a string
+ @param GSMcoreAddress IP address of the GSM core, as a string
+ @param wSPS number of samples per GSM symbol
+ @param wTransmitLatency initial setting of transmit latency
+ @param radioInterface associated radioInterface object
+ */
+ Transceiver(int wBasePort,
+ const char *TRXAddress,
+ const char *GSMcoreAddress,
+ size_t tx_sps, size_t rx_sps, size_t chans,
+ GSM::Time wTransmitLatency,
+ RadioInterface *wRadioInterface,
+ double wRssiOffset);
+
+ /** Destructor */
+ ~Transceiver();
+
+ /** Start the control loop */
+ bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge);
+
+ /** attach the radioInterface receive FIFO */
+ bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
+ {
+ if (chan >= mReceiveFIFO.size())
+ return false;
+
+ mReceiveFIFO[chan] = wFIFO;
+ return true;
+ }
+
+ /** accessor for number of channels */
+ size_t numChans() const { return mChans; };
+
+ void setSignalHandler(osmo_signal_cbfn cbfn);
+
+ /** Codes for channel combinations */
+ typedef enum {
+ FILL, ///< Channel is transmitted, but unused
+ I, ///< TCH/FS
+ II, ///< TCH/HS, idle every other slot
+ III, ///< TCH/HS
+ IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
+ V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
+ VI, ///< CCCH+BCCH, uplink RACH
+ VII, ///< SDCCH/8 + SACCH/8
+ VIII, ///< TCH/F + FACCH/F + SACCH/M
+ IX, ///< TCH/F + SACCH/M
+ X, ///< TCH/FD + SACCH/MD
+ XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
+ XII, ///< PCCCH+PDTCH+PACCH+PTCCH
+ XIII, ///< PDTCH+PACCH+PTCCH
+ NONE, ///< Channel is inactive, default
+ LOOPBACK ///< similar go VII, used in loopback testing
+ } ChannelCombination;
+
+private:
+ int mBasePort;
+ std::string mLocalAddr;
+ std::string mRemoteAddr;
+
+ std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
+ std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
+ UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
+
+ std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
+ std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
+
+ std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
+ Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
+ Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
+ std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
+ std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
+
+ GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
+ GSM::Time mLatencyUpdateTime; ///< last time latency was updated
+ GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
+ GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
+
+ RadioInterface *mRadioInterface; ///< associated radioInterface object
+ double txFullScale; ///< full scale input to radio
+ double rxFullScale; ///< full scale output to radio
+
+ double rssiOffset; ///< RSSI to dBm conversion offset
+
+ osmo_signal_cbfn *sig_cbfn; ///< Registered Signal Handler to announce events.
+
+ /** modulate and add a burst to the transmit queue */
+ void addRadioVector(size_t chan, BitVector &bits,
+ int RSSI, GSM::Time &wTime);
+
+ /** Update filler table */
+ void updateFillerTable(size_t chan, radioVector *burst);
+
+ /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
+ void pushRadioVector(GSM::Time &nowTime);
+
+ /** Pull and demodulate a burst from the receive FIFO */
+ SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
+ double &timingOffset, double &noise,
+ size_t chan = 0);
+
+ /** Set modulus for specific timeslot */
+ void setModulus(size_t timeslot, size_t chan);
+
+ /** return the expected burst type for the specified timestamp */
+ CorrType expectedCorrType(GSM::Time currTime, size_t chan);
+
+ /** send messages over the clock socket */
+ void writeClockInterface(void);
+
+ int mSPSTx; ///< number of samples per Tx symbol
+ int mSPSRx; ///< number of samples per Rx symbol
+ size_t mChans;
+
+ bool mEdge;
+ bool mOn; ///< flag to indicate that transceiver is powered on
+ bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
+ bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
+ double mTxFreq; ///< the transmit frequency
+ double mRxFreq; ///< the receive frequency
+ unsigned mTSC; ///< the midamble sequence code
+ unsigned mMaxExpectedDelayAB; ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH)
+ unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
+ unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
+
+ std::vector<TransceiverState> mStates;
+
+ /** Start and stop I/O threads through the control socket API */
+ bool start();
+ void stop();
+
+ /** Protect destructor accessable stop call */
+ Mutex mLock;
+
+protected:
+ /** drive lower receive I/O and burst generation */
+ void driveReceiveRadio();
+
+ /** drive demodulation of GSM bursts */
+ void driveReceiveFIFO(size_t chan);
+
+ /** drive transmission of GSM bursts */
+ void driveTxFIFO();
+
+ /** drive handling of control messages from GSM core */
+ void driveControl(size_t chan);
+
+ /**
+ drive modulation and sorting of GSM bursts from GSM core
+ @return true if a burst was transferred successfully
+ */
+ bool driveTxPriorityQueue(size_t chan);
+
+ friend void *RxUpperLoopAdapter(TransceiverChannel *);
+
+ friend void *TxUpperLoopAdapter(TransceiverChannel *);
+
+ friend void *RxLowerLoopAdapter(Transceiver *);
+
+ friend void *TxLowerLoopAdapter(Transceiver *);
+
+ friend void *ControlServiceLoopAdapter(TransceiverChannel *);
+
+
+ void reset();
+
+ /** set priority on current thread */
+ void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
+
+ void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
+ double rssi, double noise, double toa);
+};
+
+void *RxUpperLoopAdapter(TransceiverChannel *);
+
+/** Main drive threads */
+void *RxLowerLoopAdapter(Transceiver *);
+void *TxLowerLoopAdapter(Transceiver *);
+
+/** control message handler thread loop */
+void *ControlServiceLoopAdapter(TransceiverChannel *);
+
+/** transmit queueing thread loop */
+void *TxUpperLoopAdapter(TransceiverChannel *);
diff --git a/Transceiver52M/arch/Makefile.am b/Transceiver52M/arch/Makefile.am
new file mode 100644
index 0000000..14e6c82
--- /dev/null
+++ b/Transceiver52M/arch/Makefile.am
@@ -0,0 +1,8 @@
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS = common
+if ARCH_ARM
+SUBDIRS += arm
+else
+SUBDIRS += x86
+endif
diff --git a/Transceiver52M/arch/arm/Makefile.am b/Transceiver52M/arch/arm/Makefile.am
new file mode 100644
index 0000000..bc6da72
--- /dev/null
+++ b/Transceiver52M/arch/arm/Makefile.am
@@ -0,0 +1,22 @@
+if ARCH_ARM_A15
+ARCH_FLAGS = -mfpu=neon-vfpv4
+else
+ARCH_FLAGS = -mfpu=neon
+endif
+
+AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common
+AM_CCASFLAGS = $(ARCH_FLAGS)
+
+noinst_LTLIBRARIES = libarch.la
+
+libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
+
+libarch_la_SOURCES = \
+ convert.c \
+ convert_neon.S \
+ convolve.c \
+ convolve_neon.S \
+ scale.c \
+ scale_neon.S \
+ mult.c \
+ mult_neon.S
diff --git a/Transceiver52M/arch/arm/convert.c b/Transceiver52M/arch/arm/convert.c
new file mode 100644
index 0000000..c94a3d7
--- /dev/null
+++ b/Transceiver52M/arch/arm/convert.c
@@ -0,0 +1,85 @@
+/*
+ * NEON type conversions
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include "convert.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
+void neon_convert_si16_ps_4n(float *, const short *, int);
+
+void convert_init(void) {
+}
+
+/* 4*N 16-bit signed integer conversion with remainder */
+static void neon_convert_si16_ps(float *out,
+ const short *in,
+ int len)
+{
+ int start = len / 4 * 4;
+
+ neon_convert_si16_ps_4n(out, in, len >> 2);
+
+ for (int i = 0; i < len % 4; i++)
+ out[start + i] = (float) in[start + i];
+}
+
+/* 4*N 16-bit signed integer conversion with remainder */
+static void neon_convert_ps_si16(short *out,
+ const float *in,
+ const float *scale,
+ int len)
+{
+ int start = len / 4 * 4;
+
+ neon_convert_ps_si16_4n(out, in, scale, len >> 2);
+
+ for (int i = 0; i < len % 4; i++)
+ out[start + i] = (short) (in[start + i] * (*scale));
+}
+
+void convert_float_short(short *out, const float *in, float scale, int len)
+{
+#ifdef HAVE_NEON
+ float q[4] = { scale, scale, scale, scale };
+
+ if (len % 4)
+ neon_convert_ps_si16(out, in, q, len);
+ else
+ neon_convert_ps_si16_4n(out, in, q, len >> 2);
+#else
+ base_convert_float_short(out, in, scale, len);
+#endif
+}
+
+void convert_short_float(float *out, const short *in, int len)
+{
+#ifdef HAVE_NEON
+ if (len % 4)
+ neon_convert_si16_ps(out, in, len);
+ else
+ neon_convert_si16_ps_4n(out, in, len >> 2);
+#else
+ base_convert_short_float(out, in, len);
+#endif
+}
diff --git a/Transceiver52M/arch/arm/convert_neon.S b/Transceiver52M/arch/arm/convert_neon.S
new file mode 100644
index 0000000..842ed9f
--- /dev/null
+++ b/Transceiver52M/arch/arm/convert_neon.S
@@ -0,0 +1,51 @@
+/*
+ * NEON type conversions
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ .syntax unified
+ .text
+ .align 2
+ .global neon_convert_ps_si16_4n
+ .type neon_convert_ps_si16_4n, %function
+neon_convert_ps_si16_4n:
+ vld1.32 {q1}, [r2]
+.loop_fltint:
+ vld1.64 {d0-d1}, [r1]!
+ vmul.f32 q0, q1
+ vcvt.s32.f32 q2, q0
+ vqmovn.s32 d0, q2
+ vst1.64 {d0}, [r0]!
+ subs r3, #1
+ bne .loop_fltint
+ bx lr
+ .size neon_convert_ps_si16_4n, .-neon_convert_ps_si16_4n
+ .text
+ .align 2
+ .global neon_convert_si16_ps_4n
+ .type neon_convert_si16_ps_4n, %function
+neon_convert_si16_ps_4n:
+.loop_intflt:
+ vld1.64 {d0}, [r1]!
+ vmovl.s16 q1, d0
+ vcvt.f32.s32 q0, q1
+ vst1.64 {q0}, [r0]!
+ subs r2, #1
+ bne .loop_intflt
+ bx lr
+ .size neon_convert_si16_ps_4n, .-neon_convert_si16_ps_4n
+ .section .note.GNU-stack,"",%progbits
diff --git a/Transceiver52M/arch/arm/convolve.c b/Transceiver52M/arch/arm/convolve.c
new file mode 100644
index 0000000..912d0c2
--- /dev/null
+++ b/Transceiver52M/arch/arm/convolve.c
@@ -0,0 +1,146 @@
+/*
+ * NEON Convolution
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Forward declarations from base implementation */
+int _base_convolve_real(float *x, int x_len,
+ float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset);
+
+int _base_convolve_complex(float *x, int x_len,
+ float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset);
+
+int bounds_check(int x_len, int h_len, int y_len,
+ int start, int len, int step);
+
+#ifdef HAVE_NEON
+/* Calls into NEON assembler */
+void neon_conv_real4(float *x, float *h, float *y, int len);
+void neon_conv_real8(float *x, float *h, float *y, int len);
+void neon_conv_real12(float *x, float *h, float *y, int len);
+void neon_conv_real16(float *x, float *h, float *y, int len);
+void neon_conv_real20(float *x, float *h, float *y, int len);
+void mac_cx_neon4(float *x, float *h, float *y, int len);
+
+/* Complex-complex convolution */
+static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
+{
+ for (int i = 0; i < len; i++)
+ mac_cx_neon4(&x[2 * i], h, &y[2 * i], h_len >> 2);
+}
+#endif
+
+/* API: Initalize convolve module */
+void convolve_init(void)
+{
+ /* Stub */
+ return;
+}
+
+/* API: Aligned complex-real */
+int convolve_real(float *x, int x_len,
+ float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset)
+{
+ void (*conv_func)(float *, float *, float *, int) = NULL;
+
+ if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
+ return -1;
+
+ memset(y, 0, len * 2 * sizeof(float));
+
+#ifdef HAVE_NEON
+ if (step <= 4) {
+ switch (h_len) {
+ case 4:
+ conv_func = neon_conv_real4;
+ break;
+ case 8:
+ conv_func = neon_conv_real8;
+ break;
+ case 12:
+ conv_func = neon_conv_real12;
+ break;
+ case 16:
+ conv_func = neon_conv_real16;
+ break;
+ case 20:
+ conv_func = neon_conv_real20;
+ break;
+ }
+ }
+#endif
+ if (conv_func) {
+ conv_func(&x[2 * (-(h_len - 1) + start)],
+ h, y, len);
+ } else {
+ _base_convolve_real(x, x_len,
+ h, h_len,
+ y, y_len,
+ start, len, step, offset);
+ }
+
+ return len;
+}
+
+
+/* API: Aligned complex-complex */
+int convolve_complex(float *x, int x_len,
+ float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset)
+{
+ void (*conv_func)(float *, float *, float *, int, int) = NULL;
+
+ if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
+ return -1;
+
+ memset(y, 0, len * 2 * sizeof(float));
+
+#ifdef HAVE_NEON
+ if (step <= 4 && !(h_len % 4))
+ conv_func = neon_conv_cmplx_4n;
+#endif
+ if (conv_func) {
+ conv_func(&x[2 * (-(h_len - 1) + start)],
+ h, y, h_len, len);
+ } else {
+ _base_convolve_complex(x, x_len,
+ h, h_len,
+ y, y_len,
+ start, len, step, offset);
+ }
+
+ return len;
+}
diff --git a/Transceiver52M/arch/arm/convolve_neon.S b/Transceiver52M/arch/arm/convolve_neon.S
new file mode 100644
index 0000000..637d150
--- /dev/null
+++ b/Transceiver52M/arch/arm/convolve_neon.S
@@ -0,0 +1,277 @@
+/*
+ * NEON Convolution
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+ .syntax unified
+ .text
+ .align 2
+ .global neon_conv_real4
+ .type neon_conv_real4, %function
+neon_conv_real4:
+ push {r4, lr}
+ vpush {q4-q7}
+ vld2.32 {q0-q1}, [r1]
+ ldr r4, =8
+.neon_conv_loop4:
+ vld2.32 {q2-q3}, [r0], r4
+ vmul.f32 q4, q2, q0
+ vmul.f32 q5, q3, q0
+ vpadd.f32 d12, d8, d9
+ vpadd.f32 d13, d10, d11
+ vpadd.f32 d14, d12, d13
+ vst1.64 {d14}, [r2]!
+ subs r3, r3, #1
+ bne .neon_conv_loop4
+ vpop {q4-q7}
+ pop {r4, pc}
+ .size neon_conv_real4, .-neon_conv_real4
+ .align 2
+ .p2align 4,,15
+ .global neon_conv_real8
+ .type neon_conv_real8, %function
+neon_conv_real8:
+ push {r4-r5, lr}
+ vpush {q4-q7}
+ vld2.32 {q0-q1}, [r1]!
+ vld2.32 {q2-q3}, [r1]
+ add r4, r0, #32
+ ldr r5, =8
+.neon_conv_loop8:
+ vld2.32 {q4-q5}, [r0], r5
+ vld2.32 {q6-q7}, [r4], r5
+ vmul.f32 q8, q4, q0
+ vmul.f32 q9, q5, q0
+ vmul.f32 q10, q6, q2
+ vmul.f32 q11, q7, q2
+
+ vadd.f32 q12, q8, q10
+ vadd.f32 q13, q9, q11
+
+ vpadd.f32 d22, d24, d25
+ vpadd.f32 d23, d26, d27
+ vpadd.f32 d24, d22, d23
+ vst1.64 {d24}, [r2]!
+ subs r3, r3, #1
+ bne .neon_conv_loop8
+ vpop {q4-q7}
+ pop {r4-r5, pc}
+ .size neon_conv_real8, .-neon_conv_real8
+ .align 2
+ .global neon_conv_real12
+ .type neon_conv_real12, %function
+neon_conv_real12:
+ push {r4-r6, lr}
+ vpush {q4-q7}
+ vld2.32 {q0-q1}, [r1]!
+ vld2.32 {q2-q3}, [r1]!
+ vld2.32 {q4-q5}, [r1]!
+ add r4, r0, #32
+ add r5, r0, #64
+ ldr r6, =8
+.neon_conv_loop12:
+ vld2.32 {q6-q7}, [r0], r6
+ vld2.32 {q8-q9}, [r4], r6
+ vld2.32 {q10-q11}, [r5], r6
+#ifdef HAVE_NEON_FMA
+ vfma.f32 q1, q6, q0
+ vfma.f32 q3, q7, q0
+ vfma.f32 q1, q8, q2
+ vfma.f32 q3, q9, q2
+ vfma.f32 q1, q10, q4
+ vfma.f32 q3, q11, q4
+#else
+ vmul.f32 q12, q6, q0
+ vmul.f32 q13, q7, q0
+ vmul.f32 q14, q8, q2
+ vmul.f32 q15, q9, q2
+ vmul.f32 q1, q10, q4
+ vmul.f32 q3, q11, q4
+
+ vadd.f32 q5, q12, q14
+ vadd.f32 q6, q13, q15
+ vadd.f32 q1, q5, q1
+ vadd.f32 q3, q6, q3
+#endif
+ vpadd.f32 d2, d2, d3
+ vpadd.f32 d3, d6, d7
+ vpadd.f32 d6, d2, d3
+ vst1.64 {d6}, [r2]!
+ subs r3, r3, #1
+ bne .neon_conv_loop12
+ vpop {q4-q7}
+ pop {r4-r6, pc}
+ .size neon_conv_real12, .-neon_conv_real12
+ .align 2
+ .global neon_conv_real16
+ .type neon_conv_real16, %function
+neon_conv_real16:
+ push {r4-r7, lr}
+ vpush {q4-q7}
+ vld2.32 {q0-q1}, [r1]!
+ vld2.32 {q2-q3}, [r1]!
+ vld2.32 {q4-q5}, [r1]!
+ vld2.32 {q6-q7}, [r1]
+ add r4, r0, #32
+ add r5, r0, #64
+ add r6, r0, #96
+ ldr r7, =8
+.neon_conv_loop16:
+ vld2.32 {q8-q9}, [r0], r7
+ vld2.32 {q10-q11}, [r4], r7
+ vld2.32 {q12-q13}, [r5], r7
+ vld2.32 {q14-q15}, [r6], r7
+#ifdef HAVE_NEON_FMA
+ vmul.f32 q1, q8, q0
+ vmul.f32 q3, q9, q0
+ vfma.f32 q1, q10, q2
+ vfma.f32 q3, q11, q2
+ vfma.f32 q1, q12, q4
+ vfma.f32 q3, q13, q4
+ vfma.f32 q1, q14, q6
+ vfma.f32 q3, q15, q6
+#else
+ vmul.f32 q1, q8, q0
+ vmul.f32 q3, q9, q0
+ vmul.f32 q5, q10, q2
+ vmul.f32 q7, q11, q2
+ vmul.f32 q8, q12, q4
+ vmul.f32 q9, q13, q4
+ vmul.f32 q10, q14, q6
+ vmul.f32 q11, q15, q6
+
+ vadd.f32 q1, q1, q5
+ vadd.f32 q3, q3, q7
+ vadd.f32 q5, q8, q10
+ vadd.f32 q7, q9, q11
+ vadd.f32 q1, q1, q5
+ vadd.f32 q3, q3, q7
+#endif
+ vpadd.f32 d2, d2, d3
+ vpadd.f32 d3, d6, d7
+ vpadd.f32 d6, d2, d3
+ vst1.64 {d6}, [r2]!
+ subs r3, r3, #1
+ bne .neon_conv_loop16
+ vpop {q4-q7}
+ pop {r4-r7, pc}
+ .size neon_conv_real16, .-neon_conv_real16
+ .align 2
+ .global neon_conv_real20
+ .type neon_conv_real20, %function
+neon_conv_real20:
+ push {r4-r8, lr}
+ vpush {q4-q7}
+ vld2.32 {q0-q1}, [r1]!
+ vld2.32 {q2-q3}, [r1]!
+ vld2.32 {q4-q5}, [r1]!
+ vld2.32 {q6-q7}, [r1]!
+ vld2.32 {q8-q9}, [r1]
+ add r4, r0, #32
+ add r5, r0, #64
+ add r6, r0, #96
+ add r7, r0, #128
+ ldr r8, =8
+.neon_conv_loop20:
+ vld2.32 {q10-q11}, [r0], r8
+ vld2.32 {q12-q13}, [r4], r8
+ vld2.32 {q14-q15}, [r5], r8
+#ifdef HAVE_NEON_FMA
+ vmul.f32 q1, q10, q0
+ vfma.f32 q1, q12, q2
+ vfma.f32 q1, q14, q4
+ vmul.f32 q3, q11, q0
+ vfma.f32 q3, q13, q2
+ vfma.f32 q3, q15, q4
+
+ vld2.32 {q12-q13}, [r6], r8
+ vld2.32 {q14-q15}, [r7], r8
+
+ vfma.f32 q1, q12, q6
+ vfma.f32 q3, q13, q6
+ vfma.f32 q1, q14, q8
+ vfma.f32 q3, q15, q8
+#else
+ vmul.f32 q1, q10, q0
+ vmul.f32 q3, q12, q2
+ vmul.f32 q5, q14, q4
+ vmul.f32 q7, q11, q0
+ vmul.f32 q9, q13, q2
+ vmul.f32 q10, q15, q4
+ vadd.f32 q1, q1, q3
+ vadd.f32 q3, q7, q9
+ vadd.f32 q9, q1, q5
+ vadd.f32 q10, q3, q10
+
+ vld2.32 {q12-q13}, [r6], r8
+ vld2.32 {q14-q15}, [r7], r8
+
+ vmul.f32 q1, q12, q6
+ vmul.f32 q3, q13, q6
+ vmul.f32 q5, q14, q8
+ vmul.f32 q7, q15, q8
+ vadd.f32 q12, q1, q9
+ vadd.f32 q14, q3, q10
+ vadd.f32 q1, q12, q5
+ vadd.f32 q3, q14, q7
+#endif
+ vpadd.f32 d2, d2, d3
+ vpadd.f32 d3, d6, d7
+ vpadd.f32 d6, d2, d3
+ vst1.64 {d6}, [r2]!
+ subs r3, r3, #1
+ bne .neon_conv_loop20
+ vpop {q4-q7}
+ pop {r4-r8, pc}
+ .size neon_conv_real20, .-neon_conv_real20
+ .align 2
+ .global mac_cx_neon4
+ .type mac_cx_neon4, %function
+mac_cx_neon4:
+ push {r4, lr}
+ ldr r4, =32
+ veor q14, q14
+ veor q15, q15
+.neon_conv_loop_mac4:
+ vld2.32 {q0-q1}, [r0], r4
+ vld2.32 {q2-q3}, [r1]!
+
+ vmul.f32 q10, q0, q2
+ vmul.f32 q11, q1, q3
+ vmul.f32 q12, q0, q3
+ vmul.f32 q13, q2, q1
+ vsub.f32 q8, q10, q11
+ vadd.f32 q9, q12, q13
+
+ vadd.f32 q14, q8
+ vadd.f32 q15, q9
+ subs r3, #1
+ bne .neon_conv_loop_mac4
+
+ vld1.64 d0, [r2]
+ vpadd.f32 d28, d28, d29
+ vpadd.f32 d30, d30, d31
+ vpadd.f32 d1, d28, d30
+ vadd.f32 d1, d0
+ vst1.64 d1, [r2]
+ pop {r4, pc}
+ .size mac_cx_neon4, .-mac_cx_neon4
+ .section .note.GNU-stack,"",%progbits
diff --git a/Transceiver52M/arch/arm/mult.c b/Transceiver52M/arch/arm/mult.c
new file mode 100644
index 0000000..245be50
--- /dev/null
+++ b/Transceiver52M/arch/arm/mult.c
@@ -0,0 +1,56 @@
+/*
+ * NEON scaling
+ * Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include <mult.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+void neon_cmplx_mul_4n(float *, float *, float *, int);
+
+static void cmplx_mul_ps(float *out, float *a, float *b, int len)
+{
+ float ai, aq, bi, bq;
+
+ for (int i = 0; i < len; i++) {
+ ai = a[2 * i + 0];
+ aq = a[2 * i + 1];
+
+ bi = b[2 * i + 0];
+ bq = b[2 * i + 1];
+
+ out[2 * i + 0] = ai * bi - aq * bq;
+ out[2 * i + 1] = ai * bq + aq * bi;
+ }
+}
+
+void mul_complex(float *out, float *a, float *b, int len)
+{
+#ifdef HAVE_NEON
+ if (len % 4)
+ cmplx_mul_ps(out, a, b, len);
+ else
+ neon_cmplx_mul_4n(out, a, b, len >> 2);
+#else
+ cmplx_mul_ps(out, a, b, len);
+#endif
+}
diff --git a/Transceiver52M/arch/arm/mult_neon.S b/Transceiver52M/arch/arm/mult_neon.S
new file mode 100644
index 0000000..162846e
--- /dev/null
+++ b/Transceiver52M/arch/arm/mult_neon.S
@@ -0,0 +1,42 @@
+/*
+ * NEON complex multiplication
+ * Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ .syntax unified
+ .text
+ .align 2
+ .global neon_cmplx_mul_4n
+ .type neon_cmplx_mul_4n, %function
+neon_cmplx_mul_4n:
+ vpush {q4-q7}
+.loop_mul:
+ vld2.32 {q0-q1}, [r1]!
+ vld2.32 {q2-q3}, [r2]!
+ vmul.f32 q4, q0, q2
+ vmul.f32 q5, q1, q3
+ vmul.f32 q6, q0, q3
+ vmul.f32 q7, q2, q1
+ vsub.f32 q8, q4, q5
+ vadd.f32 q9, q6, q7
+ vst2.32 {q8-q9}, [r0]!
+ subs r3, #1
+ bne .loop_mul
+ vpop {q4-q7}
+ bx lr
+ .size neon_cmplx_mul_4n, .-neon_cmplx_mul_4n
+ .section .note.GNU-stack,"",%progbits
diff --git a/Transceiver52M/arch/arm/scale.c b/Transceiver52M/arch/arm/scale.c
new file mode 100644
index 0000000..2de13ff
--- /dev/null
+++ b/Transceiver52M/arch/arm/scale.c
@@ -0,0 +1,56 @@
+/*
+ * NEON scaling
+ * Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include <scale.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+void neon_scale_4n(float *, float *, float *, int);
+
+static void scale_ps(float *out, float *in, float *scale, int len)
+{
+ float ai, aq, bi, bq;
+
+ bi = scale[0];
+ bq = scale[1];
+
+ for (int i = 0; i < len; i++) {
+ ai = in[2 * i + 0];
+ aq = in[2 * i + 1];
+
+ out[2 * i + 0] = ai * bi - aq * bq;
+ out[2 * i + 1] = ai * bq + aq * bi;
+ }
+}
+
+void scale_complex(float *out, float *in, float* scale, int len)
+{
+#ifdef HAVE_NEON
+ if (len % 4)
+ scale_ps(out, in, scale, len);
+ else
+ neon_scale_4n(in, scale, out, len >> 2);
+#else
+ scale_ps(out, in, scale, len);
+#endif
+}
diff --git a/Transceiver52M/arch/arm/scale_neon.S b/Transceiver52M/arch/arm/scale_neon.S
new file mode 100644
index 0000000..a66fbe5
--- /dev/null
+++ b/Transceiver52M/arch/arm/scale_neon.S
@@ -0,0 +1,50 @@
+/*
+ * ARM NEON Scaling
+ * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ .syntax unified
+ .text
+ .align 2
+ .global neon_scale_4n
+ .type neon_scale_4n, %function
+neon_scale_4n:
+ push {r4, lr}
+ ldr r4, =32
+
+ vld1.64 d0, [r1]
+ vmov.32 s4, s1
+ vmov.32 s1, s0
+ vmov.64 d1, d0
+ vmov.32 s5, s4
+ vmov.64 d3, d2
+.loop_mul_const:
+ vld2.32 {q2-q3}, [r0], r4
+
+ vmul.f32 q8, q0, q2
+ vmul.f32 q9, q1, q3
+ vmul.f32 q10, q0, q3
+ vmul.f32 q11, q1, q2
+ vsub.f32 q8, q8, q9
+ vadd.f32 q9, q10, q11
+
+ vst2.32 {q8-q9}, [r2]!
+ subs r3, #1
+ bne .loop_mul_const
+ pop {r4, pc}
+ .size neon_scale_4n, .-neon_scale_4n
+ .section .note.GNU-stack,"",%progbits
diff --git a/Transceiver52M/arch/common/Makefile.am b/Transceiver52M/arch/common/Makefile.am
new file mode 100644
index 0000000..6b37906
--- /dev/null
+++ b/Transceiver52M/arch/common/Makefile.am
@@ -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
diff --git a/Transceiver52M/arch/common/convert.h b/Transceiver52M/arch/common/convert.h
new file mode 100644
index 0000000..73402b0
--- /dev/null
+++ b/Transceiver52M/arch/common/convert.h
@@ -0,0 +1,15 @@
+#ifndef _CONVERT_H_
+#define _CONVERT_H_
+
+void convert_float_short(short *out, const float *in, float scale, int len);
+
+void convert_short_float(float *out, const short *in, int len);
+
+void base_convert_float_short(short *out, const float *in,
+ float scale, int len);
+
+void base_convert_short_float(float *out, const short *in, int len);
+
+void convert_init(void);
+
+#endif /* _CONVERT_H_ */
diff --git a/Transceiver52M/arch/common/convert_base.c b/Transceiver52M/arch/common/convert_base.c
new file mode 100644
index 0000000..5251fb8
--- /dev/null
+++ b/Transceiver52M/arch/common/convert_base.c
@@ -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];
+}
+
diff --git a/Transceiver52M/arch/common/convolve.h b/Transceiver52M/arch/common/convolve.h
new file mode 100644
index 0000000..43db577
--- /dev/null
+++ b/Transceiver52M/arch/common/convolve.h
@@ -0,0 +1,32 @@
+#ifndef _CONVOLVE_H_
+#define _CONVOLVE_H_
+
+void *convolve_h_alloc(int num);
+
+int convolve_real(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset);
+
+int convolve_complex(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset);
+
+int base_convolve_real(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset);
+
+int base_convolve_complex(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset);
+
+void convolve_init(void);
+
+#endif /* _CONVOLVE_H_ */
diff --git a/Transceiver52M/arch/common/convolve_base.c b/Transceiver52M/arch/common/convolve_base.c
new file mode 100644
index 0000000..71453a1
--- /dev/null
+++ b/Transceiver52M/arch/common/convolve_base.c
@@ -0,0 +1,156 @@
+/*
+ * Convolution
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Base multiply and accumulate complex-real */
+static void mac_real(const float *x, const float *h, float *y)
+{
+ y[0] += x[0] * h[0];
+ y[1] += x[1] * h[0];
+}
+
+/* Base multiply and accumulate complex-complex */
+static void mac_cmplx(const float *x, const float *h, float *y)
+{
+ y[0] += x[0] * h[0] - x[1] * h[1];
+ y[1] += x[0] * h[1] + x[1] * h[0];
+}
+
+/* Base vector complex-complex multiply and accumulate */
+static void mac_real_vec_n(const float *x, const float *h, float *y,
+ int len, int step, int offset)
+{
+ for (int i = offset; i < len; i += step)
+ mac_real(&x[2 * i], &h[2 * i], y);
+}
+
+/* Base vector complex-complex multiply and accumulate */
+static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
+ int len, int step, int offset)
+{
+ for (int i = offset; i < len; i += step)
+ mac_cmplx(&x[2 * i], &h[2 * i], y);
+}
+
+/* Base complex-real convolution */
+int _base_convolve_real(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset)
+{
+ for (int i = 0; i < len; i++) {
+ mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
+ h,
+ &y[2 * i], h_len,
+ step, offset);
+ }
+
+ return len;
+}
+
+/* Base complex-complex convolution */
+int _base_convolve_complex(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset)
+{
+ for (int i = 0; i < len; i++) {
+ mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
+ h,
+ &y[2 * i],
+ h_len, step, offset);
+ }
+
+ return len;
+}
+
+/* Buffer validity checks */
+int bounds_check(int x_len, int h_len, int y_len,
+ int start, int len, int step)
+{
+ if ((x_len < 1) || (h_len < 1) ||
+ (y_len < 1) || (len < 1) || (step < 1)) {
+ fprintf(stderr, "Convolve: Invalid input\n");
+ return -1;
+ }
+
+ if ((start + len > x_len) || (len > y_len) || (x_len < h_len)) {
+ fprintf(stderr, "Convolve: Boundary exception\n");
+ fprintf(stderr, "start: %i, len: %i, x: %i, h: %i, y: %i\n",
+ start, len, x_len, h_len, y_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* API: Non-aligned (no SSE) complex-real */
+int base_convolve_real(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset)
+{
+ if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
+ return -1;
+
+ memset(y, 0, len * 2 * sizeof(float));
+
+ return _base_convolve_real(x, x_len,
+ h, h_len,
+ y, y_len,
+ start, len, step, offset);
+}
+
+/* API: Non-aligned (no SSE) complex-complex */
+int base_convolve_complex(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset)
+{
+ if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
+ return -1;
+
+ memset(y, 0, len * 2 * sizeof(float));
+
+ return _base_convolve_complex(x, x_len,
+ h, h_len,
+ y, y_len,
+ start, len, step, offset);
+}
+
+/* Aligned filter tap allocation */
+void *convolve_h_alloc(int len)
+{
+#ifdef HAVE_SSE3
+ return memalign(16, len * 2 * sizeof(float));
+#else
+ return malloc(len * 2 * sizeof(float));
+#endif
+}
diff --git a/Transceiver52M/arch/common/fft.c b/Transceiver52M/arch/common/fft.c
new file mode 100644
index 0000000..18b2de7
--- /dev/null
+++ b/Transceiver52M/arch/common/fft.c
@@ -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;
+}
diff --git a/Transceiver52M/arch/common/fft.h b/Transceiver52M/arch/common/fft.h
new file mode 100644
index 0000000..fb7bede
--- /dev/null
+++ b/Transceiver52M/arch/common/fft.h
@@ -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_ */
diff --git a/Transceiver52M/arch/common/mult.h b/Transceiver52M/arch/common/mult.h
new file mode 100644
index 0000000..4d96efb
--- /dev/null
+++ b/Transceiver52M/arch/common/mult.h
@@ -0,0 +1,6 @@
+#ifndef _MULT_H_
+#define _MULT_H_
+
+void mul_complex(float *out, float *a, float *b, int len);
+
+#endif /* _MULT_H_ */
diff --git a/Transceiver52M/arch/common/scale.h b/Transceiver52M/arch/common/scale.h
new file mode 100644
index 0000000..da867e7
--- /dev/null
+++ b/Transceiver52M/arch/common/scale.h
@@ -0,0 +1,6 @@
+#ifndef _SCALE_H_
+#define _SCALE_H_
+
+void scale_complex(float *out, float *in, float *scale, int len);
+
+#endif /* _SCALE_H_ */
diff --git a/Transceiver52M/arch/x86/Makefile.am b/Transceiver52M/arch/x86/Makefile.am
new file mode 100644
index 0000000..a79b80a
--- /dev/null
+++ b/Transceiver52M/arch/x86/Makefile.am
@@ -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
diff --git a/Transceiver52M/arch/x86/convert.c b/Transceiver52M/arch/x86/convert.c
new file mode 100644
index 0000000..07cdf59
--- /dev/null
+++ b/Transceiver52M/arch/x86/convert.c
@@ -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);
+}
diff --git a/Transceiver52M/arch/x86/convert_sse_3.c b/Transceiver52M/arch/x86/convert_sse_3.c
new file mode 100644
index 0000000..255db67
--- /dev/null
+++ b/Transceiver52M/arch/x86/convert_sse_3.c
@@ -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
diff --git a/Transceiver52M/arch/x86/convert_sse_3.h b/Transceiver52M/arch/x86/convert_sse_3.h
new file mode 100644
index 0000000..c2f87d7
--- /dev/null
+++ b/Transceiver52M/arch/x86/convert_sse_3.h
@@ -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);
diff --git a/Transceiver52M/arch/x86/convert_sse_4_1.c b/Transceiver52M/arch/x86/convert_sse_4_1.c
new file mode 100644
index 0000000..42a235c
--- /dev/null
+++ b/Transceiver52M/arch/x86/convert_sse_4_1.c
@@ -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
diff --git a/Transceiver52M/arch/x86/convert_sse_4_1.h b/Transceiver52M/arch/x86/convert_sse_4_1.h
new file mode 100644
index 0000000..57a5efb
--- /dev/null
+++ b/Transceiver52M/arch/x86/convert_sse_4_1.h
@@ -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);
diff --git a/Transceiver52M/arch/x86/convolve.c b/Transceiver52M/arch/x86/convolve.c
new file mode 100644
index 0000000..eb38f64
--- /dev/null
+++ b/Transceiver52M/arch/x86/convolve.c
@@ -0,0 +1,172 @@
+/*
+ * SSE Convolution
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include <stdio.h>
+#include "convolve.h"
+#include "convolve_sse_3.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Architecture dependant function pointers */
+struct convolve_cpu_context {
+ void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_cmplx_8n) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_cmplx) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_real4) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_real8) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_real12) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_real16) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_real20) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_real4n) (const float *, int, const float *, int, float *,
+ int, int, int, int, int);
+ void (*conv_real) (const float *, int, const float *, int, float *, int,
+ int, int, int, int);
+};
+static struct convolve_cpu_context c;
+
+/* Forward declarations from base implementation */
+int _base_convolve_real(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset);
+
+int _base_convolve_complex(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len,
+ int step, int offset);
+
+int bounds_check(int x_len, int h_len, int y_len,
+ int start, int len, int step);
+
+/* API: Initalize convolve module */
+void convolve_init(void)
+{
+ c.conv_cmplx_4n = (void *)_base_convolve_complex;
+ c.conv_cmplx_8n = (void *)_base_convolve_complex;
+ c.conv_cmplx = (void *)_base_convolve_complex;
+ c.conv_real4 = (void *)_base_convolve_real;
+ c.conv_real8 = (void *)_base_convolve_real;
+ c.conv_real12 = (void *)_base_convolve_real;
+ c.conv_real16 = (void *)_base_convolve_real;
+ c.conv_real20 = (void *)_base_convolve_real;
+ c.conv_real4n = (void *)_base_convolve_real;
+ c.conv_real = (void *)_base_convolve_real;
+
+#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
+ if (__builtin_cpu_supports("sse3")) {
+ c.conv_cmplx_4n = sse_conv_cmplx_4n;
+ c.conv_cmplx_8n = sse_conv_cmplx_8n;
+ c.conv_real4 = sse_conv_real4;
+ c.conv_real8 = sse_conv_real8;
+ c.conv_real12 = sse_conv_real12;
+ c.conv_real16 = sse_conv_real16;
+ c.conv_real20 = sse_conv_real20;
+ c.conv_real4n = sse_conv_real4n;
+ }
+#endif
+}
+
+/* API: Aligned complex-real */
+int convolve_real(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len, int start, int len, int step, int offset)
+{
+ if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
+ return -1;
+
+ memset(y, 0, len * 2 * sizeof(float));
+
+ if (step <= 4) {
+ switch (h_len) {
+ case 4:
+ c.conv_real4(x, x_len, h, h_len, y, y_len, start, len,
+ step, offset);
+ break;
+ case 8:
+ c.conv_real8(x, x_len, h, h_len, y, y_len, start, len,
+ step, offset);
+ break;
+ case 12:
+ c.conv_real12(x, x_len, h, h_len, y, y_len, start, len,
+ step, offset);
+ break;
+ case 16:
+ c.conv_real16(x, x_len, h, h_len, y, y_len, start, len,
+ step, offset);
+ break;
+ case 20:
+ c.conv_real20(x, x_len, h, h_len, y, y_len, start, len,
+ step, offset);
+ break;
+ default:
+ if (!(h_len % 4))
+ c.conv_real4n(x, x_len, h, h_len, y, y_len,
+ start, len, step, offset);
+ else
+ c.conv_real(x, x_len, h, h_len, y, y_len, start,
+ len, step, offset);
+ }
+ } else
+ c.conv_real(x, x_len, h, h_len, y, y_len, start, len, step,
+ offset);
+
+ return len;
+}
+
+/* API: Aligned complex-complex */
+int convolve_complex(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
+ return -1;
+
+ memset(y, 0, len * 2 * sizeof(float));
+
+ if (step <= 4) {
+ if (!(h_len % 8))
+ c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start,
+ len, step, offset);
+ else if (!(h_len % 4))
+ c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start,
+ len, step, offset);
+ else
+ c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len,
+ step, offset);
+ } else
+ c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len, step,
+ offset);
+
+ return len;
+}
diff --git a/Transceiver52M/arch/x86/convolve_sse_3.c b/Transceiver52M/arch/x86/convolve_sse_3.c
new file mode 100644
index 0000000..dbee3cc
--- /dev/null
+++ b/Transceiver52M/arch/x86/convolve_sse_3.c
@@ -0,0 +1,542 @@
+/*
+ * SSE Convolution
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include <stdio.h>
+#include "convolve_sse_3.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SSE3
+#include <xmmintrin.h>
+#include <pmmintrin.h>
+
+/* 4-tap SSE complex-real convolution */
+void sse_conv_real4(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ /* NOTE: The parameter list of this function has to match the parameter
+ * list of _base_convolve_real() in convolve_base.c. This specific
+ * implementation, ignores some of the parameters of
+ * _base_convolve_complex(), which are: x_len, y_len, offset, step */
+
+ __m128 m0, m1, m2, m3, m4, m5, m6, m7;
+
+ const float *_x = &x[2 * (-(h_len - 1) + start)];
+
+ /* Load (aligned) filter taps */
+ m0 = _mm_load_ps(&h[0]);
+ m1 = _mm_load_ps(&h[4]);
+ m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+
+ for (int i = 0; i < len; i++) {
+ /* Load (unaligned) input data */
+ m0 = _mm_loadu_ps(&_x[2 * i + 0]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 4]);
+ m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Quad multiply */
+ m4 = _mm_mul_ps(m2, m7);
+ m5 = _mm_mul_ps(m3, m7);
+
+ /* Sum and store */
+ m6 = _mm_hadd_ps(m4, m5);
+ m0 = _mm_hadd_ps(m6, m6);
+
+ _mm_store_ss(&y[2 * i + 0], m0);
+ m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
+ _mm_store_ss(&y[2 * i + 1], m0);
+ }
+}
+
+/* 8-tap SSE complex-real convolution */
+void sse_conv_real8(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ /* See NOTE in sse_conv_real4() */
+
+ __m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
+
+ const float *_x = &x[2 * (-(h_len - 1) + start)];
+
+ /* Load (aligned) filter taps */
+ m0 = _mm_load_ps(&h[0]);
+ m1 = _mm_load_ps(&h[4]);
+ m2 = _mm_load_ps(&h[8]);
+ m3 = _mm_load_ps(&h[12]);
+
+ m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m5 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+
+ for (int i = 0; i < len; i++) {
+ /* Load (unaligned) input data */
+ m0 = _mm_loadu_ps(&_x[2 * i + 0]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 4]);
+ m2 = _mm_loadu_ps(&_x[2 * i + 8]);
+ m3 = _mm_loadu_ps(&_x[2 * i + 12]);
+
+ m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+ m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Quad multiply */
+ m6 = _mm_mul_ps(m6, m4);
+ m7 = _mm_mul_ps(m7, m4);
+ m8 = _mm_mul_ps(m8, m5);
+ m9 = _mm_mul_ps(m9, m5);
+
+ /* Sum and store */
+ m6 = _mm_add_ps(m6, m8);
+ m7 = _mm_add_ps(m7, m9);
+ m6 = _mm_hadd_ps(m6, m7);
+ m6 = _mm_hadd_ps(m6, m6);
+
+ _mm_store_ss(&y[2 * i + 0], m6);
+ m6 = _mm_shuffle_ps(m6, m6, _MM_SHUFFLE(0, 3, 2, 1));
+ _mm_store_ss(&y[2 * i + 1], m6);
+ }
+}
+
+/* 12-tap SSE complex-real convolution */
+void sse_conv_real12(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ /* See NOTE in sse_conv_real4() */
+
+ __m128 m0, m1, m2, m3, m4, m5, m6, m7;
+ __m128 m8, m9, m10, m11, m12, m13, m14;
+
+ const float *_x = &x[2 * (-(h_len - 1) + start)];
+
+ /* Load (aligned) filter taps */
+ m0 = _mm_load_ps(&h[0]);
+ m1 = _mm_load_ps(&h[4]);
+ m2 = _mm_load_ps(&h[8]);
+ m3 = _mm_load_ps(&h[12]);
+ m4 = _mm_load_ps(&h[16]);
+ m5 = _mm_load_ps(&h[20]);
+
+ m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
+
+ for (int i = 0; i < len; i++) {
+ /* Load (unaligned) input data */
+ m0 = _mm_loadu_ps(&_x[2 * i + 0]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 4]);
+ m2 = _mm_loadu_ps(&_x[2 * i + 8]);
+ m3 = _mm_loadu_ps(&_x[2 * i + 12]);
+
+ m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+ m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
+
+ m0 = _mm_loadu_ps(&_x[2 * i + 16]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 20]);
+
+ m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Quad multiply */
+ m0 = _mm_mul_ps(m4, m12);
+ m1 = _mm_mul_ps(m5, m12);
+ m2 = _mm_mul_ps(m6, m13);
+ m3 = _mm_mul_ps(m7, m13);
+ m4 = _mm_mul_ps(m8, m14);
+ m5 = _mm_mul_ps(m9, m14);
+
+ /* Sum and store */
+ m8 = _mm_add_ps(m0, m2);
+ m9 = _mm_add_ps(m1, m3);
+ m10 = _mm_add_ps(m8, m4);
+ m11 = _mm_add_ps(m9, m5);
+
+ m2 = _mm_hadd_ps(m10, m11);
+ m3 = _mm_hadd_ps(m2, m2);
+
+ _mm_store_ss(&y[2 * i + 0], m3);
+ m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
+ _mm_store_ss(&y[2 * i + 1], m3);
+ }
+}
+
+/* 16-tap SSE complex-real convolution */
+void sse_conv_real16(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ /* See NOTE in sse_conv_real4() */
+
+ __m128 m0, m1, m2, m3, m4, m5, m6, m7;
+ __m128 m8, m9, m10, m11, m12, m13, m14, m15;
+
+ const float *_x = &x[2 * (-(h_len - 1) + start)];
+
+ /* Load (aligned) filter taps */
+ m0 = _mm_load_ps(&h[0]);
+ m1 = _mm_load_ps(&h[4]);
+ m2 = _mm_load_ps(&h[8]);
+ m3 = _mm_load_ps(&h[12]);
+
+ m4 = _mm_load_ps(&h[16]);
+ m5 = _mm_load_ps(&h[20]);
+ m6 = _mm_load_ps(&h[24]);
+ m7 = _mm_load_ps(&h[28]);
+
+ m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
+ m15 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
+
+ for (int i = 0; i < len; i++) {
+ /* Load (unaligned) input data */
+ m0 = _mm_loadu_ps(&_x[2 * i + 0]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 4]);
+ m2 = _mm_loadu_ps(&_x[2 * i + 8]);
+ m3 = _mm_loadu_ps(&_x[2 * i + 12]);
+
+ m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+ m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
+
+ m0 = _mm_loadu_ps(&_x[2 * i + 16]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 20]);
+ m2 = _mm_loadu_ps(&_x[2 * i + 24]);
+ m3 = _mm_loadu_ps(&_x[2 * i + 28]);
+
+ m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+ m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Quad multiply */
+ m0 = _mm_mul_ps(m4, m12);
+ m1 = _mm_mul_ps(m5, m12);
+ m2 = _mm_mul_ps(m6, m13);
+ m3 = _mm_mul_ps(m7, m13);
+
+ m4 = _mm_mul_ps(m8, m14);
+ m5 = _mm_mul_ps(m9, m14);
+ m6 = _mm_mul_ps(m10, m15);
+ m7 = _mm_mul_ps(m11, m15);
+
+ /* Sum and store */
+ m8 = _mm_add_ps(m0, m2);
+ m9 = _mm_add_ps(m1, m3);
+ m10 = _mm_add_ps(m4, m6);
+ m11 = _mm_add_ps(m5, m7);
+
+ m0 = _mm_add_ps(m8, m10);
+ m1 = _mm_add_ps(m9, m11);
+ m2 = _mm_hadd_ps(m0, m1);
+ m3 = _mm_hadd_ps(m2, m2);
+
+ _mm_store_ss(&y[2 * i + 0], m3);
+ m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
+ _mm_store_ss(&y[2 * i + 1], m3);
+ }
+}
+
+/* 20-tap SSE complex-real convolution */
+void sse_conv_real20(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ /* See NOTE in sse_conv_real4() */
+
+ __m128 m0, m1, m2, m3, m4, m5, m6, m7;
+ __m128 m8, m9, m11, m12, m13, m14, m15;
+
+ const float *_x = &x[2 * (-(h_len - 1) + start)];
+
+ /* Load (aligned) filter taps */
+ m0 = _mm_load_ps(&h[0]);
+ m1 = _mm_load_ps(&h[4]);
+ m2 = _mm_load_ps(&h[8]);
+ m3 = _mm_load_ps(&h[12]);
+ m4 = _mm_load_ps(&h[16]);
+ m5 = _mm_load_ps(&h[20]);
+ m6 = _mm_load_ps(&h[24]);
+ m7 = _mm_load_ps(&h[28]);
+ m8 = _mm_load_ps(&h[32]);
+ m9 = _mm_load_ps(&h[36]);
+
+ m11 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m12 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m13 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
+ m14 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
+ m15 = _mm_shuffle_ps(m8, m9, _MM_SHUFFLE(0, 2, 0, 2));
+
+ for (int i = 0; i < len; i++) {
+ /* Multiply-accumulate first 12 taps */
+ m0 = _mm_loadu_ps(&_x[2 * i + 0]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 4]);
+ m2 = _mm_loadu_ps(&_x[2 * i + 8]);
+ m3 = _mm_loadu_ps(&_x[2 * i + 12]);
+ m4 = _mm_loadu_ps(&_x[2 * i + 16]);
+ m5 = _mm_loadu_ps(&_x[2 * i + 20]);
+
+ m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+ m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
+ m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
+ m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3));
+
+ m2 = _mm_mul_ps(m6, m11);
+ m3 = _mm_mul_ps(m7, m11);
+ m4 = _mm_mul_ps(m8, m12);
+ m5 = _mm_mul_ps(m9, m12);
+ m6 = _mm_mul_ps(m0, m13);
+ m7 = _mm_mul_ps(m1, m13);
+
+ m0 = _mm_add_ps(m2, m4);
+ m1 = _mm_add_ps(m3, m5);
+ m8 = _mm_add_ps(m0, m6);
+ m9 = _mm_add_ps(m1, m7);
+
+ /* Multiply-accumulate last 8 taps */
+ m0 = _mm_loadu_ps(&_x[2 * i + 24]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 28]);
+ m2 = _mm_loadu_ps(&_x[2 * i + 32]);
+ m3 = _mm_loadu_ps(&_x[2 * i + 36]);
+
+ m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+ m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
+
+ m0 = _mm_mul_ps(m4, m14);
+ m1 = _mm_mul_ps(m5, m14);
+ m2 = _mm_mul_ps(m6, m15);
+ m3 = _mm_mul_ps(m7, m15);
+
+ m4 = _mm_add_ps(m0, m2);
+ m5 = _mm_add_ps(m1, m3);
+
+ /* Final sum and store */
+ m0 = _mm_add_ps(m8, m4);
+ m1 = _mm_add_ps(m9, m5);
+ m2 = _mm_hadd_ps(m0, m1);
+ m3 = _mm_hadd_ps(m2, m2);
+
+ _mm_store_ss(&y[2 * i + 0], m3);
+ m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
+ _mm_store_ss(&y[2 * i + 1], m3);
+ }
+}
+
+/* 4*N-tap SSE complex-real convolution */
+void sse_conv_real4n(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ /* See NOTE in sse_conv_real4() */
+
+ __m128 m0, m1, m2, m4, m5, m6, m7;
+
+ const float *_x = &x[2 * (-(h_len - 1) + start)];
+
+ for (int i = 0; i < len; i++) {
+ /* Zero */
+ m6 = _mm_setzero_ps();
+ m7 = _mm_setzero_ps();
+
+ for (int n = 0; n < h_len / 4; n++) {
+ /* Load (aligned) filter taps */
+ m0 = _mm_load_ps(&h[8 * n + 0]);
+ m1 = _mm_load_ps(&h[8 * n + 4]);
+ m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+
+ /* Load (unaligned) input data */
+ m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]);
+ m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Quad multiply */
+ m0 = _mm_mul_ps(m2, m4);
+ m1 = _mm_mul_ps(m2, m5);
+
+ /* Accumulate */
+ m6 = _mm_add_ps(m6, m0);
+ m7 = _mm_add_ps(m7, m1);
+ }
+
+ m0 = _mm_hadd_ps(m6, m7);
+ m0 = _mm_hadd_ps(m0, m0);
+
+ _mm_store_ss(&y[2 * i + 0], m0);
+ m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
+ _mm_store_ss(&y[2 * i + 1], m0);
+ }
+}
+
+/* 4*N-tap SSE complex-complex convolution */
+void sse_conv_cmplx_4n(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ /* NOTE: The parameter list of this function has to match the parameter
+ * list of _base_convolve_complex() in convolve_base.c. This specific
+ * implementation, ignores some of the parameters of
+ * _base_convolve_complex(), which are: x_len, y_len, offset, step. */
+
+ __m128 m0, m1, m2, m3, m4, m5, m6, m7;
+
+ const float *_x = &x[2 * (-(h_len - 1) + start)];
+
+ for (int i = 0; i < len; i++) {
+ /* Zero */
+ m6 = _mm_setzero_ps();
+ m7 = _mm_setzero_ps();
+
+ for (int n = 0; n < h_len / 4; n++) {
+ /* Load (aligned) filter taps */
+ m0 = _mm_load_ps(&h[8 * n + 0]);
+ m1 = _mm_load_ps(&h[8 * n + 4]);
+ m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Load (unaligned) input data */
+ m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]);
+ m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Quad multiply */
+ m0 = _mm_mul_ps(m2, m4);
+ m1 = _mm_mul_ps(m3, m5);
+
+ m2 = _mm_mul_ps(m2, m5);
+ m3 = _mm_mul_ps(m3, m4);
+
+ /* Sum */
+ m0 = _mm_sub_ps(m0, m1);
+ m2 = _mm_add_ps(m2, m3);
+
+ /* Accumulate */
+ m6 = _mm_add_ps(m6, m0);
+ m7 = _mm_add_ps(m7, m2);
+ }
+
+ m0 = _mm_hadd_ps(m6, m7);
+ m0 = _mm_hadd_ps(m0, m0);
+
+ _mm_store_ss(&y[2 * i + 0], m0);
+ m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
+ _mm_store_ss(&y[2 * i + 1], m0);
+ }
+}
+
+/* 8*N-tap SSE complex-complex convolution */
+void sse_conv_cmplx_8n(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset)
+{
+ /* See NOTE in sse_conv_cmplx_4n() */
+
+ __m128 m0, m1, m2, m3, m4, m5, m6, m7;
+ __m128 m8, m9, m10, m11, m12, m13, m14, m15;
+
+ const float *_x = &x[2 * (-(h_len - 1) + start)];
+
+ for (int i = 0; i < len; i++) {
+ /* Zero */
+ m12 = _mm_setzero_ps();
+ m13 = _mm_setzero_ps();
+ m14 = _mm_setzero_ps();
+ m15 = _mm_setzero_ps();
+
+ for (int n = 0; n < h_len / 8; n++) {
+ /* Load (aligned) filter taps */
+ m0 = _mm_load_ps(&h[16 * n + 0]);
+ m1 = _mm_load_ps(&h[16 * n + 4]);
+ m2 = _mm_load_ps(&h[16 * n + 8]);
+ m3 = _mm_load_ps(&h[16 * n + 12]);
+
+ m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+ m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Load (unaligned) input data */
+ m0 = _mm_loadu_ps(&_x[2 * i + 16 * n + 0]);
+ m1 = _mm_loadu_ps(&_x[2 * i + 16 * n + 4]);
+ m2 = _mm_loadu_ps(&_x[2 * i + 16 * n + 8]);
+ m3 = _mm_loadu_ps(&_x[2 * i + 16 * n + 12]);
+
+ m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
+ m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
+ m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
+ m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
+
+ /* Quad multiply */
+ m0 = _mm_mul_ps(m4, m8);
+ m1 = _mm_mul_ps(m5, m9);
+ m2 = _mm_mul_ps(m6, m10);
+ m3 = _mm_mul_ps(m7, m11);
+
+ m4 = _mm_mul_ps(m4, m9);
+ m5 = _mm_mul_ps(m5, m8);
+ m6 = _mm_mul_ps(m6, m11);
+ m7 = _mm_mul_ps(m7, m10);
+
+ /* Sum */
+ m0 = _mm_sub_ps(m0, m1);
+ m2 = _mm_sub_ps(m2, m3);
+ m4 = _mm_add_ps(m4, m5);
+ m6 = _mm_add_ps(m6, m7);
+
+ /* Accumulate */
+ m12 = _mm_add_ps(m12, m0);
+ m13 = _mm_add_ps(m13, m2);
+ m14 = _mm_add_ps(m14, m4);
+ m15 = _mm_add_ps(m15, m6);
+ }
+
+ m0 = _mm_add_ps(m12, m13);
+ m1 = _mm_add_ps(m14, m15);
+ m2 = _mm_hadd_ps(m0, m1);
+ m2 = _mm_hadd_ps(m2, m2);
+
+ _mm_store_ss(&y[2 * i + 0], m2);
+ m2 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(0, 3, 2, 1));
+ _mm_store_ss(&y[2 * i + 1], m2);
+ }
+}
+#endif
diff --git a/Transceiver52M/arch/x86/convolve_sse_3.h b/Transceiver52M/arch/x86/convolve_sse_3.h
new file mode 100644
index 0000000..ac30ca5
--- /dev/null
+++ b/Transceiver52M/arch/x86/convolve_sse_3.h
@@ -0,0 +1,68 @@
+/*
+ * SSE Convolution
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+/* 4-tap SSE complex-real convolution */
+void sse_conv_real4(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset);
+
+/* 8-tap SSE complex-real convolution */
+void sse_conv_real8(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset);
+
+/* 12-tap SSE complex-real convolution */
+void sse_conv_real12(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset);
+
+/* 16-tap SSE complex-real convolution */
+void sse_conv_real16(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset);
+
+/* 20-tap SSE complex-real convolution */
+void sse_conv_real20(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset);
+
+/* 4*N-tap SSE complex-real convolution */
+void sse_conv_real4n(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset);
+
+/* 4*N-tap SSE complex-complex convolution */
+void sse_conv_cmplx_4n(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset);
+
+/* 8*N-tap SSE complex-complex convolution */
+void sse_conv_cmplx_8n(const float *x, int x_len,
+ const float *h, int h_len,
+ float *y, int y_len,
+ int start, int len, int step, int offset);
diff --git a/Transceiver52M/device/Makefile.am b/Transceiver52M/device/Makefile.am
new file mode 100644
index 0000000..1a2d077
--- /dev/null
+++ b/Transceiver52M/device/Makefile.am
@@ -0,0 +1,17 @@
+include $(top_srcdir)/Makefile.common
+
+noinst_HEADERS = radioDevice.h
+
+SUBDIRS =
+
+if DEVICE_USRP1
+SUBDIRS += usrp1
+endif
+
+if DEVICE_UHD
+SUBDIRS += uhd
+endif
+
+if DEVICE_LMS
+SUBDIRS += lms
+endif
diff --git a/Transceiver52M/device/lms/LMSDevice.cpp b/Transceiver52M/device/lms/LMSDevice.cpp
new file mode 100644
index 0000000..0a6c4a2
--- /dev/null
+++ b/Transceiver52M/device/lms/LMSDevice.cpp
@@ -0,0 +1,637 @@
+/*
+* 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 <lime/LimeSuite.h>
+
+#include <osmocom/core/utils.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+using namespace std;
+
+constexpr double LMSDevice::masterClockRate;
+
+#define MAX_ANTENNA_LIST_SIZE 10
+#define LMS_SAMPLE_RATE GSMRATE*32
+#define GSM_CARRIER_BW 270000.0 /* 270kHz */
+#define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */
+#define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED)
+
+LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
+ const std::vector<std::string>& tx_paths,
+ const std::vector<std::string>& rx_paths):
+ RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
+ m_lms_dev(NULL)
+{
+ LOGC(DDEV, INFO) << "creating LMS device...";
+
+ m_lms_stream_rx.resize(chans);
+ m_lms_stream_tx.resize(chans);
+
+ m_last_rx_underruns.resize(chans, 0);
+ m_last_rx_overruns.resize(chans, 0);
+ m_last_tx_underruns.resize(chans, 0);
+ m_last_tx_overruns.resize(chans, 0);
+}
+
+static void lms_log_callback(int lvl, const char *msg)
+{
+ /* map lime specific log levels */
+ static const int lvl_map[5] = {
+ [0] = LOGL_FATAL,
+ [LMS_LOG_ERROR] = LOGL_ERROR,
+ [LMS_LOG_WARNING] = LOGL_NOTICE,
+ [LMS_LOG_INFO] = LOGL_INFO,
+ [LMS_LOG_DEBUG] = LOGL_DEBUG,
+ };
+ /* protect against future higher log level values (lower importance) */
+ if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map))
+ lvl = ARRAY_SIZE(lvl_map)-1;
+
+ LOGLV(DLMS, lvl_map[lvl]) << msg;
+}
+
+static void thread_enable_cancel(bool cancel)
+{
+ cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+}
+
+static void print_range(const char* name, lms_range_t *range)
+{
+ LOGC(DDEV, INFO) << name << ": Min=" << range->min << " Max=" << range->max
+ << " Step=" << range->step;
+}
+
+int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
+{
+ //lms_info_str_t dev_str;
+ lms_info_str_t* info_list;
+ lms_range_t range_lpfbw_rx, range_lpfbw_tx, range_sr;
+ float_type sr_host, sr_rf, lpfbw_rx, lpfbw_tx;
+ uint16_t dac_val;
+ unsigned int i, n;
+ int rc;
+
+ 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];
+
+ rc = LMS_Open(&m_lms_dev, info_list[0], NULL);
+ if (rc != 0) {
+ LOGC(DDEV, ERROR) << "LMS_GetDeviceList() failed)";
+ delete [] info_list;
+ return -1;
+ }
+
+ delete [] info_list;
+
+ LOGC(DDEV, INFO) << "Init LMS device";
+ if (LMS_Init(m_lms_dev) != 0) {
+ LOGC(DDEV, ERROR) << "LMS_Init() failed";
+ return -1;
+ }
+
+ if (LMS_GetSampleRateRange(m_lms_dev, LMS_CH_RX, &range_sr))
+ goto out_close;
+ print_range("Sample Rate", &range_sr);
+
+ LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps;
+ if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0)
+ goto out_close;
+
+ if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf))
+ goto out_close;
+ LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf;
+
+ /* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */
+ ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */
+
+ switch (ref) {
+ case REF_INTERNAL:
+ LOGC(DDEV, INFO) << "Setting Internal clock reference";
+ /* Ugly API: Selecting clock source implicit by writing to VCTCXO DAC ?!? */
+ if (LMS_VCTCXORead(m_lms_dev, &dac_val) < 0)
+ goto out_close;
+ LOGC(DDEV, INFO) << "Setting VCTCXO to " << dac_val;
+ if (LMS_VCTCXOWrite(m_lms_dev, dac_val) < 0)
+ goto out_close;
+ break;
+ case REF_EXTERNAL:
+ LOGC(DDEV, INFO) << "Setting External clock reference to " << 10000000.0;
+ /* Assume an external 10 MHz reference clock */
+ if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
+ goto out_close;
+ break;
+ default:
+ LOGC(DDEV, ALERT) << "Invalid reference type";
+ goto out_close;
+ }
+
+ if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_rx))
+ goto out_close;
+ print_range("LPFBWRange Rx", &range_lpfbw_rx);
+ if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_tx))
+ goto out_close;
+ print_range("LPFBWRange Tx", &range_lpfbw_tx);
+ lpfbw_rx = OSMO_MIN(OSMO_MAX(1.4001e6, range_lpfbw_rx.min), range_lpfbw_rx.max);
+ lpfbw_tx = OSMO_MIN(OSMO_MAX(5.2e6, range_lpfbw_tx.min), range_lpfbw_tx.max);
+
+ LOGC(DDEV, INFO) << "LPFBW: Rx=" << lpfbw_rx << " Tx=" << lpfbw_tx;
+
+ if (!set_antennas()) {
+ LOGC(DDEV, ALERT) << "LMS antenna setting failed";
+ return -1;
+ }
+
+ /* Perform Rx and Tx calibration */
+ for (i=0; i<chans; i++) {
+ LOGC(DDEV, INFO) << "Setting LPFBW chan " << i;
+ if (LMS_SetLPFBW(m_lms_dev, LMS_CH_RX, i, lpfbw_rx) < 0)
+ goto out_close;
+ if (LMS_SetLPFBW(m_lms_dev, LMS_CH_TX, i, lpfbw_tx) < 0)
+ goto out_close;
+ LOGC(DDEV, INFO) << "Calibrating chan " << i;
+ if (LMS_Calibrate(m_lms_dev, LMS_CH_RX, i, LMS_CALIBRATE_BW_HZ, 0) < 0)
+ goto out_close;
+ if (LMS_Calibrate(m_lms_dev, LMS_CH_TX, i, LMS_CALIBRATE_BW_HZ, 0) < 0)
+ goto out_close;
+ }
+
+ samplesRead = 0;
+ samplesWritten = 0;
+ started = false;
+
+ return NORMAL;
+
+out_close:
+ LOGC(DDEV, ALERT) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
+ LMS_Close(m_lms_dev);
+ return -1;
+}
+
+bool LMSDevice::start()
+{
+ LOGC(DDEV, INFO) << "starting LMS...";
+
+ unsigned int i;
+
+ /* configure the channels/streams */
+ for (i=0; i<chans; i++) {
+ if (LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, true) < 0)
+ return false;
+
+ if (LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, true) < 0)
+ return false;
+
+ // Set gains to midpoint
+ setTxGain((minTxGain() + maxTxGain()) / 2, i);
+ setRxGain(34.0, i);
+
+ m_lms_stream_rx[i] = {};
+ m_lms_stream_rx[i].isTx = false;
+ m_lms_stream_rx[i].channel = i;
+ m_lms_stream_rx[i].fifoSize = 1024 * 1024;
+ m_lms_stream_rx[i].throughputVsLatency = 0.3;
+ m_lms_stream_rx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
+
+ m_lms_stream_tx[i] = {};
+ m_lms_stream_tx[i].isTx = true;
+ m_lms_stream_tx[i].channel = i;
+ m_lms_stream_tx[i].fifoSize = 1024 * 1024;
+ m_lms_stream_tx[i].throughputVsLatency = 0.3;
+ m_lms_stream_tx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
+
+ if (LMS_SetupStream(m_lms_dev, &m_lms_stream_rx[i]) < 0)
+ return false;
+
+ if (LMS_SetupStream(m_lms_dev, &m_lms_stream_tx[i]) < 0)
+ return false;
+ }
+
+ /* now start the streams in a second loop, as we can no longer call
+ * LMS_SetupStream() after LMS_StartStream() of the first stream */
+ for (i = 0; i < chans; i++) {
+ if (LMS_StartStream(&m_lms_stream_rx[i]) < 0)
+ return false;
+
+ if (LMS_StartStream(&m_lms_stream_tx[i]) < 0)
+ return false;
+ }
+
+ flush_recv(10);
+
+ started = true;
+ return true;
+}
+
+bool LMSDevice::stop()
+{
+ unsigned int i;
+
+ if (!started)
+ return true;
+
+ for (i=0; i<chans; i++) {
+ LMS_StopStream(&m_lms_stream_tx[i]);
+ LMS_StopStream(&m_lms_stream_rx[i]);
+
+ LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, false);
+ LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, false);
+ }
+
+ return true;
+}
+
+double LMSDevice::maxTxGain()
+{
+ return 73.0;
+}
+
+double LMSDevice::minTxGain()
+{
+ return 0.0;
+}
+
+double LMSDevice::maxRxGain()
+{
+ return 73.0;
+}
+
+double LMSDevice::minRxGain()
+{
+ return 0.0;
+}
+
+double LMSDevice::setTxGain(double dB, size_t chan)
+{
+ if (chan) {
+ LOGC(DDEV, ALERT) << "Invalid channel " << chan;
+ return 0.0;
+ }
+
+ if (dB > maxTxGain())
+ dB = maxTxGain();
+ if (dB < minTxGain())
+ dB = minTxGain();
+
+ LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
+
+ if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
+ LOGC(DDEV, ERR) << "Error setting TX gain";
+
+ return dB;
+}
+
+double LMSDevice::setRxGain(double dB, size_t chan)
+{
+ if (chan) {
+ LOGC(DDEV, ALERT) << "Invalid channel " << chan;
+ return 0.0;
+ }
+
+ if (dB > maxRxGain())
+ dB = maxRxGain();
+ if (dB < minRxGain())
+ dB = minRxGain();
+
+ LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB.";
+
+ if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0)
+ LOGC(DDEV, ERR) << "Error setting RX gain";
+
+ return dB;
+}
+
+int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan)
+{
+ lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
+ const char* c_name = name.c_str();
+ int num_names;
+ int i;
+
+ num_names = LMS_GetAntennaList(m_lms_dev, dir_tx, chan, name_list);
+ for (i = 0; i < num_names; i++) {
+ if (!strcmp(c_name, name_list[i]))
+ return i;
+ }
+ return -1;
+}
+
+bool LMSDevice::flush_recv(size_t num_pkts)
+{
+ #define CHUNK 625
+ int len = CHUNK * tx_sps;
+ short *buffer = new short[len * 2];
+ int rc;
+ lms_stream_meta_t rx_metadata = {};
+ rx_metadata.flushPartialPacket = false;
+ rx_metadata.waitForTimestamp = false;
+
+ ts_initial = 0;
+
+ while (!ts_initial || (num_pkts-- > 0)) {
+ rc = LMS_RecvStream(&m_lms_stream_rx[0], &buffer[0], len, &rx_metadata, 100);
+ LOGC(DDEV, DEBUG) << "Flush: Recv buffer of len " << rc << " at " << std::hex << rx_metadata.timestamp;
+ if (rc != len) {
+ LOGC(DDEV, ALERT) << "LMS: Device receive timed out";
+ delete[] buffer;
+ return false;
+ }
+
+ ts_initial = rx_metadata.timestamp + len;
+ }
+
+ LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
+ delete[] buffer;
+ return true;
+}
+
+bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan)
+{
+ int idx;
+
+ if (chan >= rx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+
+ idx = get_ant_idx(ant, LMS_CH_RX, chan);
+ if (idx < 0) {
+ LOGC(DDEV, ALERT) << "Invalid Rx Antenna";
+ return false;
+ }
+
+ if (LMS_SetAntenna(m_lms_dev, LMS_CH_RX, chan, idx) < 0) {
+ LOGC(DDEV, ALERT) << "Unable to set Rx Antenna";
+ }
+
+ return true;
+}
+
+std::string LMSDevice::getRxAntenna(size_t chan)
+{
+ lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
+ int idx;
+
+ if (chan >= rx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return "";
+ }
+
+ idx = LMS_GetAntenna(m_lms_dev, LMS_CH_RX, chan);
+ if (idx < 0) {
+ LOGC(DDEV, ALERT) << "Error getting Rx Antenna";
+ return "";
+ }
+
+ if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, name_list) < idx) {
+ LOGC(DDEV, ALERT) << "Error getting Rx Antenna List";
+ return "";
+ }
+
+ return name_list[idx];
+}
+
+bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan)
+{
+ int idx;
+
+ if (chan >= tx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+
+ idx = get_ant_idx(ant, LMS_CH_TX, chan);
+ if (idx < 0) {
+ LOGC(DDEV, ALERT) << "Invalid Rx Antenna";
+ return false;
+ }
+
+ if (LMS_SetAntenna(m_lms_dev, LMS_CH_TX, chan, idx) < 0) {
+ LOGC(DDEV, ALERT) << "Unable to set Rx Antenna";
+ }
+
+ return true;
+}
+
+std::string LMSDevice::getTxAntenna(size_t chan)
+{
+ lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
+ int idx;
+
+ if (chan >= tx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return "";
+ }
+
+ idx = LMS_GetAntenna(m_lms_dev, LMS_CH_TX, chan);
+ if (idx < 0) {
+ LOGC(DDEV, ALERT) << "Error getting Tx Antenna";
+ return "";
+ }
+
+ if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, name_list) < idx) {
+ LOGC(DDEV, ALERT) << "Error getting Tx Antenna List";
+ return "";
+ }
+
+ return name_list[idx];
+}
+
+bool LMSDevice::requiresRadioAlign()
+{
+ return false;
+}
+
+GSM::Time LMSDevice::minLatency() {
+ /* Empirical data from a handful of
+ relatively recent machines shows that the B100 will underrun when
+ the transmit threshold is reduced to a time of 6 and a half frames,
+ so we set a minimum 7 frame threshold. */
+ return GSM::Time(6,7);
+}
+
+// NOTE: Assumes sequential reads
+int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
+ TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
+{
+ int rc = 0;
+ unsigned int i;
+ lms_stream_status_t status;
+ lms_stream_meta_t rx_metadata = {};
+ rx_metadata.flushPartialPacket = false;
+ rx_metadata.waitForTimestamp = false;
+ rx_metadata.timestamp = 0;
+
+ if (bufs.size() != chans) {
+ LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
+ return -1;
+ }
+
+ *overrun = false;
+ *underrun = false;
+ for (i = 0; i<chans; i++) {
+ thread_enable_cancel(false);
+ rc = LMS_RecvStream(&m_lms_stream_rx[i], bufs[i], len, &rx_metadata, 100);
+ if (rc != len) {
+ LOGC(DDEV, ALERT) << "LMS: Device receive timed out (" << rc << " vs exp " << len << ").";
+ thread_enable_cancel(true);
+ return -1;
+ }
+ if (timestamp != (TIMESTAMP)rx_metadata.timestamp)
+ LOGC(DDEV, ALERT) << "chan "<< i << " recv buffer of len " << rc << " expect " << std::hex << timestamp << " got " << std::hex << (TIMESTAMP)rx_metadata.timestamp << " (" << std::hex << rx_metadata.timestamp <<") diff=" << rx_metadata.timestamp - timestamp;
+
+ if (LMS_GetStreamStatus(&m_lms_stream_rx[i], &status) == 0) {
+ if (status.underrun > m_last_rx_underruns[i])
+ *underrun = true;
+ m_last_rx_underruns[i] = status.underrun;
+
+ if (status.overrun > m_last_rx_overruns[i])
+ *overrun = true;
+ m_last_rx_overruns[i] = status.overrun;
+ }
+ thread_enable_cancel(true);
+ }
+
+ samplesRead += rc;
+
+ if (((TIMESTAMP) rx_metadata.timestamp) < timestamp)
+ rc = 0;
+
+ return rc;
+}
+
+int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
+ bool * underrun, unsigned long long timestamp,
+ bool isControl)
+{
+ int rc = 0;
+ unsigned int i;
+ lms_stream_status_t status;
+ lms_stream_meta_t tx_metadata = {};
+ tx_metadata.flushPartialPacket = false;
+ tx_metadata.waitForTimestamp = true;
+ tx_metadata.timestamp = timestamp - ts_offset; /* Shift Tx time by offset */
+
+ if (isControl) {
+ LOGC(DDEV, ERR) << "Control packets not supported";
+ return 0;
+ }
+
+ if (bufs.size() != chans) {
+ LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
+ return -1;
+ }
+
+ *underrun = false;
+
+ for (i = 0; i<chans; i++) {
+ LOGC(DDEV, DEBUG) << "chan "<< i << " send buffer of len " << len << " timestamp " << std::hex << tx_metadata.timestamp;
+ thread_enable_cancel(false);
+ rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100);
+ if (rc != len) {
+ LOGC(DDEV, ALERT) << "LMS: Device send timed out";
+ }
+
+ if (LMS_GetStreamStatus(&m_lms_stream_tx[i], &status) == 0) {
+ if (status.underrun > m_last_tx_underruns[i])
+ *underrun = true;
+ m_last_tx_underruns[i] = status.underrun;
+ }
+ thread_enable_cancel(true);
+ }
+
+ samplesWritten += rc;
+
+ return rc;
+}
+
+bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
+{
+ return true;
+}
+
+bool LMSDevice::setTxFreq(double wFreq, size_t chan)
+{
+
+ if (chan) {
+ LOGC(DDEV, ALERT) << "Invalid channel " << chan;
+ return false;
+ }
+
+ if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
+ LOGC(DDEV, ALERT) << "set Tx: " << wFreq << " failed!";
+ return false;
+ }
+
+ return true;
+}
+
+bool LMSDevice::setRxFreq(double wFreq, size_t chan)
+{
+ if (chan) {
+ LOGC(DDEV, ALERT) << "Invalid channel " << chan;
+ return false;
+ }
+
+ if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
+ LOGC(DDEV, ALERT) << "set Rx: " << wFreq << " failed!";
+ 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);
+}
diff --git a/Transceiver52M/device/lms/LMSDevice.h b/Transceiver52M/device/lms/LMSDevice.h
new file mode 100644
index 0000000..349efbb
--- /dev/null
+++ b/Transceiver52M/device/lms/LMSDevice.h
@@ -0,0 +1,202 @@
+/*
+* Copyright 2018 sysmocom - s.f.m.c. GmbH
+*
+* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+*/
+
+#ifndef _LMS_DEVICE_H_
+#define _LMS_DEVICE_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "radioDevice.h"
+
+#include <sys/time.h>
+#include <math.h>
+#include <limits.h>
+#include <string>
+#include <iostream>
+#include <lime/LimeSuite.h>
+
+#define LIMESDR_TX_AMPL 0.3
+
+/** A class to handle a LimeSuite supported device */
+class LMSDevice:public RadioDevice {
+
+private:
+
+ static constexpr double masterClockRate = 52.0e6;
+
+ lms_device_t *m_lms_dev;
+ std::vector<lms_stream_t> m_lms_stream_rx;
+ std::vector<lms_stream_t> m_lms_stream_tx;
+
+ std::vector<uint32_t> m_last_rx_underruns;
+ std::vector<uint32_t> m_last_rx_overruns;
+ std::vector<uint32_t> m_last_tx_underruns;
+ std::vector<uint32_t> m_last_tx_overruns;
+
+ double actualSampleRate; ///< the actual USRP sampling rate
+
+ unsigned long long samplesRead; ///< number of samples read from LMS
+ unsigned long long samplesWritten; ///< number of samples sent to LMS
+
+ bool started; ///< flag indicates LMS has started
+ bool skipRx; ///< set if LMS is transmit-only.
+
+ TIMESTAMP ts_initial, ts_offset;
+
+ double rxGain;
+
+ int get_ant_idx(const std::string & name, bool dir_tx, size_t chan);
+ bool flush_recv(size_t num_pkts);
+
+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);
+
+ /** Instantiate the LMS */
+ int open(const std::string &args, int ref, bool swap_channels);
+
+ /** Start the LMS */
+ bool start();
+
+ /** Stop the LMS */
+ bool stop();
+
+ /** Set priority not supported */
+ void setPriority(float prio = 0.5) {
+ }
+
+ enum TxWindowType getWindowType() {
+ return TX_WINDOW_LMS1;
+ }
+
+ /**
+ Read samples from the LMS.
+ @param buf preallocated buf to contain read result
+ @param len number of samples desired
+ @param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
+ @param timestamp The timestamp of the first samples to be read
+ @param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
+ @param RSSI The received signal strength of the read result
+ @return The number of samples actually read
+ */
+ int readSamples(std::vector < short *>&buf, int len, bool * overrun,
+ TIMESTAMP timestamp = 0xffffffff, bool * underrun =
+ NULL, unsigned *RSSI = NULL);
+ /**
+ Write samples to the LMS.
+ @param buf Contains the data to be written.
+ @param len number of samples to write.
+ @param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
+ @param timestamp The timestamp of the first sample of the data buffer.
+ @param isControl Set if data is a control packet, e.g. a ping command
+ @return The number of samples actually written
+ */
+ int writeSamples(std::vector < short *>&bufs, int len, bool * underrun,
+ TIMESTAMP timestamp = 0xffffffff, bool isControl =
+ false);
+
+ /** Update the alignment between the read and write timestamps */
+ bool updateAlignment(TIMESTAMP timestamp);
+
+ /** Set the transmitter frequency */
+ bool setTxFreq(double wFreq, size_t chan = 0);
+
+ /** Set the receiver frequency */
+ bool setRxFreq(double wFreq, size_t chan = 0);
+
+ /** Returns the starting write Timestamp*/
+ TIMESTAMP initialWriteTimestamp(void) {
+ return ts_initial;
+ }
+
+ /** Returns the starting read Timestamp*/
+ TIMESTAMP initialReadTimestamp(void) {
+ return ts_initial;
+ }
+
+ /** returns the full-scale transmit amplitude **/
+ double fullScaleInputValue() {
+ return(double) SHRT_MAX * LIMESDR_TX_AMPL;
+ }
+
+ /** returns the full-scale receive amplitude **/
+ double fullScaleOutputValue() {
+ return (double) SHRT_MAX;
+ }
+
+ /** sets the receive chan gain, returns the gain setting **/
+ double setRxGain(double dB, size_t chan = 0);
+
+ /** get the current receive gain */
+ double getRxGain(size_t chan = 0) {
+ return rxGain;
+ }
+
+ /** return maximum Rx Gain **/
+ double maxRxGain(void);
+
+ /** return minimum Rx Gain **/
+ double minRxGain(void);
+
+ /** sets the transmit chan gain, returns the gain setting **/
+ double setTxGain(double dB, size_t chan = 0);
+
+ /** return maximum Tx Gain **/
+ double maxTxGain(void);
+
+ /** return minimum Rx Gain **/
+ double minTxGain(void);
+
+ /** sets the RX path to use, returns true if successful and false otherwise */
+ bool setRxAntenna(const std::string & ant, size_t chan = 0);
+
+ /* return the used RX path */
+ std::string getRxAntenna(size_t chan = 0);
+
+ /** sets the RX path to use, returns true if successful and false otherwise */
+ bool setTxAntenna(const std::string & ant, size_t chan = 0);
+
+ /* return the used RX path */
+ std::string getTxAntenna(size_t chan = 0);
+
+ /** return whether user drives synchronization of Tx/Rx of USRP */
+ bool requiresRadioAlign();
+
+ /** return whether user drives synchronization of Tx/Rx of USRP */
+ virtual GSM::Time minLatency();
+
+ /** Return internal status values */
+ inline double getTxFreq(size_t chan = 0) {
+ return 0;
+ }
+ inline double getRxFreq(size_t chan = 0) {
+ return 0;
+ }
+ inline double getSampleRate() {
+ return actualSampleRate;
+ }
+ inline double numberRead() {
+ return samplesRead;
+ }
+ inline double numberWritten() {
+ return samplesWritten;
+ }
+};
+
+#endif // _LMS_DEVICE_H_
diff --git a/Transceiver52M/device/lms/Makefile.am b/Transceiver52M/device/lms/Makefile.am
new file mode 100644
index 0000000..8471074
--- /dev/null
+++ b/Transceiver52M/device/lms/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
+AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
+
+noinst_HEADERS = LMSDevice.h
+
+noinst_LTLIBRARIES = libdevice.la
+
+libdevice_la_SOURCES = LMSDevice.cpp
diff --git a/Transceiver52M/device/radioDevice.h b/Transceiver52M/device/radioDevice.h
new file mode 100644
index 0000000..5d001fb
--- /dev/null
+++ b/Transceiver52M/device/radioDevice.h
@@ -0,0 +1,210 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* 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 __RADIO_DEVICE_H__
+#define __RADIO_DEVICE_H__
+
+#include <string>
+#include <vector>
+
+#include "GSMCommon.h"
+#include "Logger.h"
+
+extern "C" {
+#include "config_defs.h"
+}
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define GSMRATE (1625e3/6)
+#define MCBTS_SPACING 800000.0
+
+/** a 64-bit virtual timestamp for radio data */
+typedef unsigned long long TIMESTAMP;
+
+/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
+class RadioDevice {
+
+ public:
+ /* Available transport bus types */
+ enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED, TX_WINDOW_LMS1 };
+
+ /* Radio interface types */
+ enum InterfaceType {
+ NORMAL,
+ RESAMP_64M,
+ RESAMP_100M,
+ MULTI_ARFCN,
+ };
+
+ static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
+ size_t chans = 1, double offset = 0.0,
+ const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
+ const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
+
+ /** Initialize the USRP */
+ virtual int open(const std::string &args, int ref, bool swap_channels)=0;
+
+ virtual ~RadioDevice() { }
+
+ /** Start the USRP */
+ virtual bool start()=0;
+
+ /** Stop the USRP */
+ virtual bool stop()=0;
+
+ /** Get the Tx window type */
+ virtual enum TxWindowType getWindowType()=0;
+
+ /** Enable thread priority */
+ virtual void setPriority(float prio = 0.5) = 0;
+
+ /**
+ Read samples from the radio.
+ @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 radio 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
+ */
+ virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
+ TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
+ unsigned *RSSI = 0) = 0;
+ /**
+ Write samples to the radio.
+ @param buf Contains the data to be written.
+ @param len number of samples to write.
+ @param underrun Set if radio 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
+ */
+ virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
+ TIMESTAMP timestamp, bool isControl = false) = 0;
+
+ /** Update the alignment between the read and write timestamps */
+ virtual bool updateAlignment(TIMESTAMP timestamp)=0;
+
+ /** Set the transmitter frequency */
+ virtual bool setTxFreq(double wFreq, size_t chan = 0) = 0;
+
+ /** Set the receiver frequency */
+ virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
+
+ /** Returns the starting write Timestamp*/
+ virtual TIMESTAMP initialWriteTimestamp(void)=0;
+
+ /** Returns the starting read Timestamp*/
+ virtual TIMESTAMP initialReadTimestamp(void)=0;
+
+ /** returns the full-scale transmit amplitude **/
+ virtual double fullScaleInputValue()=0;
+
+ /** returns the full-scale receive amplitude **/
+ virtual double fullScaleOutputValue()=0;
+
+ /** sets the receive chan gain, returns the gain setting **/
+ virtual double setRxGain(double dB, size_t chan = 0) = 0;
+
+ /** gets the current receive gain **/
+ virtual double getRxGain(size_t chan = 0) = 0;
+
+ /** return maximum Rx Gain **/
+ virtual double maxRxGain(void) = 0;
+
+ /** return minimum Rx Gain **/
+ virtual double minRxGain(void) = 0;
+
+ /** sets the transmit chan gain, returns the gain setting **/
+ virtual double setTxGain(double dB, size_t chan = 0) = 0;
+
+ /** return maximum Tx Gain **/
+ virtual double maxTxGain(void) = 0;
+
+ /** return minimum Tx Gain **/
+ virtual double minTxGain(void) = 0;
+
+ /** sets the RX path to use, returns true if successful and false otherwise */
+ virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0;
+
+ /** return the used RX path */
+ virtual std::string getRxAntenna(size_t chan = 0) = 0;
+
+ /** sets the RX path to use, returns true if successful and false otherwise */
+ virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0;
+
+ /** return the used RX path */
+ virtual std::string getTxAntenna(size_t chan = 0) = 0;
+
+ /** return whether user drives synchronization of Tx/Rx of USRP */
+ virtual bool requiresRadioAlign() = 0;
+
+ /** Minimum latency that the device can achieve */
+ virtual GSM::Time minLatency() = 0;
+
+ /** Return internal status values */
+ virtual double getTxFreq(size_t chan = 0) = 0;
+ virtual double getRxFreq(size_t chan = 0) = 0;
+ 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;
+ }
+
+
+};
+
+#endif
diff --git a/Transceiver52M/device/uhd/Makefile.am b/Transceiver52M/device/uhd/Makefile.am
new file mode 100644
index 0000000..bb34d2f
--- /dev/null
+++ b/Transceiver52M/device/uhd/Makefile.am
@@ -0,0 +1,8 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
+AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
+
+noinst_LTLIBRARIES = libdevice.la
+
+libdevice_la_SOURCES = UHDDevice.cpp
diff --git a/Transceiver52M/device/uhd/UHDDevice.cpp b/Transceiver52M/device/uhd/UHDDevice.cpp
new file mode 100644
index 0000000..3db09a8
--- /dev/null
+++ b/Transceiver52M/device/uhd/UHDDevice.cpp
@@ -0,0 +1,1561 @@
+/*
+ * Device support for Ettus Research UHD driver
+ *
+ * Copyright 2010,2011 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * 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 <map>
+#include "radioDevice.h"
+#include "Threads.h"
+#include "Logger.h"
+#include <uhd/version.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/multi_usrp.hpp>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef USE_UHD_3_11
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/thread_priority.hpp>
+#else
+#include <uhd/utils/thread.hpp>
+#endif
+
+#define USRP_TX_AMPL 0.3
+#define UMTRX_TX_AMPL 0.7
+#define LIMESDR_TX_AMPL 0.3
+#define SAMPLE_BUF_SZ (1 << 20)
+
+/*
+ * UHD timeout value on streaming (re)start
+ *
+ * Allow some time for streaming to commence after the start command is issued,
+ * but consider a wait beyond one second to be a definite error condition.
+ */
+#define UHD_RESTART_TIMEOUT 1.0
+
+/*
+ * UmTRX specific settings
+ */
+#define UMTRX_VGA1_DEF -18
+
+enum uhd_dev_type {
+ USRP1,
+ USRP2,
+ B100,
+ B200,
+ B210,
+ B2XX_MCBTS,
+ E1XX,
+ E3XX,
+ X3XX,
+ UMTRX,
+ LIMESDR,
+};
+
+/*
+ * USRP version dependent device timings
+ */
+#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11)
+#define B2XX_TIMING_1SPS 1.7153e-4
+#define B2XX_TIMING_4SPS 1.1696e-4
+#define B2XX_TIMING_4_4SPS 6.18462e-5
+#define B2XX_TIMING_MCBTS 7e-5
+#else
+#define B2XX_TIMING_1SPS 9.9692e-5
+#define B2XX_TIMING_4SPS 6.9248e-5
+#define B2XX_TIMING_4_4SPS 4.52308e-5
+#define B2XX_TIMING_MCBTS 6.42452e-5
+#endif
+
+/*
+ * Tx / Rx sample offset values. In a perfect world, there is no group delay
+ * though analog components, and behaviour through digital filters exactly
+ * matches calculated values. In reality, there are unaccounted factors,
+ * which are captured in these empirically measured (using a loopback test)
+ * timing correction values.
+ *
+ * Notes:
+ * USRP1 with timestamps is not supported by UHD.
+ */
+
+/* Device Type, Tx-SPS, Rx-SPS */
+typedef std::tuple<uhd_dev_type, int, int> dev_key;
+
+/* Device parameter descriptor */
+struct dev_desc {
+ unsigned channels;
+ double mcr;
+ double rate;
+ double offset;
+ std::string str;
+};
+
+static const std::map<dev_key, dev_desc> dev_param_map {
+ { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
+ { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
+ { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
+ { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } },
+ { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } },
+ { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
+ { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
+ { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
+ { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } },
+ { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
+ { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
+ { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } },
+ { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } },
+ { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } },
+ { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } },
+ { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } },
+ { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } },
+ { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } },
+ { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } },
+ { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} },
+ { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
+ { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
+ { std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
+};
+
+/*
+ Sample Buffer - Allows reading and writing of timed samples using osmo-trx
+ or UHD style timestamps. Time conversions are handled
+ internally or accessable through the static convert calls.
+*/
+class smpl_buf {
+public:
+ /** Sample buffer constructor
+ @param len number of 32-bit samples the buffer should hold
+ @param rate sample clockrate
+ @param timestamp
+ */
+ smpl_buf(size_t len, double rate);
+ ~smpl_buf();
+
+ /** Query number of samples available for reading
+ @param timestamp time of first sample
+ @return number of available samples or error
+ */
+ ssize_t avail_smpls(TIMESTAMP timestamp) const;
+ ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
+
+ /** Read and write
+ @param buf pointer to buffer
+ @param len number of samples desired to read or write
+ @param timestamp time of first stample
+ @return number of actual samples read or written or error
+ */
+ ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
+ ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
+ ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
+ ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
+
+ /** Buffer status string
+ @return a formatted string describing internal buffer state
+ */
+ std::string str_status(size_t ts) const;
+
+ /** Formatted error string
+ @param code an error code
+ @return a formatted error string
+ */
+ static std::string str_code(ssize_t code);
+
+ enum err_code {
+ ERROR_TIMESTAMP = -1,
+ ERROR_READ = -2,
+ ERROR_WRITE = -3,
+ ERROR_OVERFLOW = -4
+ };
+
+private:
+ uint32_t *data;
+ size_t buf_len;
+
+ double clk_rt;
+
+ TIMESTAMP time_start;
+ TIMESTAMP time_end;
+
+ size_t data_start;
+ size_t data_end;
+};
+
+/*
+ uhd_device - UHD implementation of the Device interface. Timestamped samples
+ are sent to and received from the device. An intermediate buffer
+ on the receive side collects and aligns packets of samples.
+ Events and errors such as underruns are reported asynchronously
+ by the device and received in a separate thread.
+*/
+class uhd_device : public RadioDevice {
+public:
+ uhd_device(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);
+ ~uhd_device();
+
+ int open(const std::string &args, int ref, bool swap_channels);
+ bool start();
+ bool stop();
+ bool restart();
+ void setPriority(float prio);
+ enum TxWindowType getWindowType() { return tx_window; }
+
+ int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
+ TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
+
+ int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
+ TIMESTAMP timestamp, bool isControl);
+
+ bool updateAlignment(TIMESTAMP timestamp);
+
+ bool setTxFreq(double wFreq, size_t chan);
+ bool setRxFreq(double wFreq, size_t chan);
+
+ TIMESTAMP initialWriteTimestamp();
+ TIMESTAMP initialReadTimestamp();
+
+ double fullScaleInputValue();
+ double fullScaleOutputValue();
+
+ double setRxGain(double db, size_t chan);
+ double getRxGain(size_t chan);
+ double maxRxGain(void) { return rx_gain_max; }
+ double minRxGain(void) { return rx_gain_min; }
+
+ double setTxGain(double db, size_t chan);
+ double maxTxGain(void) { return tx_gain_max; }
+ double minTxGain(void) { return tx_gain_min; }
+
+ double getTxFreq(size_t chan);
+ double getRxFreq(size_t chan);
+ double getRxFreq();
+
+ bool setRxAntenna(const std::string &ant, size_t chan);
+ std::string getRxAntenna(size_t chan);
+ bool setTxAntenna(const std::string &ant, size_t chan);
+ std::string getTxAntenna(size_t chan);
+
+ bool requiresRadioAlign();
+
+ GSM::Time minLatency();
+
+ inline double getSampleRate() { return tx_rate; }
+ inline double numberRead() { return rx_pkt_cnt; }
+ inline double numberWritten() { return 0; }
+
+ /** Receive and process asynchronous message
+ @return true if message received or false on timeout or error
+ */
+ bool recv_async_msg();
+
+ enum err_code {
+ ERROR_TIMING = -1,
+ ERROR_TIMEOUT = -2,
+ ERROR_UNRECOVERABLE = -3,
+ ERROR_UNHANDLED = -4,
+ };
+
+private:
+ uhd::usrp::multi_usrp::sptr usrp_dev;
+ uhd::tx_streamer::sptr tx_stream;
+ uhd::rx_streamer::sptr rx_stream;
+ enum TxWindowType tx_window;
+ enum uhd_dev_type dev_type;
+
+ double tx_rate, rx_rate;
+
+ double tx_gain_min, tx_gain_max;
+ double rx_gain_min, rx_gain_max;
+
+ std::vector<double> tx_gains, rx_gains;
+ std::vector<double> tx_freqs, rx_freqs;
+ size_t tx_spp, rx_spp;
+
+ bool started;
+ bool aligned;
+
+ size_t rx_pkt_cnt;
+ size_t drop_cnt;
+ uhd::time_spec_t prev_ts;
+
+ TIMESTAMP ts_initial, ts_offset;
+ std::vector<smpl_buf *> rx_buffers;
+
+ void init_gains();
+ void set_channels(bool swap);
+ void set_rates();
+ bool parse_dev_type();
+ bool flush_recv(size_t num_pkts);
+ int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
+
+ std::string str_code(uhd::rx_metadata_t metadata);
+ std::string str_code(uhd::async_metadata_t metadata);
+
+ uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
+ bool set_freq(double freq, size_t chan, bool tx);
+
+ Thread *async_event_thrd;
+ Mutex tune_lock;
+};
+
+void *async_event_loop(uhd_device *dev)
+{
+ set_selfthread_name("UHDAsyncEvent");
+ dev->setPriority(0.43);
+
+ while (1) {
+ dev->recv_async_msg();
+ pthread_testcancel();
+ }
+
+ return NULL;
+}
+
+#ifndef USE_UHD_3_11
+/*
+ Catch and drop underrun 'U' and overrun 'O' messages from stdout
+ since we already report using the logging facility. Direct
+ everything else appropriately.
+ */
+void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
+{
+ switch (type) {
+ case uhd::msg::status:
+ LOGC(DDEV, INFO) << msg;
+ break;
+ case uhd::msg::warning:
+ LOGC(DDEV, WARNING) << msg;
+ break;
+ case uhd::msg::error:
+ LOGC(DDEV, ERR) << msg;
+ break;
+ case uhd::msg::fastpath:
+ break;
+ }
+}
+#endif
+
+static void thread_enable_cancel(bool cancel)
+{
+ cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+}
+
+uhd_device::uhd_device(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),
+ tx_gain_min(0.0), tx_gain_max(0.0),
+ rx_gain_min(0.0), rx_gain_max(0.0),
+ tx_spp(0), rx_spp(0),
+ started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
+ prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
+{
+}
+
+uhd_device::~uhd_device()
+{
+ stop();
+
+ for (size_t i = 0; i < rx_buffers.size(); i++)
+ delete rx_buffers[i];
+}
+
+void uhd_device::init_gains()
+{
+ uhd::gain_range_t range;
+
+ if (dev_type == UMTRX) {
+ std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
+ if (gain_stages[0] == "VGA") {
+ LOGC(DDEV, WARNING) << "Update your UHD version for a proper Tx gain support";
+ }
+ if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
+ range = usrp_dev->get_tx_gain_range();
+ tx_gain_min = range.start();
+ tx_gain_max = range.stop();
+ } else {
+ range = usrp_dev->get_tx_gain_range("VGA2");
+ tx_gain_min = UMTRX_VGA1_DEF + range.start();
+ tx_gain_max = UMTRX_VGA1_DEF + range.stop();
+ }
+ } else {
+ range = usrp_dev->get_tx_gain_range();
+ tx_gain_min = range.start();
+ tx_gain_max = range.stop();
+ }
+ LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
+
+ range = usrp_dev->get_rx_gain_range();
+ rx_gain_min = range.start();
+ rx_gain_max = range.stop();
+ LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
+
+ for (size_t i = 0; i < tx_gains.size(); i++) {
+ double gain = (tx_gain_min + tx_gain_max) / 2;
+ LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
+ usrp_dev->set_tx_gain(gain, i);
+ tx_gains[i] = usrp_dev->get_tx_gain(i);
+ }
+
+ for (size_t i = 0; i < rx_gains.size(); i++) {
+ double gain = (rx_gain_min + rx_gain_max) / 2;
+ LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
+ usrp_dev->set_rx_gain(gain, i);
+ rx_gains[i] = usrp_dev->get_rx_gain(i);
+ }
+
+ return;
+
+}
+
+void uhd_device::set_rates()
+{
+ dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
+ if (desc.mcr != 0.0)
+ usrp_dev->set_master_clock_rate(desc.mcr);
+
+ tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
+ rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
+
+ usrp_dev->set_tx_rate(tx_rate);
+ usrp_dev->set_rx_rate(rx_rate);
+ tx_rate = usrp_dev->get_tx_rate();
+ rx_rate = usrp_dev->get_rx_rate();
+
+ ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
+ LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
+}
+
+double uhd_device::setTxGain(double db, size_t chan)
+{
+ if (iface == MULTI_ARFCN)
+ chan = 0;
+
+ if (chan >= tx_gains.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
+ return 0.0f;
+ }
+
+ if (dev_type == UMTRX) {
+ std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
+ if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
+ usrp_dev->set_tx_gain(db, chan);
+ } else {
+ // New UHD versions support split configuration of
+ // Tx gain stages. We utilize this to set the gain
+ // configuration, optimal for the Tx signal quality.
+ // From our measurements, VGA1 must be 18dB plus-minus
+ // one and VGA2 is the best when 23dB or lower.
+ usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
+ usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
+ }
+ } else {
+ usrp_dev->set_tx_gain(db, chan);
+ }
+
+ tx_gains[chan] = usrp_dev->get_tx_gain(chan);
+
+ LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
+
+ return tx_gains[chan];
+}
+
+double uhd_device::setRxGain(double db, size_t chan)
+{
+ if (iface == MULTI_ARFCN)
+ chan = 0;
+
+ if (chan >= rx_gains.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0f;
+ }
+
+ usrp_dev->set_rx_gain(db, chan);
+ rx_gains[chan] = usrp_dev->get_rx_gain(chan);
+
+ LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
+
+ return rx_gains[chan];
+}
+
+double uhd_device::getRxGain(size_t chan)
+{
+ if (iface == MULTI_ARFCN)
+ chan = 0;
+
+ if (chan >= rx_gains.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0f;
+ }
+
+ return rx_gains[chan];
+}
+
+/*
+ Parse the UHD device tree and mboard name to find out what device we're
+ dealing with. We need the window type so that the transceiver knows how to
+ deal with the transport latency. Reject the USRP1 because UHD doesn't
+ support timestamped samples with it.
+ */
+bool uhd_device::parse_dev_type()
+{
+ uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
+ std::string devString = prop_tree->access<std::string>("/name").get();
+ std::string mboardString = usrp_dev->get_mboard_name();
+
+ const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
+ { "B100", { B100, TX_WINDOW_USRP1 } },
+ { "B200", { B200, TX_WINDOW_USRP1 } },
+ { "B200mini", { B200, TX_WINDOW_USRP1 } },
+ { "B205mini", { B200, TX_WINDOW_USRP1 } },
+ { "B210", { B210, TX_WINDOW_USRP1 } },
+ { "E100", { E1XX, TX_WINDOW_FIXED } },
+ { "E110", { E1XX, TX_WINDOW_FIXED } },
+ { "E310", { E3XX, TX_WINDOW_FIXED } },
+ { "E3XX", { E3XX, TX_WINDOW_FIXED } },
+ { "X300", { X3XX, TX_WINDOW_FIXED } },
+ { "X310", { X3XX, TX_WINDOW_FIXED } },
+ { "USRP2", { USRP2, TX_WINDOW_FIXED } },
+ { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
+ { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
+ };
+
+ // Compare UHD motherboard and device strings */
+ auto mapIter = devStringMap.begin();
+ while (mapIter != devStringMap.end()) {
+ if (devString.find(mapIter->first) != std::string::npos ||
+ mboardString.find(mapIter->first) != std::string::npos) {
+ dev_type = std::get<0>(mapIter->second);
+ tx_window = std::get<1>(mapIter->second);
+ return true;
+ }
+ mapIter++;
+ }
+
+ LOGC(DDEV, ALERT) << "Unsupported device " << devString;
+ return false;
+}
+
+/*
+ * Check for UHD version > 3.9.0 for E3XX support
+ */
+static bool uhd_e3xx_version_chk()
+{
+ std::string ver = uhd::get_version_string();
+ std::string major_str(ver.begin(), ver.begin() + 3);
+ std::string minor_str(ver.begin() + 4, ver.begin() + 7);
+
+ int major_val = atoi(major_str.c_str());
+ int minor_val = atoi(minor_str.c_str());
+
+ if (major_val < 3)
+ return false;
+ if (minor_val < 9)
+ return false;
+
+ return true;
+}
+
+void uhd_device::set_channels(bool swap)
+{
+ if (iface == MULTI_ARFCN) {
+ if (dev_type != B200 && dev_type != B210)
+ throw std::invalid_argument("Device does not support MCBTS");
+ dev_type = B2XX_MCBTS;
+ chans = 1;
+ }
+
+ if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
+ throw std::invalid_argument("Device does not support number of requested channels");
+
+ std::string subdev_string;
+ switch (dev_type) {
+ case B210:
+ case E3XX:
+ if (chans == 1)
+ subdev_string = swap ? "A:B" : "A:A";
+ else if (chans == 2)
+ subdev_string = swap ? "A:B A:A" : "A:A A:B";
+ break;
+ case X3XX:
+ case UMTRX:
+ if (chans == 1)
+ subdev_string = swap ? "B:0" : "A:0";
+ else if (chans == 2)
+ subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
+ break;
+ default:
+ break;
+ }
+
+ if (!subdev_string.empty()) {
+ uhd::usrp::subdev_spec_t spec(subdev_string);
+ usrp_dev->set_tx_subdev_spec(spec);
+ usrp_dev->set_rx_subdev_spec(spec);
+ }
+}
+
+int uhd_device::open(const std::string &args, int ref, bool swap_channels)
+{
+ const char *refstr;
+
+ // Find UHD devices
+ uhd::device_addr_t addr(args);
+ uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
+ if (dev_addrs.size() == 0) {
+ LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
+ return -1;
+ }
+
+ // Use the first found device
+ LOGC(DDEV, INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
+ try {
+ usrp_dev = uhd::usrp::multi_usrp::make(addr);
+ } catch(...) {
+ LOGC(DDEV, ALERT) << "UHD make failed, device " << args;
+ return -1;
+ }
+
+ // Check for a valid device type and set bus type
+ if (!parse_dev_type())
+ return -1;
+
+ if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
+ LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
+ return -1;
+ }
+
+ try {
+ set_channels(swap_channels);
+ } catch (const std::exception &e) {
+ LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
+ return -1;
+ }
+
+ if (!set_antennas()) {
+ LOGC(DDEV, ALERT) << "UHD antenna setting failed";
+ return -1;
+ }
+
+ tx_freqs.resize(chans);
+ rx_freqs.resize(chans);
+ tx_gains.resize(chans);
+ rx_gains.resize(chans);
+ rx_buffers.resize(chans);
+
+ switch (ref) {
+ case REF_INTERNAL:
+ refstr = "internal";
+ break;
+ case REF_EXTERNAL:
+ refstr = "external";
+ break;
+ case REF_GPS:
+ refstr = "gpsdo";
+ break;
+ default:
+ LOGC(DDEV, ALERT) << "Invalid reference type";
+ return -1;
+ }
+
+ usrp_dev->set_clock_source(refstr);
+
+ try {
+ set_rates();
+ } catch (const std::exception &e) {
+ LOGC(DDEV, ALERT) << "UHD rate setting failed - " << e.what();
+ return -1;
+ }
+
+ // Set RF frontend bandwidth
+ if (dev_type == UMTRX) {
+ // Setting LMS6002D LPF to 500kHz gives us the best signal quality
+ for (size_t i = 0; i < chans; i++) {
+ usrp_dev->set_tx_bandwidth(500*1000*2, i);
+ usrp_dev->set_rx_bandwidth(500*1000*2, i);
+ }
+ } else if (dev_type == LIMESDR) {
+ for (size_t i = 0; i < chans; i++) {
+ usrp_dev->set_tx_bandwidth(5.2e6, i);
+ usrp_dev->set_rx_bandwidth(1.4001e6, i);
+ }
+ }
+
+ /* Create TX and RX streamers */
+ uhd::stream_args_t stream_args("sc16");
+ for (size_t i = 0; i < chans; i++)
+ stream_args.channels.push_back(i);
+
+ tx_stream = usrp_dev->get_tx_stream(stream_args);
+ rx_stream = usrp_dev->get_rx_stream(stream_args);
+
+ /* Number of samples per over-the-wire packet */
+ tx_spp = tx_stream->get_max_num_samps();
+ rx_spp = rx_stream->get_max_num_samps();
+
+ // Create receive buffer
+ size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
+ for (size_t i = 0; i < rx_buffers.size(); i++)
+ rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
+
+ // Initialize and shadow gain values
+ init_gains();
+
+ // Print configuration
+ LOGC(DDEV, INFO) << "\n" << usrp_dev->get_pp_string();
+
+ if (iface == MULTI_ARFCN)
+ return MULTI_ARFCN;
+
+ switch (dev_type) {
+ case B100:
+ return RESAMP_64M;
+ case USRP2:
+ case X3XX:
+ return RESAMP_100M;
+ case B200:
+ case B210:
+ case E1XX:
+ case E3XX:
+ case LIMESDR:
+ default:
+ break;
+ }
+
+ return NORMAL;
+}
+
+bool uhd_device::flush_recv(size_t num_pkts)
+{
+ uhd::rx_metadata_t md;
+ size_t num_smpls;
+ float timeout = UHD_RESTART_TIMEOUT;
+
+ std::vector<std::vector<short> >
+ pkt_bufs(chans, std::vector<short>(2 * rx_spp));
+
+ std::vector<short *> pkt_ptrs;
+ for (size_t i = 0; i < pkt_bufs.size(); i++)
+ pkt_ptrs.push_back(&pkt_bufs[i].front());
+
+ ts_initial = 0;
+ while (!ts_initial || (num_pkts-- > 0)) {
+ num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
+ timeout, true);
+ if (!num_smpls) {
+ switch (md.error_code) {
+ case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
+ LOGC(DDEV, ALERT) << "Device timed out";
+ return false;
+ default:
+ continue;
+ }
+ }
+
+ ts_initial = md.time_spec.to_ticks(rx_rate);
+ }
+
+ LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
+
+ return true;
+}
+
+bool uhd_device::restart()
+{
+ /* Allow 100 ms delay to align multi-channel streams */
+ double delay = 0.1;
+
+ aligned = false;
+
+ uhd::time_spec_t current = usrp_dev->get_time_now();
+
+ uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
+ cmd.stream_now = false;
+ cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
+
+ usrp_dev->issue_stream_cmd(cmd);
+
+ return flush_recv(10);
+}
+
+bool uhd_device::start()
+{
+ LOGC(DDEV, INFO) << "Starting USRP...";
+
+ if (started) {
+ LOGC(DDEV, ERR) << "Device already started";
+ return false;
+ }
+
+#ifndef USE_UHD_3_11
+ // Register msg handler
+ uhd::msg::register_handler(&uhd_msg_handler);
+#endif
+ // Start asynchronous event (underrun check) loop
+ async_event_thrd = new Thread();
+ async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
+
+ // Start streaming
+ if (!restart())
+ return false;
+
+ // Display usrp time
+ double time_now = usrp_dev->get_time_now().get_real_secs();
+ LOGC(DDEV, INFO) << "The current time is " << time_now << " seconds";
+
+ started = true;
+ return true;
+}
+
+bool uhd_device::stop()
+{
+ if (!started)
+ return false;
+
+ uhd::stream_cmd_t stream_cmd =
+ uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
+
+ usrp_dev->issue_stream_cmd(stream_cmd);
+
+ async_event_thrd->cancel();
+ async_event_thrd->join();
+ delete async_event_thrd;
+
+ started = false;
+ return true;
+}
+
+void uhd_device::setPriority(float prio)
+{
+ uhd::set_thread_priority_safe(prio);
+ return;
+}
+
+int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
+{
+ if (!num_smpls) {
+ LOGC(DDEV, ERR) << str_code(md);
+
+ switch (md.error_code) {
+ case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
+ LOGC(DDEV, ALERT) << "UHD: Receive timed out";
+ return ERROR_TIMEOUT;
+ case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
+ case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
+ case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
+ case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
+ default:
+ return ERROR_UNHANDLED;
+ }
+ }
+
+ // Missing timestamp
+ if (!md.has_time_spec) {
+ LOGC(DDEV, ALERT) << "UHD: Received packet missing timestamp";
+ return ERROR_UNRECOVERABLE;
+ }
+
+ // Monotonicity check
+ if (md.time_spec < prev_ts) {
+ LOGC(DDEV, ALERT) << "UHD: Loss of monotonic time";
+ LOGC(DDEV, ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
+ << "Previous time: " << prev_ts.get_real_secs();
+ return ERROR_TIMING;
+ }
+
+ // Workaround for UHD tick rounding bug
+ TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
+ if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
+ md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
+
+ prev_ts = md.time_spec;
+
+ return 0;
+}
+
+int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
+ TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
+{
+ ssize_t rc;
+ uhd::time_spec_t ts;
+ uhd::rx_metadata_t metadata;
+
+ if (bufs.size() != chans) {
+ LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
+ return -1;
+ }
+
+ *overrun = false;
+ *underrun = false;
+
+ // Shift read time with respect to transmit clock
+ timestamp += ts_offset;
+
+ ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
+ LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts.get_real_secs();
+
+ // Check that timestamp is valid
+ rc = rx_buffers[0]->avail_smpls(timestamp);
+ if (rc < 0) {
+ LOGC(DDEV, ERR) << rx_buffers[0]->str_code(rc);
+ LOGC(DDEV, ERR) << rx_buffers[0]->str_status(timestamp);
+ return 0;
+ }
+
+ // Create vector buffer
+ std::vector<std::vector<short> >
+ pkt_bufs(chans, std::vector<short>(2 * rx_spp));
+
+ std::vector<short *> pkt_ptrs;
+ for (size_t i = 0; i < pkt_bufs.size(); i++)
+ pkt_ptrs.push_back(&pkt_bufs[i].front());
+
+ // Receive samples from the usrp until we have enough
+ while (rx_buffers[0]->avail_smpls(timestamp) < len) {
+ thread_enable_cancel(false);
+ size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
+ metadata, 0.1, true);
+ thread_enable_cancel(true);
+
+ rx_pkt_cnt++;
+
+ // Check for errors
+ rc = check_rx_md_err(metadata, num_smpls);
+ switch (rc) {
+ case ERROR_UNRECOVERABLE:
+ LOGC(DDEV, ALERT) << "UHD: Version " << uhd::get_version_string();
+ LOGC(DDEV, ALERT) << "UHD: Unrecoverable error, exiting...";
+ exit(-1);
+ case ERROR_TIMEOUT:
+ // Assume stopping condition
+ return 0;
+ case ERROR_TIMING:
+ restart();
+ case ERROR_UNHANDLED:
+ continue;
+ }
+
+ ts = metadata.time_spec;
+ LOGC(DDEV, DEBUG) << "Received timestamp = " << ts.get_real_secs();
+
+ for (size_t i = 0; i < rx_buffers.size(); i++) {
+ rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
+ num_smpls,
+ metadata.time_spec);
+
+ // Continue on local overrun, exit on other errors
+ if ((rc < 0)) {
+ LOGC(DDEV, ERR) << rx_buffers[i]->str_code(rc);
+ LOGC(DDEV, ERR) << 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, ERR) << rx_buffers[i]->str_code(rc);
+ LOGC(DDEV, ERR) << rx_buffers[i]->str_status(timestamp);
+ return 0;
+ }
+ }
+
+ return len;
+}
+
+int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
+ unsigned long long timestamp,bool isControl)
+{
+ uhd::tx_metadata_t metadata;
+ metadata.has_time_spec = true;
+ metadata.start_of_burst = false;
+ metadata.end_of_burst = false;
+ metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
+
+ *underrun = false;
+
+ // No control packets
+ if (isControl) {
+ LOGC(DDEV, ERR) << "Control packets not supported";
+ return 0;
+ }
+
+ if (bufs.size() != chans) {
+ LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
+ return -1;
+ }
+
+ // Drop a fixed number of packets (magic value)
+ if (!aligned) {
+ drop_cnt++;
+
+ if (drop_cnt == 1) {
+ LOGC(DDEV, DEBUG) << "Aligning transmitter: stop burst";
+ *underrun = true;
+ metadata.end_of_burst = true;
+ } else if (drop_cnt < 30) {
+ LOGC(DDEV, DEBUG) << "Aligning transmitter: packet advance";
+ return len;
+ } else {
+ LOGC(DDEV, DEBUG) << "Aligning transmitter: start burst";
+ metadata.start_of_burst = true;
+ aligned = true;
+ drop_cnt = 0;
+ }
+ }
+
+ thread_enable_cancel(false);
+ size_t num_smpls = tx_stream->send(bufs, len, metadata);
+ thread_enable_cancel(true);
+
+ if (num_smpls != (unsigned) len) {
+ LOGC(DDEV, ALERT) << "UHD: Device send timed out";
+ }
+
+ return num_smpls;
+}
+
+bool uhd_device::updateAlignment(TIMESTAMP timestamp)
+{
+ return true;
+}
+
+uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
+{
+ double rf_spread, rf_freq;
+ std::vector<double> freqs;
+ uhd::tune_request_t treq(freq);
+
+ if (dev_type == UMTRX) {
+ if (lo_offset != 0.0)
+ return uhd::tune_request_t(freq, lo_offset);
+
+ // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
+ // We end up with DSP tuning just for 2-3Hz, which is meaningless and
+ // only distort the signal (because cordic is not ideal).
+ treq.target_freq = freq;
+ treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
+ treq.rf_freq = freq;
+ treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
+ treq.dsp_freq = 0.0;
+ return treq;
+ } else if (chans == 1) {
+ if (lo_offset == 0.0)
+ return treq;
+
+ return uhd::tune_request_t(freq, lo_offset);
+ } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
+ LOGC(DDEV, ALERT) << chans << " channels unsupported";
+ return treq;
+ }
+
+ if (tx)
+ freqs = tx_freqs;
+ else
+ freqs = rx_freqs;
+
+ /* Tune directly if other channel isn't tuned */
+ if (freqs[!chan] < 10.0)
+ return treq;
+
+ /* Find center frequency between channels */
+ rf_spread = fabs(freqs[!chan] - freq);
+ if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
+ LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n";
+ return treq;
+ }
+
+ rf_freq = (freqs[!chan] + freq) / 2.0f;
+
+ treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
+ treq.target_freq = freq;
+ treq.rf_freq = rf_freq;
+
+ return treq;
+}
+
+bool uhd_device::set_freq(double freq, size_t chan, bool tx)
+{
+ std::vector<double> freqs;
+ uhd::tune_result_t tres;
+ uhd::tune_request_t treq = select_freq(freq, chan, tx);
+
+ if (tx) {
+ tres = usrp_dev->set_tx_freq(treq, chan);
+ tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
+ } else {
+ tres = usrp_dev->set_rx_freq(treq, chan);
+ rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
+ }
+ LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
+
+ if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
+ return true;
+
+ /* Manual RF policy means we intentionally tuned with a baseband
+ * offset for dual-channel purposes. Now retune the other channel
+ * with the opposite corresponding frequency offset
+ */
+ if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
+ if (tx) {
+ treq = select_freq(tx_freqs[!chan], !chan, true);
+ tres = usrp_dev->set_tx_freq(treq, !chan);
+ tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
+ } else {
+ treq = select_freq(rx_freqs[!chan], !chan, false);
+ tres = usrp_dev->set_rx_freq(treq, !chan);
+ rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
+
+ }
+ LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
+ }
+
+ return true;
+}
+
+bool uhd_device::setTxFreq(double wFreq, size_t chan)
+{
+ if (chan >= tx_freqs.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+ ScopedLock lock(tune_lock);
+
+ return set_freq(wFreq, chan, true);
+}
+
+bool uhd_device::setRxFreq(double wFreq, size_t chan)
+{
+ if (chan >= rx_freqs.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+ ScopedLock lock(tune_lock);
+
+ return set_freq(wFreq, chan, false);
+}
+
+double uhd_device::getTxFreq(size_t chan)
+{
+ if (chan >= tx_freqs.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0;
+ }
+
+ return tx_freqs[chan];
+}
+
+double uhd_device::getRxFreq(size_t chan)
+{
+ if (chan >= rx_freqs.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0;
+ }
+
+ return rx_freqs[chan];
+}
+
+bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
+{
+ std::vector<std::string> avail;
+ if (chan >= rx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+
+ avail = usrp_dev->get_rx_antennas(chan);
+ if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
+ LOGC(DDEV, INFO) << "Available Rx antennas: ";
+ for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
+ LOGC(DDEV, INFO) << "- '" << *i << "'";
+ return false;
+ }
+ usrp_dev->set_rx_antenna(ant, chan);
+ rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
+
+ if (ant != rx_paths[chan]) {
+ LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
+ return false;
+ }
+
+ return true;
+}
+
+std::string uhd_device::getRxAntenna(size_t chan)
+{
+ if (chan >= rx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return "";
+ }
+ return usrp_dev->get_rx_antenna(chan);
+}
+
+bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
+{
+ std::vector<std::string> avail;
+ if (chan >= tx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+
+ avail = usrp_dev->get_tx_antennas(chan);
+ if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
+ LOGC(DDEV, INFO) << "Available Tx antennas: ";
+ for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
+ LOGC(DDEV, INFO) << "- '" << *i << "'";
+ return false;
+ }
+ usrp_dev->set_tx_antenna(ant, chan);
+ tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
+
+ if (ant != tx_paths[chan]) {
+ LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
+ return false;
+ }
+
+ return true;
+}
+
+std::string uhd_device::getTxAntenna(size_t chan)
+{
+ if (chan >= tx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return "";
+ }
+ return usrp_dev->get_tx_antenna(chan);
+}
+
+bool uhd_device::requiresRadioAlign()
+{
+ return false;
+}
+
+GSM::Time uhd_device::minLatency() {
+ /* Empirical data from a handful of
+ relatively recent machines shows that the B100 will underrun when
+ the transmit threshold is reduced to a time of 6 and a half frames,
+ so we set a minimum 7 frame threshold. */
+ return GSM::Time(6,7);
+}
+
+/*
+ * Only allow sampling the Rx path lower than Tx and not vice-versa.
+ * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
+ * combination.
+ */
+TIMESTAMP uhd_device::initialWriteTimestamp()
+{
+ if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
+ return ts_initial;
+ else
+ return ts_initial * tx_sps;
+}
+
+TIMESTAMP uhd_device::initialReadTimestamp()
+{
+ return ts_initial;
+}
+
+double uhd_device::fullScaleInputValue()
+{
+ if (dev_type == LIMESDR)
+ return (double) SHRT_MAX * LIMESDR_TX_AMPL;
+ if (dev_type == UMTRX)
+ return (double) SHRT_MAX * UMTRX_TX_AMPL;
+ else
+ return (double) SHRT_MAX * USRP_TX_AMPL;
+}
+
+double uhd_device::fullScaleOutputValue()
+{
+ return (double) SHRT_MAX;
+}
+
+bool uhd_device::recv_async_msg()
+{
+ uhd::async_metadata_t md;
+
+ thread_enable_cancel(false);
+ bool rc = usrp_dev->get_device()->recv_async_msg(md);
+ thread_enable_cancel(true);
+ if (!rc)
+ return false;
+
+ // Assume that any error requires resynchronization
+ if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
+ aligned = false;
+
+ if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
+ (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
+ LOGC(DDEV, ERR) << str_code(md);
+ }
+ }
+
+ return true;
+}
+
+std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
+{
+ std::ostringstream ost("UHD: ");
+
+ switch (metadata.error_code) {
+ case uhd::rx_metadata_t::ERROR_CODE_NONE:
+ ost << "No error";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
+ ost << "No packet received, implementation timed-out";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
+ ost << "A stream command was issued in the past";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
+ ost << "Expected another stream command";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
+ ost << "An internal receive buffer has filled";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
+ ost << "Multi-channel alignment failed";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
+ ost << "The packet could not be parsed";
+ break;
+ default:
+ ost << "Unknown error " << metadata.error_code;
+ }
+
+ if (metadata.has_time_spec)
+ ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
+
+ return ost.str();
+}
+
+std::string uhd_device::str_code(uhd::async_metadata_t metadata)
+{
+ std::ostringstream ost("UHD: ");
+
+ switch (metadata.event_code) {
+ case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
+ ost << "A packet was successfully transmitted";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
+ ost << "An internal send buffer has emptied";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
+ ost << "Packet loss between host and device";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
+ ost << "Packet time was too late or too early";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
+ ost << "Underflow occurred inside a packet";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
+ ost << "Packet loss within a burst";
+ break;
+ default:
+ ost << "Unknown error " << metadata.event_code;
+ }
+
+ if (metadata.has_time_spec)
+ ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
+
+ return ost.str();
+}
+
+smpl_buf::smpl_buf(size_t len, double rate)
+ : buf_len(len), clk_rt(rate),
+ time_start(0), time_end(0), data_start(0), data_end(0)
+{
+ data = new uint32_t[len];
+}
+
+smpl_buf::~smpl_buf()
+{
+ delete[] data;
+}
+
+ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
+{
+ if (timestamp < time_start)
+ return ERROR_TIMESTAMP;
+ else if (timestamp >= time_end)
+ return 0;
+ else
+ return time_end - timestamp;
+}
+
+ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
+{
+ return avail_smpls(timespec.to_ticks(clk_rt));
+}
+
+ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
+{
+ int type_sz = 2 * sizeof(short);
+
+ // Check for valid read
+ if (timestamp < time_start)
+ return ERROR_TIMESTAMP;
+ if (timestamp >= time_end)
+ return 0;
+ if (len >= buf_len)
+ return ERROR_READ;
+
+ // How many samples should be copied
+ size_t num_smpls = time_end - timestamp;
+ if (num_smpls > len)
+ num_smpls = len;
+
+ // Starting index
+ size_t read_start = (data_start + (timestamp - time_start)) % 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::read(void *buf, size_t len, uhd::time_spec_t ts)
+{
+ return read(buf, len, ts.to_ticks(clk_rt));
+}
+
+ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
+{
+ int type_sz = 2 * sizeof(short);
+
+ // Check for valid write
+ if ((len == 0) || (len >= buf_len))
+ return ERROR_WRITE;
+ if ((timestamp + len) <= time_end)
+ return ERROR_TIMESTAMP;
+
+ if (timestamp < time_end) {
+ LOGC(DDEV, ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
+ uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
+ LOGC(DDEV, DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
+ // 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;
+ uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
+ LOGC(DDEV, DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
+ // 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;
+}
+
+ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
+{
+ return write(buf, len, ts.to_ticks(clk_rt));
+}
+
+std::string smpl_buf::str_status(size_t ts) const
+{
+ std::ostringstream ost("Sample buffer: ");
+
+ ost << "timestamp = " << ts;
+ ost << ", length = " << buf_len;
+ ost << ", time_start = " << time_start;
+ ost << ", time_end = " << time_end;
+ ost << ", data_start = " << data_start;
+ ost << ", data_end = " << data_end;
+
+ return ost.str();
+}
+
+std::string smpl_buf::str_code(ssize_t code)
+{
+ switch (code) {
+ case ERROR_TIMESTAMP:
+ return "Sample buffer: Requested timestamp is not valid";
+ case ERROR_READ:
+ return "Sample buffer: Read error";
+ case ERROR_WRITE:
+ return "Sample buffer: Write error";
+ case ERROR_OVERFLOW:
+ return "Sample buffer: Overrun";
+ default:
+ return "Sample buffer: Unknown error";
+ }
+}
+
+RadioDevice *RadioDevice::make(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)
+{
+ return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
+}
diff --git a/Transceiver52M/device/usrp1/Makefile.am b/Transceiver52M/device/usrp1/Makefile.am
new file mode 100644
index 0000000..d99874a
--- /dev/null
+++ b/Transceiver52M/device/usrp1/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
+AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS)
+
+noinst_HEADERS = USRPDevice.h
+
+noinst_LTLIBRARIES = libdevice.la
+
+libdevice_la_SOURCES = USRPDevice.cpp
diff --git a/Transceiver52M/device/usrp1/USRPDevice.cpp b/Transceiver52M/device/usrp1/USRPDevice.cpp
new file mode 100644
index 0000000..5d19514
--- /dev/null
+++ b/Transceiver52M/device/usrp1/USRPDevice.cpp
@@ -0,0 +1,671 @@
+/*
+* Copyright 2008, 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/>.
+
+*/
+
+
+/*
+ Compilation Flags
+
+ SWLOOPBACK compile for software loopback testing
+*/
+
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include "Logger.h"
+#include "Threads.h"
+#include "USRPDevice.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+using namespace std;
+
+enum dboardConfigType {
+ TXA_RXB,
+ TXB_RXA,
+ TXA_RXA,
+ TXB_RXB
+};
+
+#ifdef SINGLEDB
+const dboardConfigType dboardConfig = TXA_RXA;
+#else
+const dboardConfigType dboardConfig = TXA_RXB;
+#endif
+
+const double USRPDevice::masterClockRate = 52.0e6;
+
+USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
+ size_t chans, double lo_offset,
+ const std::vector<std::string>& tx_paths,
+ const std::vector<std::string>& rx_paths):
+ RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths)
+{
+ LOGC(DDEV, INFO) << "creating USRP device...";
+
+ decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
+ actualSampleRate = masterClockRate/decimRate;
+ rxGain = 0;
+
+ /*
+ * Undetermined delay b/w ping response timestamp and true
+ * receive timestamp. Values are empirically measured. With
+ * split sample rate Tx/Rx - 4/1 sps we need to need to
+ * compensate for advance rather than delay.
+ */
+ if (tx_sps == 1)
+ pingOffset = 272;
+ else if (tx_sps == 4)
+ pingOffset = 269 - 7500;
+ else
+ pingOffset = 0;
+
+#ifdef SWLOOPBACK
+ samplePeriod = 1.0e6/actualSampleRate;
+ loopbackBufferSize = 0;
+ gettimeofday(&lastReadTime,NULL);
+ firstRead = false;
+#endif
+}
+
+int USRPDevice::open(const std::string &, int, bool)
+{
+ writeLock.unlock();
+
+ LOGC(DDEV, INFO) << "opening USRP device..";
+#ifndef SWLOOPBACK
+ string rbf = "std_inband.rbf";
+ //string rbf = "inband_1rxhb_1tx.rbf";
+ m_uRx.reset();
+ if (!skipRx) {
+ try {
+ m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
+ 0, decimRate * tx_sps, 1, -1,
+ usrp_standard_rx::FPGA_MODE_NORMAL,
+ 1024, 16 * 8, rbf));
+ m_uRx->set_fpga_master_clock_freq(masterClockRate);
+ }
+
+ catch(...) {
+ LOGC(DDEV, ALERT) << "make failed on Rx";
+ m_uRx.reset();
+ return -1;
+ }
+
+ if (m_uRx->fpga_master_clock_freq() != masterClockRate)
+ {
+ LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
+ << ", desired clock freq = " << masterClockRate;
+ m_uRx.reset();
+ return -1;
+ }
+ }
+
+ try {
+ m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
+ 0, decimRate * 2, 1, -1,
+ 1024, 16 * 8, rbf));
+ m_uTx->set_fpga_master_clock_freq(masterClockRate);
+ }
+
+ catch(...) {
+ LOGC(DDEV, ALERT) << "make failed on Tx";
+ m_uTx.reset();
+ return -1;
+ }
+
+ if (m_uTx->fpga_master_clock_freq() != masterClockRate)
+ {
+ LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
+ << ", desired clock freq = " << masterClockRate;
+ m_uTx.reset();
+ return -1;
+ }
+
+ if (!skipRx) m_uRx->stop();
+ m_uTx->stop();
+
+#endif
+
+ switch (dboardConfig) {
+ case TXA_RXB:
+ txSubdevSpec = usrp_subdev_spec(0,0);
+ rxSubdevSpec = usrp_subdev_spec(1,0);
+ break;
+ case TXB_RXA:
+ txSubdevSpec = usrp_subdev_spec(1,0);
+ rxSubdevSpec = usrp_subdev_spec(0,0);
+ break;
+ case TXA_RXA:
+ txSubdevSpec = usrp_subdev_spec(0,0);
+ rxSubdevSpec = usrp_subdev_spec(0,0);
+ break;
+ case TXB_RXB:
+ txSubdevSpec = usrp_subdev_spec(1,0);
+ rxSubdevSpec = usrp_subdev_spec(1,0);
+ break;
+ default:
+ txSubdevSpec = usrp_subdev_spec(0,0);
+ rxSubdevSpec = usrp_subdev_spec(1,0);
+ }
+
+ m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
+ m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
+
+ samplesRead = 0;
+ samplesWritten = 0;
+ started = false;
+
+ return NORMAL;
+}
+
+
+
+bool USRPDevice::start()
+{
+ LOGC(DDEV, INFO) << "starting USRP...";
+#ifndef SWLOOPBACK
+ if (!m_uRx && !skipRx) return false;
+ if (!m_uTx) return false;
+
+ if (!skipRx) m_uRx->stop();
+ m_uTx->stop();
+
+ writeLock.lock();
+ // power up and configure daughterboards
+ m_dbTx->set_enable(true);
+ m_uTx->set_mux(m_uTx->determine_tx_mux_value(txSubdevSpec));
+ m_uRx->set_mux(m_uRx->determine_rx_mux_value(rxSubdevSpec));
+
+ if (!m_dbRx->select_rx_antenna(1))
+ m_dbRx->select_rx_antenna(0);
+
+ writeLock.unlock();
+
+ // Set gains to midpoint
+ setTxGain((minTxGain() + maxTxGain()) / 2);
+ setRxGain((minRxGain() + maxRxGain()) / 2);
+
+ data = new short[currDataSize];
+ dataStart = 0;
+ dataEnd = 0;
+ timeStart = 0;
+ timeEnd = 0;
+ timestampOffset = 0;
+ latestWriteTimestamp = 0;
+ lastPktTimestamp = 0;
+ hi32Timestamp = 0;
+ isAligned = false;
+
+
+ if (!skipRx)
+ started = (m_uRx->start() && m_uTx->start());
+ else
+ started = m_uTx->start();
+ return started;
+#else
+ gettimeofday(&lastReadTime,NULL);
+ return true;
+#endif
+}
+
+bool USRPDevice::stop()
+{
+#ifndef SWLOOPBACK
+ if (!m_uRx) return false;
+ if (!m_uTx) return false;
+
+ delete[] currData;
+
+ started = !(m_uRx->stop() && m_uTx->stop());
+ return !started;
+#else
+ return true;
+#endif
+}
+
+double USRPDevice::maxTxGain()
+{
+ return m_dbTx->gain_max();
+}
+
+double USRPDevice::minTxGain()
+{
+ return m_dbTx->gain_min();
+}
+
+double USRPDevice::maxRxGain()
+{
+ return m_dbRx->gain_max();
+}
+
+double USRPDevice::minRxGain()
+{
+ return m_dbRx->gain_min();
+}
+
+double USRPDevice::setTxGain(double dB, size_t chan)
+{
+ if (chan) {
+ LOGC(DDEV, ALERT) << "Invalid channel " << chan;
+ return 0.0;
+ }
+
+ writeLock.lock();
+ if (dB > maxTxGain())
+ dB = maxTxGain();
+ if (dB < minTxGain())
+ dB = minTxGain();
+
+ LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
+
+ if (!m_dbTx->set_gain(dB))
+ LOGC(DDEV, ERR) << "Error setting TX gain";
+
+ writeLock.unlock();
+
+ return dB;
+}
+
+
+double USRPDevice::setRxGain(double dB, size_t chan)
+{
+ if (chan) {
+ LOGC(DDEV, ALERT) << "Invalid channel " << chan;
+ return 0.0;
+ }
+
+ dB = 47.0;
+
+ writeLock.lock();
+ if (dB > maxRxGain())
+ dB = maxRxGain();
+ if (dB < minRxGain())
+ dB = minRxGain();
+
+ LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB.";
+
+ if (!m_dbRx->set_gain(dB))
+ LOGC(DDEV, ERR) << "Error setting RX gain";
+
+ writeLock.unlock();
+
+ return dB;
+}
+
+bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
+{
+ if (chan >= rx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+ LOGC(DDEV, ALERT) << "Not implemented";
+ return true;
+}
+
+std::string USRPDevice::getRxAntenna(size_t chan)
+{
+ if (chan >= rx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return "";
+ }
+ LOGC(DDEV, ALERT) << "Not implemented";
+ return "";
+}
+
+bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
+{
+ if (chan >= tx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+ LOGC(DDEV, ALERT) << "Not implemented";
+ return true;
+}
+
+std::string USRPDevice::getTxAntenna(size_t chan)
+{
+ if (chan >= tx_paths.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return "";
+ }
+ LOGC(DDEV, ALERT) << "Not implemented";
+ return "";
+}
+
+bool USRPDevice::requiresRadioAlign()
+{
+ return true;
+}
+
+GSM::Time USRPDevice::minLatency() {
+ return GSM::Time(1,1);
+}
+
+// NOTE: Assumes sequential reads
+int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
+ TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
+{
+#ifndef SWLOOPBACK
+ if (!m_uRx)
+ return 0;
+
+ short *buf = bufs[0];
+
+ timestamp += timestampOffset;
+
+ if (timestamp + len < timeStart) {
+ memset(buf,0,len*2*sizeof(short));
+ return len;
+ }
+
+ if (underrun) *underrun = false;
+
+ uint32_t readBuf[2000];
+
+ while (1) {
+ //guestimate USB read size
+ int readLen=0;
+ {
+ int numSamplesNeeded = timestamp + len - timeEnd;
+ if (numSamplesNeeded <=0) break;
+ readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
+ if (readLen > 8000) readLen= (8000/512)*512;
+ }
+
+ // read USRP packets, parse and save A/D data as needed
+ readLen = m_uRx->read((void *)readBuf,readLen,overrun);
+ for (int pktNum = 0; pktNum < (readLen/512); pktNum++) {
+ // tmpBuf points to start of a USB packet
+ uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
+ TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
+ uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
+ uint32_t chan = (word0 >> 16) & 0x1f;
+ unsigned payloadSz = word0 & 0x1ff;
+ LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
+
+ bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
+ if (incrementHi32 && (timeStart!=0)) {
+ LOGC(DDEV, DEBUG) << "high 32 increment!!!";
+ hi32Timestamp++;
+ }
+ pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
+ lastPktTimestamp = pktTimestamp;
+
+ if (chan == 0x01f) {
+ // control reply, check to see if its ping reply
+ uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
+ if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
+ timestamp -= timestampOffset;
+ timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
+ LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset;
+ timestamp += timestampOffset;
+ isAligned = true;
+ }
+ continue;
+ }
+ if (chan != 0) {
+ LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
+ continue;
+ }
+ if ((word0 >> 28) & 0x04) {
+ if (underrun) *underrun = true;
+ LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
+ }
+ if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
+
+ if (!isAligned) continue;
+
+ unsigned cursorStart = pktTimestamp - timeStart + dataStart;
+ while (cursorStart*2 > currDataSize) {
+ cursorStart -= currDataSize/2;
+ }
+ if (cursorStart*2 + payloadSz/2 > currDataSize) {
+ // need to circle around buffer
+ memcpy(data+cursorStart*2,tmpBuf+2,(currDataSize-cursorStart*2)*sizeof(short));
+ memcpy(data,tmpBuf+2+(currDataSize/2-cursorStart),payloadSz-(currDataSize-cursorStart*2)*sizeof(short));
+ }
+ else {
+ memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
+ }
+ if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
+ timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
+
+ LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
+
+ }
+ }
+
+ // copy desired data to buf
+ unsigned bufStart = dataStart+(timestamp-timeStart);
+ if (bufStart + len < currDataSize/2) {
+ LOGC(DDEV, DEBUG) << "bufStart: " << bufStart;
+ memcpy(buf,data+bufStart*2,len*2*sizeof(short));
+ memset(data+bufStart*2,0,len*2*sizeof(short));
+ }
+ else {
+ LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
+ unsigned firstLength = (currDataSize/2-bufStart);
+ LOGC(DDEV, DEBUG) << "firstLength: " << firstLength;
+ memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
+ memset(data+bufStart*2,0,firstLength*2*sizeof(short));
+ memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
+ memset(data,0,(len-firstLength)*2*sizeof(short));
+ }
+ dataStart = (bufStart + len) % (currDataSize/2);
+ timeStart = timestamp + len;
+
+ return len;
+
+#else
+ if (loopbackBufferSize < 2) return 0;
+ int numSamples = 0;
+ struct timeval currTime;
+ gettimeofday(&currTime,NULL);
+ double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
+ (currTime.tv_usec - lastReadTime.tv_usec);
+ if (timeElapsed < samplePeriod) {return 0;}
+ int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
+ if (numSamplesToRead < len) return 0;
+
+ if (numSamplesToRead > len) numSamplesToRead = len;
+ if (numSamplesToRead > loopbackBufferSize/2) {
+ firstRead =false;
+ numSamplesToRead = loopbackBufferSize/2;
+ }
+ memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
+ loopbackBufferSize -= 2*numSamplesToRead;
+ memcpy(loopbackBuffer,loopbackBuffer+2*numSamplesToRead,
+ sizeof(short)*loopbackBufferSize);
+ numSamples = numSamplesToRead;
+ if (firstRead) {
+ int new_usec = lastReadTime.tv_usec + (int) round((double) numSamplesToRead * samplePeriod);
+ lastReadTime.tv_sec = lastReadTime.tv_sec + new_usec/1000000;
+ lastReadTime.tv_usec = new_usec % 1000000;
+ }
+ else {
+ gettimeofday(&lastReadTime,NULL);
+ firstRead = true;
+ }
+ samplesRead += numSamples;
+
+ return numSamples;
+#endif
+}
+
+int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
+ bool *underrun, unsigned long long timestamp,
+ bool isControl)
+{
+ writeLock.lock();
+
+#ifndef SWLOOPBACK
+ if (!m_uTx)
+ return 0;
+
+ short *buf = bufs[0];
+
+ static uint32_t outData[128*20];
+
+ for (int i = 0; i < len*2; i++) {
+ buf[i] = host_to_usrp_short(buf[i]);
+ }
+
+ int numWritten = 0;
+ unsigned isStart = 1;
+ unsigned RSSI = 0;
+ unsigned CHAN = (isControl) ? 0x01f : 0x00;
+ len = len*2*sizeof(short);
+ int numPkts = (int) ceil((float)len/(float)504);
+ unsigned isEnd = (numPkts < 2);
+ uint32_t *outPkt = outData;
+ int pktNum = 0;
+ while (numWritten < len) {
+ // pkt is pointer to start of a USB packet
+ uint32_t *pkt = outPkt + pktNum*128;
+ isEnd = (len - numWritten <= 504);
+ unsigned payloadLen = ((len - numWritten) < 504) ? (len-numWritten) : 504;
+ pkt[0] = (isStart << 12 | isEnd << 11 | (RSSI & 0x3f) << 5 | CHAN) << 16 | payloadLen;
+ pkt[1] = timestamp & 0x0ffffffffll;
+ memcpy(pkt+2,buf+(numWritten/sizeof(short)),payloadLen);
+ numWritten += payloadLen;
+ timestamp += payloadLen/2/sizeof(short);
+ isStart = 0;
+ pkt[0] = host_to_usrp_u32(pkt[0]);
+ pkt[1] = host_to_usrp_u32(pkt[1]);
+ pktNum++;
+ }
+ m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
+
+ samplesWritten += len/2/sizeof(short);
+ writeLock.unlock();
+
+ return len/2/sizeof(short);
+#else
+ int retVal = len;
+ memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
+ samplesWritten += retVal;
+ loopbackBufferSize += retVal*2;
+
+ return retVal;
+#endif
+}
+
+bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
+{
+#ifndef SWLOOPBACK
+ short data[] = {0x00,0x02,0x00,0x00};
+ uint32_t *wordPtr = (uint32_t *) data;
+ *wordPtr = host_to_usrp_u32(*wordPtr);
+ bool tmpUnderrun;
+
+ std::vector<short *> buf(1, data);
+ if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
+ pingTimestamp = timestamp;
+ return true;
+ }
+ return false;
+#else
+ return true;
+#endif
+}
+
+#ifndef SWLOOPBACK
+bool USRPDevice::setTxFreq(double wFreq, size_t chan)
+{
+ usrp_tune_result result;
+
+ if (chan) {
+ LOGC(DDEV, ALERT) << "Invalid channel " << chan;
+ return false;
+ }
+
+ if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
+ LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl
+ << " baseband freq: " << result.baseband_freq << std::endl
+ << " DDC freq: " << result.dxc_freq << std::endl
+ << " residual freq: " << result.residual_freq;
+ return true;
+ }
+ else {
+ LOGC(DDEV, ALERT) << "set TX: " << wFreq << "failed" << std::endl
+ << " baseband freq: " << result.baseband_freq << std::endl
+ << " DDC freq: " << result.dxc_freq << std::endl
+ << " residual freq: " << result.residual_freq;
+ return false;
+ }
+}
+
+bool USRPDevice::setRxFreq(double wFreq, size_t chan)
+{
+ usrp_tune_result result;
+
+ if (chan) {
+ LOGC(DDEV, ALERT) << "Invalid channel " << chan;
+ return false;
+ }
+
+ if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
+ LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl
+ << " baseband freq: " << result.baseband_freq << std::endl
+ << " DDC freq: " << result.dxc_freq << std::endl
+ << " residual freq: " << result.residual_freq;
+ return true;
+ }
+ else {
+ LOGC(DDEV, ALERT) << "set RX: " << wFreq << "failed" << std::endl
+ << " baseband freq: " << result.baseband_freq << std::endl
+ << " DDC freq: " << result.dxc_freq << std::endl
+ << " residual freq: " << result.residual_freq;
+ return false;
+ }
+
+}
+
+#else
+bool USRPDevice::setTxFreq(double wFreq) { return true;};
+bool USRPDevice::setRxFreq(double wFreq) { return true;};
+#endif
+
+RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
+ InterfaceType iface, size_t chans, double lo_offset,
+ const std::vector<std::string>& tx_paths,
+ const std::vector<std::string>& rx_paths)
+{
+ if (tx_sps != rx_sps) {
+ LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
+ return NULL;
+ }
+ if (chans != 1) {
+ LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
+ return NULL;
+ }
+ if (lo_offset != 0.0) {
+ LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
+ return NULL;
+ }
+ return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
+}
diff --git a/Transceiver52M/device/usrp1/USRPDevice.h b/Transceiver52M/device/usrp1/USRPDevice.h
new file mode 100644
index 0000000..451b5a9
--- /dev/null
+++ b/Transceiver52M/device/usrp1/USRPDevice.h
@@ -0,0 +1,209 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* 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 _USRP_DEVICE_H_
+#define _USRP_DEVICE_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "radioDevice.h"
+
+#include <usrp/usrp_standard.h>
+#include <usrp/usrp_bytesex.h>
+#include <usrp/usrp_prims.h>
+#include <sys/time.h>
+#include <math.h>
+#include <string>
+#include <iostream>
+
+#include <boost/shared_ptr.hpp>
+typedef boost::shared_ptr<usrp_standard_tx> usrp_standard_tx_sptr;
+typedef boost::shared_ptr<usrp_standard_rx> usrp_standard_rx_sptr;
+
+/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
+class USRPDevice: public RadioDevice {
+
+private:
+
+ static const double masterClockRate; ///< the USRP clock rate
+ double desiredSampleRate; ///< the desired sampling rate
+ usrp_standard_rx_sptr m_uRx; ///< the USRP receiver
+ usrp_standard_tx_sptr m_uTx; ///< the USRP transmitter
+
+ db_base_sptr m_dbRx; ///< rx daughterboard
+ db_base_sptr m_dbTx; ///< tx daughterboard
+ usrp_subdev_spec rxSubdevSpec;
+ usrp_subdev_spec txSubdevSpec;
+
+ double actualSampleRate; ///< the actual USRP sampling rate
+ unsigned int decimRate; ///< the USRP decimation rate
+
+ unsigned long long samplesRead; ///< number of samples read from USRP
+ unsigned long long samplesWritten; ///< number of samples sent to USRP
+
+ bool started; ///< flag indicates USRP has started
+ bool skipRx; ///< set if USRP is transmit-only.
+
+ static const unsigned int currDataSize_log2 = 21;
+ static const unsigned long currDataSize = (1 << currDataSize_log2);
+ short *data;
+ unsigned long dataStart;
+ unsigned long dataEnd;
+ TIMESTAMP timeStart;
+ TIMESTAMP timeEnd;
+ bool isAligned;
+
+ Mutex writeLock;
+
+ short *currData; ///< internal data buffer when reading from USRP
+ TIMESTAMP currTimestamp; ///< timestamp of internal data buffer
+ unsigned currLen; ///< size of internal data buffer
+
+ TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks
+ TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command
+ TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response
+
+ long long pingOffset;
+ unsigned long hi32Timestamp;
+ unsigned long lastPktTimestamp;
+
+ double rxGain;
+
+#ifdef SWLOOPBACK
+ short loopbackBuffer[1000000];
+ int loopbackBufferSize;
+ double samplePeriod;
+
+ struct timeval startTime;
+ struct timeval lastReadTime;
+ bool firstRead;
+#endif
+
+ public:
+
+ /** Object constructor */
+ USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
+ const std::vector<std::string>& tx_paths,
+ const std::vector<std::string>& rx_paths);
+
+ /** Instantiate the USRP */
+ int open(const std::string &, int, bool);
+
+ /** Start the USRP */
+ bool start();
+
+ /** Stop the USRP */
+ bool stop();
+
+ /** Set priority not supported */
+ void setPriority(float prio = 0.5) { }
+
+ enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
+
+ /**
+ Read samples from the USRP.
+ @param buf preallocated buf to contain read result
+ @param len number of samples desired
+ @param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
+ @param timestamp The timestamp of the first samples to be read
+ @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
+ @param RSSI The received signal strength of the read result
+ @return The number of samples actually read
+ */
+ int readSamples(std::vector<short *> &buf, int len, bool *overrun,
+ TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
+ unsigned *RSSI = NULL);
+ /**
+ Write samples to the USRP.
+ @param buf Contains the data to be written.
+ @param len number of samples to write.
+ @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
+ @param timestamp The timestamp of the first sample of the data buffer.
+ @param isControl Set if data is a control packet, e.g. a ping command
+ @return The number of samples actually written
+ */
+ int writeSamples(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 20000;}
+
+ /** Returns the starting read Timestamp*/
+ TIMESTAMP initialReadTimestamp(void) { return 20000;}
+
+ /** returns the full-scale transmit amplitude **/
+ double fullScaleInputValue() {return 13500.0;}
+
+ /** returns the full-scale receive amplitude **/
+ double fullScaleOutputValue() {return 9450.0;}
+
+ /** sets the receive chan gain, returns the gain setting **/
+ double setRxGain(double dB, size_t chan = 0);
+
+ /** get the current receive gain */
+ double getRxGain(size_t chan = 0) { return rxGain; }
+
+ /** return maximum Rx Gain **/
+ double maxRxGain(void);
+
+ /** return minimum Rx Gain **/
+ double minRxGain(void);
+
+ /** sets the transmit chan gain, returns the gain setting **/
+ double setTxGain(double dB, size_t chan = 0);
+
+ /** return maximum Tx Gain **/
+ double maxTxGain(void);
+
+ /** return minimum Rx Gain **/
+ double minTxGain(void);
+
+ /** sets the RX path to use, returns true if successful and false otherwise */
+ bool setRxAntenna(const std::string &ant, size_t chan = 0);
+
+ /* return the used RX path */
+ std::string getRxAntenna(size_t chan = 0);
+
+ /** sets the RX path to use, returns true if successful and false otherwise */
+ bool setTxAntenna(const std::string &ant, size_t chan = 0);
+
+ /* return the used RX path */
+ std::string getTxAntenna(size_t chan = 0);
+
+ /** return whether user drives synchronization of Tx/Rx of USRP */
+ bool requiresRadioAlign();
+
+ /** return whether user drives synchronization of Tx/Rx of USRP */
+ virtual GSM::Time minLatency();
+
+ /** Return internal status values */
+ inline double getTxFreq(size_t chan = 0) { return 0; }
+ inline double getRxFreq(size_t chan = 0) { return 0; }
+ inline double getSampleRate() { return actualSampleRate; }
+ inline double numberRead() { return samplesRead; }
+ inline double numberWritten() { return samplesWritten; }
+};
+
+#endif // _USRP_DEVICE_H_
diff --git a/Transceiver52M/inband-signaling-usb b/Transceiver52M/inband-signaling-usb
new file mode 100644
index 0000000..14f8347
--- /dev/null
+++ b/Transceiver52M/inband-signaling-usb
@@ -0,0 +1,314 @@
+This file specifies the format of USB packets used for in-band data
+transmission and signaling on the USRP. All packets are 512-byte long,
+and are transfered using USB "bulk" transfers.
+
+IN packets are sent towards the host.
+OUT packets are sent away from the host.
+
+The layout is 32-bits wide. All data is transmitted in little-endian
+format across the USB.
+
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |O|U|D|S|E| RSSI | Chan | mbz | Tag | Payload Len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Timestamp |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + +
+ | Payload |
+ . .
+ . .
+ . .
+ | |
+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... | .
+ +-+-+-+-+-+-+-+ .
+ . .
+ . Padding .
+ . .
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+ mbz Must be Zero: these bits must be zero in both IN and OUT packets.
+
+ O Overrun Flag: set in an IN packet if an overrun condition was
+ detected. Must be zero in OUT packets. Overrun occurs when
+ the FPGA has data to transmit to the host and there is no
+ buffer space available. This generally indicates a problem on
+ the host. Either it is not keeping up, or it has configured
+ the FPGA to transmit data at a higher rate than the transport
+ (USB) can support.
+
+ U Underrun Flag: set in an IN packet if an underrun condition
+ was detected. Must be zero in OUT packets. Underrun occurs
+ when the FPGA runs out of samples, and it's not between
+ bursts. See the "End of Burst flag" below.
+
+ D Dropped Packet Flag: Set in an IN packet if the FPGA
+ discarded an OUT packet because its timestamp had already
+ passed.
+
+ S Start of Burst Flag: Set in an OUT packet if the data is the
+ first segment of what is logically a continuous burst of data.
+ Must be zero in IN packets.
+
+ E End of Burst Flag: Set in an OUT packet if the data is the
+ last segment of what is logically a continuous burst of data.
+ Must be zero in IN packets. Underruns are not reported
+ when the FPGA runs out of samples between bursts.
+
+
+ RSSI 6-bit Received Strength Signal Indicator: Must be zero in OUT
+ packets. In IN packets, indicates RSSI as reported by front end.
+ FIXME The format and interpretation are to be determined.
+
+ Chan 5-bit logical channel number. Channel number 0x1f is reserved
+ for control information. See "Control Channel" below. Other
+ channels are "data channels." Each data channel is logically
+ independent of the others. A data channel payload field
+ contains a sequence of homogeneous samples. The format of the
+ samples is determined by the configuration associated with the
+ given channel. It is often the case that the payload field
+ contains 32-bit complex samples, each containing 16-bit real
+ and imaginary components.
+
+ Tag 4-bit tag for matching IN packets with OUT packets.
+ [FIXME, write more...]
+
+ Payload Len: 9-bit field that specifies the length of the payload
+ field in bytes. Must be in the range 0 to 504 inclusive.
+
+ Timestamp: 32-bit timestamp.
+ On IN packets, the timestamp indicates the time at which the
+ first sample of the packet was produced by the A/D converter(s)
+ for that channel. On OUT packets, the timestamp specifies the
+ time at which the first sample in the packet should go out the
+ D/A converter(s) for that channel. If a packet reaches the
+ head of the transmit queue, and the current time is later than
+ the timestamp, an error is assumed to have occurred and the
+ packet is discarded. As a special case, the timestamp
+ 0xffffffff is interpreted as "Now".
+
+ The time base is a free running 32-bit counter that is
+ incremented by the A/D sample-clock.
+
+ Payload: Variable length field. Length is specified by the
+ Payload Len field.
+
+ Padding: This field is 504 - Payload Len bytes long, and its content
+ is unspecified. This field pads the packet out to a constant
+ 512 bytes.
+
+
+
+"Data Channel" payload format:
+-------------------------------
+
+If Chan != 0x1f, the packet is a "data packet" and the payload is a
+sequence of homogeneous samples. The format of the samples is
+determined by the configuration associated with the given channel.
+It is often the case that the payload field contains 32-bit complex
+samples, each containing 16-bit real and imaginary components.
+
+
+"Control Channel" payload format:
+---------------------------------
+
+If Chan == 0x1f, the packet is a "control packet". The control channel
+payload consists of a sequence of 0 or more sub-packets.
+
+Each sub-packet starts on a 32-bit boundary, and consists of an 8-bit
+Opcode field, an 8-bit Length field, Length bytes of arguments, and 0,
+1, 2 or 3 bytes of padding to align the tail of the sub-packet to
+a 32-bit boundary.
+
+Control channel packets shall be processed at the head of the queue,
+and shall observe the timestamp semantics described above.
+
+
+General sub-packet format:
+--------------------------
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+
+ | Opcode | Length | <length bytes> ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+
+
+
+Specific sub-packet formats:
+----------------------------
+
+ RID: 6-bit Request-ID. Copied from request sub-packet into corresponding
+ reply sub-packet. RID allows the host to match requests and replies.
+
+ Reg Number: 10-bit Register Number.
+
+
+
+Ping Fixed Length:
+
+ Opcode: OP_PING_FIXED
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 2 | RID | Ping Value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+Ping Fixed Length Reply:
+
+ Opcode: OP_PING_FIXED_REPLY
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 2 | RID | Ping Value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+Write Register:
+
+ Opcode: OP_WRITE_REG
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 6 | mbz | Reg Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Register Value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+Write Register Masked:
+
+ Opcode: OP_WRITE_REG_MASKED
+
+ REG[Num] = (REG[Num] & ~Mask) | (Value & Mask)
+
+ That is, only the register bits that correspond to 1's in the
+ mask are written with the new value.
+
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 10 | mbz | Reg Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Register Value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Mask Value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+Read Register:
+
+ Opcode: OP_READ_REG
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 2 | RID | Reg Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+Read Register Reply:
+
+ Opcode: OP_READ_REG_REPLY
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 6 | RID | Reg Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Register Value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+I2C Write:
+
+ Opcode: OP_I2C_WRITE
+ I2C Addr: 7-bit I2C address
+ Data: The bytes to write to the I2C bus
+ Length: Length of Data + 2
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | Length | mbz | I2C Addr |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Data ... .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+I2C Read:
+
+ Opcode: OP_I2C_READ
+ I2C Addr: 7-bit I2C address
+ Nbytes: Number of bytes to read from I2C bus
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 3 | RID | mbz | I2C Addr |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Nbytes | unspecified padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+I2C Read Reply:
+
+ Opcode: OP_I2C_READ_REPLY
+ I2C Addr: 7-bit I2C address
+ Data: Length - 2 bytes of data read from I2C bus.
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | Length | RID | mbz | I2C Addr |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Data ... .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+SPI Write:
+
+ Opcode: OP_SPI_WRITE
+ Enables: Which SPI enables to assert (mask)
+ Format: Specifies format of SPI data and Opt Header Bytes
+ Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format
+ Data: The bytes to write to the SPI bus
+ Length: Length of Data + 6
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | Length | mbz |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Enables | Format | Opt Header Bytes |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Data ... .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+SPI Read:
+
+ Opcode: OP_SPI_READ
+ Enables: Which SPI enables to assert (mask)
+ Format: Specifies format of SPI data and Opt Header Bytes
+ Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format
+ Nbytes: Number of bytes to read from SPI bus.
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 7 | RID | mbz |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Enables | Format | Opt Header Bytes |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Nbytes | unspecified padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+SPI Read Reply:
+
+ Opcode: OP_SPI_READ_REPLY
+ Data: Length - 2 bytes of data read from SPI bus.
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | Length | RID | mbz |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Data ... .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+Delay:
+
+ Opcode: OP_DELAY
+ Ticks: 16-bit unsigned delay count
+
+ Delay Ticks clock ticks before executing next operation.
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Opcode | 2 | Ticks |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
diff --git a/Transceiver52M/laurent.m b/Transceiver52M/laurent.m
new file mode 100644
index 0000000..ef15428
--- /dev/null
+++ b/Transceiver52M/laurent.m
@@ -0,0 +1,83 @@
+%
+% Laurent decomposition of GMSK signals
+% Generates C0, C1, and C2 pulse shapes
+%
+% Pierre Laurent, "Exact and Approximate Construction of Digital Phase
+% Modulations by Superposition of Amplitude Modulated Pulses", IEEE
+% Transactions of Communications, Vol. 34, No. 2, Feb 1986.
+%
+% Author: Thomas Tsou <tom@tsou.cc>
+%
+
+% Modulation parameters
+oversamp = 16;
+L = 3;
+f = 270.83333e3;
+T = 1/f;
+h = 0.5;
+BT = 0.30;
+B = BT / T;
+
+% Generate sampling points for L symbol periods
+t = -(L*T/2):T/oversamp:(L*T/2);
+t = t(1:end-1) + (T/oversamp/2);
+
+% Generate Gaussian pulse
+g = qfunc(2*pi*B*(t - T/2)/(log(2)^.5)) - qfunc(2*pi*B*(t + T/2)/(log(2)^.5));
+g = g / sum(g) * pi/2;
+g = [0 g];
+
+% Integrate phase
+q = 0;
+for i = 1:size(g,2);
+ q(i) = sum(g(1:i));
+end
+
+% Compute two sided "generalized phase pulse" function
+s = 0;
+for i = 1:size(g,2);
+ s(i) = sin(q(i)) / sin(pi*h);
+end
+for i = (size(g,2) + 1):(2 * size(g,2) - 1);
+ s(i) = sin(pi*h - q(i - (size(g,2) - 1))) / sin(pi*h);
+end
+
+% Compute C0 pulse: valid for all L values
+c0 = s(1:end-(oversamp*(L-1)));
+for i = 1:L-1;
+ c0 = c0 .* s((1 + i*oversamp):end-(oversamp*(L - 1 - i)));
+end
+
+% Compute C1 pulse: valid for L = 3 only!
+% C1 = S0 * S4 * S2
+c1 = s(1:end-(oversamp*(4)));
+c1 = c1 .* s((1 + 4*oversamp):end-(oversamp*(4 - 1 - 3)));
+c1 = c1 .* s((1 + 2*oversamp):end-(oversamp*(4 - 1 - 1)));
+
+% Compute C2 pulse: valid for L = 3 only!
+% C2 = S0 * S1 * S5
+c2 = s(1:end-(oversamp*(5)));
+c2 = c2 .* s((1 + 1*oversamp):end-(oversamp*(5 - 1 - 0)));
+c2 = c2 .* s((1 + 5*oversamp):end-(oversamp*(5 - 1 - 4)));
+
+% Plot C0, C1, C2 Laurent pulse series
+figure(1);
+hold off;
+plot((0:size(c0,2)-1)/oversamp - 2,c0, 'b');
+hold on;
+plot((0:size(c1,2)-1)/oversamp - 2,c1, 'r');
+plot((0:size(c2,2)-1)/oversamp - 2,c2, 'g');
+
+% Generate OpenBTS pulse
+numSamples = size(c0,2);
+centerPoint = (numSamples - 1)/2;
+i = ((0:numSamples) - centerPoint) / oversamp;
+xP = .96*exp(-1.1380*i.^2 - 0.527*i.^4);
+xP = xP / max(xP) * max(c0);
+
+% Plot C0 pulse compared to OpenBTS pulse
+figure(2);
+hold off;
+plot((0:size(c0,2)-1)/oversamp, c0, 'b');
+hold on;
+plot((0:size(xP,2)-1)/oversamp, xP, 'r');
diff --git a/Transceiver52M/osmo-trx.cpp b/Transceiver52M/osmo-trx.cpp
new file mode 100644
index 0000000..b6b676e
--- /dev/null
+++ b/Transceiver52M/osmo-trx.cpp
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Transceiver.h"
+#include "radioDevice.h"
+
+#include <time.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sched.h>
+#include <vector>
+#include <string>
+#include <sstream>
+#include <iostream>
+
+#include <GSMCommon.h>
+#include <Logger.h>
+
+extern "C" {
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/ports.h>
+#include <osmocom/vty/misc.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/ctrl/control_vty.h>
+#include <osmocom/ctrl/ports.h>
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/command.h>
+
+#include "convolve.h"
+#include "convert.h"
+#include "trx_vty.h"
+#include "debug.h"
+#include "osmo_signal.h"
+}
+
+#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
+
+#define charp2str(a) ((a) ? std::string(a) : std::string(""))
+
+static char* config_file = (char*)DEFAULT_CONFIG_FILE;
+
+volatile bool gshutdown = false;
+
+static void *tall_trx_ctx;
+static struct trx_ctx *g_trx_ctx;
+static struct ctrl_handle *g_ctrlh;
+
+static RadioDevice *usrp;
+static RadioInterface *radio;
+static Transceiver *transceiver;
+
+/* Create radio interface
+ * The interface consists of sample rate changes, frequency shifts,
+ * channel multiplexing, and other conversions. The transceiver core
+ * accepts input vectors sampled at multiples of the GSM symbol rate.
+ * The radio interface connects the main transceiver with the device
+ * object, which may be operating some other rate.
+ */
+RadioInterface *makeRadioInterface(struct trx_ctx *trx,
+ RadioDevice *usrp, int type)
+{
+ RadioInterface *radio = NULL;
+
+ switch (type) {
+ case RadioDevice::NORMAL:
+ radio = new RadioInterface(usrp, trx->cfg.tx_sps,
+ trx->cfg.rx_sps, trx->cfg.num_chans);
+ break;
+ case RadioDevice::RESAMP_64M:
+ case RadioDevice::RESAMP_100M:
+ radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
+ trx->cfg.rx_sps);
+ break;
+ case RadioDevice::MULTI_ARFCN:
+ radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
+ trx->cfg.rx_sps, trx->cfg.num_chans);
+ break;
+ default:
+ LOG(ALERT) << "Unsupported radio interface configuration";
+ return NULL;
+ }
+
+ if (!radio->init(type)) {
+ LOG(ALERT) << "Failed to initialize radio interface";
+ return NULL;
+ }
+
+ return radio;
+}
+
+/* Callback function to be called every time we receive a signal from TRANSC */
+static int transc_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ switch (signal) {
+ case S_TRANSC_STOP_REQUIRED:
+ gshutdown = true;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Create transceiver core
+ * The multi-threaded modem core operates at multiples of the GSM rate of
+ * 270.8333 ksps and consists of GSM specific modulation, demodulation,
+ * and decoding schemes. Also included are the socket interfaces for
+ * connecting to the upper layer stack.
+ */
+int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
+{
+ VectorFIFO *fifo;
+
+ transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr,
+ trx->cfg.remote_addr, trx->cfg.tx_sps,
+ trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0),
+ radio, trx->cfg.rssi_offset);
+ if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc,
+ trx->cfg.rach_delay, trx->cfg.egprs)) {
+ LOG(ALERT) << "Failed to initialize transceiver";
+ return -1;
+ }
+
+ transceiver->setSignalHandler(transc_sig_cb);
+
+ for (size_t i = 0; i < trx->cfg.num_chans; i++) {
+ fifo = radio->receiveFIFO(i);
+ if (fifo && transceiver->receiveFIFO(fifo, i))
+ continue;
+
+ LOG(ALERT) << "Could not attach FIFO to channel " << i;
+ return -1;
+ }
+ return 0;
+}
+
+static void sig_handler(int signo)
+{
+ fprintf(stdout, "signal %d received\n", signo);
+ switch (signo) {
+ case SIGINT:
+ case SIGTERM:
+ fprintf(stdout, "shutting down\n");
+ gshutdown = true;
+ break;
+ case SIGABRT:
+ case SIGUSR1:
+ talloc_report(tall_trx_ctx, stderr);
+ talloc_report_full(tall_trx_ctx, stderr);
+ break;
+ case SIGUSR2:
+ talloc_report_full(tall_trx_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+static void setup_signal_handlers()
+{
+ /* Handle keyboard interrupt SIGINT */
+ signal(SIGINT, &sig_handler);
+ signal(SIGTERM, &sig_handler);
+ signal(SIGABRT, &sig_handler);
+ signal(SIGUSR1, &sig_handler);
+ signal(SIGUSR2, &sig_handler);
+ osmo_init_ignore_signals();
+}
+
+static std::vector<std::string> comma_delimited_to_vector(char* opt)
+{
+ std::string str = std::string(opt);
+ std::vector<std::string> result;
+ std::stringstream ss(str);
+
+ while( ss.good() )
+ {
+ std::string substr;
+ getline(ss, substr, ',');
+ result.push_back(substr);
+ }
+ return result;
+}
+
+static void print_help()
+{
+ fprintf(stdout, "Options:\n"
+ " -h, --help This text\n"
+ " -C, --config Filename The config file to use\n"
+ " -V, --version Print the version of OsmoTRX\n"
+ );
+}
+
+static void print_deprecated(char opt)
+{
+ LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
+ << " Please use VTY cfg option instead."
+ << " All cmd line options are already being overriden by VTY options if set.";
+}
+
+static void handle_options(int argc, char **argv, struct trx_ctx* trx)
+{
+ int option;
+ unsigned int i;
+ std::vector<std::string> rx_paths, tx_paths;
+ bool rx_paths_set = false, tx_paths_set = false;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"config", 1, 0, 'C'},
+ {"version", 0, 0, 'V'},
+ {NULL, 0, 0, 0}
+ };
+
+ while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options,
+ NULL)) != -1) {
+ switch (option) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 'a':
+ print_deprecated(option);
+ osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
+ break;
+ case 'l':
+ print_deprecated(option);
+ log_set_log_level(osmo_stderr_target, atoi(optarg));
+ break;
+ case 'i':
+ print_deprecated(option);
+ osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg);
+ break;
+ case 'j':
+ print_deprecated(option);
+ osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg);
+ break;
+ case 'p':
+ print_deprecated(option);
+ trx->cfg.base_port = atoi(optarg);
+ break;
+ case 'c':
+ print_deprecated(option);
+ trx->cfg.num_chans = atoi(optarg);
+ break;
+ case 'm':
+ print_deprecated(option);
+ trx->cfg.multi_arfcn = true;
+ break;
+ case 'x':
+ print_deprecated(option);
+ trx->cfg.clock_ref = REF_EXTERNAL;
+ break;
+ case 'g':
+ print_deprecated(option);
+ trx->cfg.clock_ref = REF_GPS;
+ break;
+ case 'f':
+ print_deprecated(option);
+ trx->cfg.filler = FILLER_DUMMY;
+ break;
+ case 'o':
+ print_deprecated(option);
+ trx->cfg.offset = atof(optarg);
+ break;
+ case 's':
+ print_deprecated(option);
+ trx->cfg.tx_sps = atoi(optarg);
+ break;
+ case 'b':
+ print_deprecated(option);
+ trx->cfg.rx_sps = atoi(optarg);
+ break;
+ case 'r':
+ print_deprecated(option);
+ trx->cfg.rtsc_set = true;
+ trx->cfg.rtsc = atoi(optarg);
+ if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
+ trx->cfg.filler = FILLER_NORM_RAND;
+ break;
+ case 'A':
+ print_deprecated(option);
+ trx->cfg.rach_delay_set = true;
+ trx->cfg.rach_delay = atoi(optarg);
+ trx->cfg.filler = FILLER_ACCESS_RAND;
+ break;
+ case 'R':
+ print_deprecated(option);
+ trx->cfg.rssi_offset = atof(optarg);
+ break;
+ case 'S':
+ print_deprecated(option);
+ trx->cfg.swap_channels = true;
+ break;
+ case 'e':
+ print_deprecated(option);
+ trx->cfg.egprs = true;
+ break;
+ case 't':
+ print_deprecated(option);
+ trx->cfg.sched_rr = atoi(optarg);
+ break;
+ case 'y':
+ print_deprecated(option);
+ tx_paths = comma_delimited_to_vector(optarg);
+ tx_paths_set = true;
+ break;
+ case 'z':
+ print_deprecated(option);
+ rx_paths = comma_delimited_to_vector(optarg);
+ rx_paths_set = true;
+ break;
+ case 'C':
+ config_file = optarg;
+ break;
+ case 'V':
+ print_version(1);
+ exit(0);
+ break;
+ default:
+ goto bad_config;
+ }
+ }
+
+ /* Cmd line option specific validation & setup */
+
+ if (trx->cfg.num_chans > TRX_CHAN_MAX) {
+ LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX;
+ goto bad_config;
+ }
+ if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) ||
+ (rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) {
+ LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match";
+ goto bad_config;
+ }
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ trx->cfg.chans[i].trx = trx;
+ trx->cfg.chans[i].idx = i;
+ if (tx_paths_set)
+ osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str());
+ if (rx_paths_set)
+ osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str());
+ }
+
+ return;
+
+bad_config:
+ print_help();
+ exit(0);
+}
+
+int trx_validate_config(struct trx_ctx *trx)
+{
+ if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) {
+ LOG(ERROR) << "Unsupported number of channels";
+ return -1;
+ }
+
+ /* Force 4 SPS for EDGE or multi-ARFCN configurations */
+ if ((trx->cfg.egprs || trx->cfg.multi_arfcn) &&
+ (trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) {
+ LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_sched_rr(unsigned int prio)
+{
+ struct sched_param param;
+ int rc;
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = prio;
+ printf("Setting SCHED_RR priority(%d)\n", param.sched_priority);
+ rc = sched_setscheduler(getpid(), SCHED_RR, &param);
+ if (rc != 0) {
+ LOG(ERROR) << "Config: Setting SCHED_RR failed";
+ return -1;
+ }
+ return 0;
+}
+
+static void print_config(struct trx_ctx *trx)
+{
+ unsigned int i;
+ std::ostringstream ost("");
+
+ ost << "Config Settings" << std::endl;
+ ost << " Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl;
+ ost << " Device args............. " << charp2str(trx->cfg.dev_args) << std::endl;
+ ost << " TRX Base Port........... " << trx->cfg.base_port << std::endl;
+ ost << " TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl;
+ ost << " GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl;
+ ost << " Channels................ " << trx->cfg.num_chans << std::endl;
+ ost << " Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl;
+ ost << " Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl;
+ ost << " EDGE support............ " << trx->cfg.egprs << std::endl;
+ ost << " Reference............... " << trx->cfg.clock_ref << std::endl;
+ ost << " C0 Filler Table......... " << trx->cfg.filler << std::endl;
+ ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
+ ost << " Tuning offset........... " << trx->cfg.offset << std::endl;
+ ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl;
+ ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
+ ost << " Tx Antennas.............";
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ std::string p = charp2str(trx->cfg.chans[i].tx_path);
+ ost << " '" << ((p != "") ? p : "<default>") << "'";
+ }
+ ost << std::endl;
+ ost << " Rx Antennas.............";
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ std::string p = charp2str(trx->cfg.chans[i].rx_path);
+ ost << " '" << ((p != "") ? p : "<default>") << "'";
+ }
+ ost << std::endl;
+
+ std::cout << ost << std::endl;
+}
+
+static void trx_stop()
+{
+ std::cout << "Shutting down transceiver..." << std::endl;
+
+ delete transceiver;
+ delete radio;
+ delete usrp;
+}
+
+static int trx_start(struct trx_ctx *trx)
+{
+ int type, chans;
+ unsigned int i;
+ std::vector<std::string> rx_paths, tx_paths;
+ RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
+
+ /* Create the low level device object */
+ if (trx->cfg.multi_arfcn)
+ iface = RadioDevice::MULTI_ARFCN;
+
+ /* Generate vector of rx/tx_path: */
+ for (i = 0; i < trx->cfg.num_chans; i++) {
+ rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
+ tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
+ }
+
+ usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
+ trx->cfg.num_chans, trx->cfg.offset,
+ tx_paths, rx_paths);
+ type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
+ if (type < 0) {
+ LOG(ALERT) << "Failed to create radio device" << std::endl;
+ goto shutdown;
+ }
+
+ /* Setup the appropriate device interface */
+ radio = makeRadioInterface(trx, usrp, type);
+ if (!radio)
+ goto shutdown;
+
+ /* Create the transceiver core */
+ if (makeTransceiver(trx, radio) < 0)
+ goto shutdown;
+
+ chans = transceiver->numChans();
+ std::cout << "-- Transceiver active with "
+ << chans << " channel(s)" << std::endl;
+
+ return 0;
+
+shutdown:
+ trx_stop();
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+
+ tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
+ msgb_talloc_ctx_init(tall_trx_ctx, 0);
+ g_vty_info.tall_ctx = tall_trx_ctx;
+
+ setup_signal_handlers();
+
+ g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
+
+#ifdef HAVE_SSE3
+ printf("Info: SSE3 support compiled in");
+#ifdef HAVE___BUILTIN_CPU_SUPPORTS
+ if (__builtin_cpu_supports("sse3"))
+ printf(" and supported by CPU\n");
+ else
+ printf(", but not supported by CPU\n");
+#else
+ printf(", but runtime SIMD detection disabled\n");
+#endif
+#endif
+
+#ifdef HAVE_SSE4_1
+ printf("Info: SSE4.1 support compiled in");
+#ifdef HAVE___BUILTIN_CPU_SUPPORTS
+ if (__builtin_cpu_supports("sse4.1"))
+ printf(" and supported by CPU\n");
+ else
+ printf(", but not supported by CPU\n");
+#else
+ printf(", but runtime SIMD detection disabled\n");
+#endif
+#endif
+
+ convolve_init();
+ convert_init();
+
+ osmo_init_logging2(tall_trx_ctx, &log_info);
+ osmo_stats_init(tall_trx_ctx);
+ vty_init(&g_vty_info);
+ ctrl_vty_init(tall_trx_ctx);
+ trx_vty_init(g_trx_ctx);
+
+ logging_vty_add_cmds();
+ osmo_talloc_vty_add_cmds();
+ osmo_stats_vty_add_cmds();
+
+ handle_options(argc, argv, g_trx_ctx);
+
+ rate_ctr_init(tall_trx_ctx);
+
+ rc = vty_read_config_file(config_file, NULL);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
+ exit(2);
+ }
+
+ rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
+ if (rc < 0)
+ exit(1);
+
+ g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
+ if (!g_ctrlh) {
+ fprintf(stderr, "Failed to create CTRL interface.\n");
+ exit(1);
+ }
+
+ /* Backward compatibility: Hack to have 1 channel allocated by default.
+ * Can be Dropped once we * get rid of "-c" cmdline param */
+ if (g_trx_ctx->cfg.num_chans == 0) {
+ g_trx_ctx->cfg.num_chans = 1;
+ g_trx_ctx->cfg.chans[0].trx = g_trx_ctx;
+ g_trx_ctx->cfg.chans[0].idx = 0;
+ LOG(ERROR) << "No explicit channel config found. Make sure you" \
+ " configure channels in VTY config. Using 1 channel as default," \
+ " but expect your config to break in the future.";
+ }
+
+ print_config(g_trx_ctx);
+
+ if (trx_validate_config(g_trx_ctx) < 0) {
+ LOG(ERROR) << "Config failure - exiting";
+ return EXIT_FAILURE;
+ }
+
+ if (g_trx_ctx->cfg.sched_rr) {
+ if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0)
+ return EXIT_FAILURE;
+ }
+
+ srandom(time(NULL));
+
+ if(trx_start(g_trx_ctx) < 0)
+ return EXIT_FAILURE;
+
+ while (!gshutdown)
+ osmo_select_main(0);
+
+ trx_stop();
+
+ return 0;
+}
diff --git a/Transceiver52M/pulseApproximate.m b/Transceiver52M/pulseApproximate.m
new file mode 100644
index 0000000..2ff9234
--- /dev/null
+++ b/Transceiver52M/pulseApproximate.m
@@ -0,0 +1,15 @@
+pp = [0 0 0.015 0.18 0.7 0.96 0.7 0.18 0.015 0 0];
+t = -2.5:0.5:2.5;
+
+v = -0.000:-0.001:-1.999;
+
+
+for ix1 = 1:length(v),
+ disp(ix1);
+ for ix2 = 1:length(v),
+ p = exp(v(ix1)*t.^2+v(ix2)*t.^4);
+ r(ix1,ix2) = norm(p./max(abs(p)) - pp./max(abs(pp)));
+ end;
+end;
+
+
diff --git a/Transceiver52M/radioBuffer.cpp b/Transceiver52M/radioBuffer.cpp
new file mode 100644
index 0000000..a2b42c4
--- /dev/null
+++ b/Transceiver52M/radioBuffer.cpp
@@ -0,0 +1,228 @@
+/*
+ * Segmented Ring Buffer
+ *
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * Author: Tom Tsou <tom@tsou.cc>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#include <string.h>
+#include <iostream>
+#include "radioBuffer.h"
+
+RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
+ size_t hLen, bool outDirection)
+ : writeIndex(0), readIndex(0), availSamples(0)
+{
+ if (!outDirection)
+ hLen = 0;
+
+ buffer = new float[2 * (hLen + numSegments * segmentLen)];
+ bufferLen = numSegments * segmentLen;
+
+ segments.resize(numSegments);
+
+ for (size_t i = 0; i < numSegments; i++)
+ segments[i] = &buffer[2 * (hLen + i * segmentLen)];
+
+ this->outDirection = outDirection;
+ this->numSegments = numSegments;
+ this->segmentLen = segmentLen;
+ this->hLen = hLen;
+}
+
+RadioBuffer::~RadioBuffer()
+{
+ delete[] buffer;
+}
+
+void RadioBuffer::reset()
+{
+ writeIndex = 0;
+ readIndex = 0;
+ availSamples = 0;
+}
+
+/*
+ * Output direction
+ *
+ * Return a pointer to the oldest segment or NULL if a complete segment is not
+ * available.
+ */
+const float *RadioBuffer::getReadSegment()
+{
+ if (!outDirection) {
+ std::cout << "Invalid direction" << std::endl;
+ return NULL;
+ }
+ if (availSamples < segmentLen) {
+ std::cout << "Not enough samples " << std::endl;
+ std::cout << availSamples << " available per segment "
+ << segmentLen << std::endl;
+ return NULL;
+ }
+
+ size_t num = readIndex / segmentLen;
+
+ if (num >= numSegments) {
+ std::cout << "Invalid segment" << std::endl;
+ return NULL;
+ } else if (!num) {
+ memcpy(buffer,
+ &buffer[2 * bufferLen],
+ hLen * 2 * sizeof(float));
+ }
+
+ availSamples -= segmentLen;
+ readIndex = (readIndex + segmentLen) % bufferLen;
+
+ return segments[num];
+}
+
+/*
+ * Output direction
+ *
+ * Write a non-segment length of samples to the buffer.
+ */
+bool RadioBuffer::write(const float *wr, size_t len)
+{
+ if (!outDirection) {
+ std::cout << "Invalid direction" << std::endl;
+ return false;
+ }
+ if (availSamples + len > bufferLen) {
+ std::cout << "Insufficient space" << std::endl;
+ std::cout << bufferLen - availSamples << " available per write "
+ << len << std::endl;
+ return false;
+ }
+
+ if (writeIndex + len <= bufferLen) {
+ memcpy(&buffer[2 * (writeIndex + hLen)],
+ wr, len * 2 * sizeof(float));
+ } else {
+ size_t len0 = bufferLen - writeIndex;
+ size_t len1 = len - len0;
+ memcpy(&buffer[2 * (writeIndex + hLen)], wr, len0 * 2 * sizeof(float));
+ memcpy(&buffer[2 * hLen], &wr[2 * len0], len1 * 2 * sizeof(float));
+ }
+
+ availSamples += len;
+ writeIndex = (writeIndex + len) % bufferLen;
+
+ return true;
+}
+
+bool RadioBuffer::zero(size_t len)
+{
+ if (!outDirection) {
+ std::cout << "Invalid direction" << std::endl;
+ return false;
+ }
+ if (availSamples + len > bufferLen) {
+ std::cout << "Insufficient space" << std::endl;
+ std::cout << bufferLen - availSamples << " available per zero "
+ << len << std::endl;
+ return false;
+ }
+
+ if (writeIndex + len <= bufferLen) {
+ memset(&buffer[2 * (writeIndex + hLen)],
+ 0, len * 2 * sizeof(float));
+ } else {
+ size_t len0 = bufferLen - writeIndex;
+ size_t len1 = len - len0;
+ memset(&buffer[2 * (writeIndex + hLen)], 0, len0 * 2 * sizeof(float));
+ memset(&buffer[2 * hLen], 0, len1 * 2 * sizeof(float));
+ }
+
+ availSamples += len;
+ writeIndex = (writeIndex + len) % bufferLen;
+
+ return true;
+}
+
+/*
+ * Input direction
+ */
+float *RadioBuffer::getWriteSegment()
+{
+ if (outDirection) {
+ std::cout << "Invalid direction" << std::endl;
+ return NULL;
+ }
+ if (bufferLen - availSamples < segmentLen) {
+ std::cout << "Insufficient samples" << std::endl;
+ std::cout << bufferLen - availSamples
+ << " available for segment " << segmentLen
+ << std::endl;
+ return NULL;
+ }
+ if (writeIndex % segmentLen) {
+ std::cout << "Internal segment error" << std::endl;
+ return NULL;
+ }
+
+ size_t num = writeIndex / segmentLen;
+
+ if (num >= numSegments)
+ return NULL;
+
+ availSamples += segmentLen;
+ writeIndex = (writeIndex + segmentLen) % bufferLen;
+
+ return segments[num];
+}
+
+bool RadioBuffer::zeroWriteSegment()
+{
+ float *segment = getWriteSegment();
+ if (!segment)
+ return false;
+
+ memset(segment, 0, segmentLen * 2 * sizeof(float));
+
+ return true;
+}
+
+bool RadioBuffer::read(float *rd, size_t len)
+{
+ if (outDirection) {
+ std::cout << "Invalid direction" << std::endl;
+ return false;
+ }
+ if (availSamples < len) {
+ std::cout << "Insufficient samples" << std::endl;
+ std::cout << availSamples << " available for "
+ << len << std::endl;
+ return false;
+ }
+
+ if (readIndex + len <= bufferLen) {
+ memcpy(rd, &buffer[2 * readIndex], len * 2 * sizeof(float));
+ } else {
+ size_t len0 = bufferLen - readIndex;
+ size_t len1 = len - len0;
+ memcpy(rd, &buffer[2 * readIndex], len0 * 2 * sizeof(float));
+ memcpy(&rd[2 * len0], buffer, len1 * 2 * sizeof(float));
+ }
+
+ availSamples -= len;
+ readIndex = (readIndex + len) % bufferLen;
+
+ return true;
+}
diff --git a/Transceiver52M/radioBuffer.h b/Transceiver52M/radioBuffer.h
new file mode 100644
index 0000000..e5aa315
--- /dev/null
+++ b/Transceiver52M/radioBuffer.h
@@ -0,0 +1,45 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <vector>
+
+class RadioBuffer {
+public:
+ RadioBuffer(size_t numSegments, size_t segmentLen,
+ size_t hLen, bool outDirection);
+
+ ~RadioBuffer();
+
+ const size_t getSegmentLen() { return segmentLen; }
+ const size_t getNumSegments() { return numSegments; }
+ const size_t getAvailSamples() { return availSamples; }
+ const size_t getAvailSegments() { return availSamples / segmentLen; }
+
+ const size_t getFreeSamples()
+ {
+ return bufferLen - availSamples;
+ }
+
+ const size_t getFreeSegments()
+ {
+ return getFreeSamples() / segmentLen;
+ }
+
+ void reset();
+
+ /* Output direction */
+ const float *getReadSegment();
+ bool write(const float *wr, size_t len);
+ bool zero(size_t len);
+
+ /* Input direction */
+ float *getWriteSegment();
+ bool zeroWriteSegment();
+ bool read(float *rd, size_t len);
+
+private:
+ size_t writeIndex, readIndex, availSamples;
+ size_t bufferLen, numSegments, segmentLen, hLen;
+ float *buffer;
+ std::vector<float *> segments;
+ bool outDirection;
+};
diff --git a/Transceiver52M/radioClock.cpp b/Transceiver52M/radioClock.cpp
new file mode 100644
index 0000000..505bb01
--- /dev/null
+++ b/Transceiver52M/radioClock.cpp
@@ -0,0 +1,49 @@
+/*
+ * Written by Thomas Tsou <ttsou@vt.edu>
+ * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
+ *
+ * Copyright 2011 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#include "radioClock.h"
+
+void RadioClock::set(const GSM::Time& wTime)
+{
+ ScopedLock lock(mLock);
+ mClock = wTime;
+ updateSignal.signal();
+}
+
+void RadioClock::incTN()
+{
+ ScopedLock lock(mLock);
+ mClock.incTN();
+ updateSignal.signal();
+}
+
+GSM::Time RadioClock::get()
+{
+ ScopedLock lock(mLock);
+ GSM::Time retVal = mClock;
+ return retVal;
+}
+
+void RadioClock::wait()
+{
+ ScopedLock lock(mLock);
+ updateSignal.wait(mLock,1);
+}
diff --git a/Transceiver52M/radioClock.h b/Transceiver52M/radioClock.h
new file mode 100644
index 0000000..9c35c44
--- /dev/null
+++ b/Transceiver52M/radioClock.h
@@ -0,0 +1,40 @@
+/*
+ * Written by Thomas Tsou <ttsou@vt.edu>
+ * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
+ *
+ * Copyright 2011 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#ifndef RADIOCLOCK_H
+#define RADIOCLOCK_H
+
+#include "GSMCommon.h"
+
+class RadioClock {
+public:
+ void set(const GSM::Time& wTime);
+ void incTN();
+ GSM::Time get();
+ void wait();
+
+private:
+ GSM::Time mClock;
+ Mutex mLock;
+ Signal updateSignal;
+};
+
+#endif /* RADIOCLOCK_H */
diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp
new file mode 100644
index 0000000..6245cfc
--- /dev/null
+++ b/Transceiver52M/radioInterface.cpp
@@ -0,0 +1,360 @@
+/*
+ * Radio device interface
+ *
+ * Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ * 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 "radioInterface.h"
+#include "Resampler.h"
+#include <Logger.h>
+
+extern "C" {
+#include "convert.h"
+}
+
+#define CHUNK 625
+#define NUMCHUNKS 4
+
+RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
+ size_t rx_sps, size_t chans,
+ int wReceiveOffset, GSM::Time wStartTime)
+ : mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
+ underrun(false), overrun(false), receiveOffset(wReceiveOffset), mOn(false)
+{
+ mClock.set(wStartTime);
+}
+
+RadioInterface::~RadioInterface(void)
+{
+ close();
+}
+
+bool RadioInterface::init(int type)
+{
+ if ((type != RadioDevice::NORMAL) || !mChans) {
+ LOG(ALERT) << "Invalid configuration";
+ return false;
+ }
+
+ close();
+
+ sendBuffer.resize(mChans);
+ recvBuffer.resize(mChans);
+ convertSendBuffer.resize(mChans);
+ convertRecvBuffer.resize(mChans);
+ mReceiveFIFO.resize(mChans);
+ powerScaling.resize(mChans);
+
+ for (size_t i = 0; i < mChans; i++) {
+ sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
+ recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
+
+ convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
+ convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
+
+ powerScaling[i] = 1.0;
+ }
+
+ return true;
+}
+
+void RadioInterface::close()
+{
+ sendBuffer.resize(0);
+ recvBuffer.resize(0);
+ convertSendBuffer.resize(0);
+ convertRecvBuffer.resize(0);
+}
+
+double RadioInterface::fullScaleInputValue(void) {
+ return mRadio->fullScaleInputValue();
+}
+
+double RadioInterface::fullScaleOutputValue(void) {
+ return mRadio->fullScaleOutputValue();
+}
+
+int RadioInterface::setPowerAttenuation(int atten, size_t chan)
+{
+ double rfGain, digAtten;
+
+ if (chan >= mChans) {
+ LOG(ALERT) << "Invalid channel requested";
+ return -1;
+ }
+
+ if (atten < 0.0)
+ atten = 0.0;
+
+ rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
+ digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
+
+ if (digAtten < 1.0)
+ powerScaling[chan] = 1.0;
+ else
+ powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
+
+ return atten;
+}
+
+int RadioInterface::radioifyVector(signalVector &wVector,
+ size_t chan, bool zero)
+{
+ if (zero)
+ sendBuffer[chan]->zero(wVector.size());
+ else
+ sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
+
+ return wVector.size();
+}
+
+int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
+{
+ if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
+ LOG(ALERT) << "Insufficient number of samples in receive buffer";
+ return -1;
+ }
+
+ recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
+
+ return newVector->size();
+}
+
+bool RadioInterface::tuneTx(double freq, size_t chan)
+{
+ return mRadio->setTxFreq(freq, chan);
+}
+
+bool RadioInterface::tuneRx(double freq, size_t chan)
+{
+ return mRadio->setRxFreq(freq, chan);
+}
+
+/** synchronization thread loop */
+void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
+{
+ set_selfthread_name("AlignRadio");
+ while (1) {
+ sleep(60);
+ radioInterface->alignRadio();
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+void RadioInterface::alignRadio() {
+ mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
+}
+
+bool RadioInterface::start()
+{
+ if (mOn)
+ return true;
+
+ LOG(INFO) << "Starting radio device";
+ if (mRadio->requiresRadioAlign())
+ mAlignRadioServiceLoopThread.start(
+ (void * (*)(void*))AlignRadioServiceLoopAdapter,
+ (void*)this);
+
+ if (!mRadio->start())
+ return false;
+
+ for (size_t i = 0; i < mChans; i++) {
+ sendBuffer[i]->reset();
+ recvBuffer[i]->reset();
+ }
+
+ writeTimestamp = mRadio->initialWriteTimestamp();
+ readTimestamp = mRadio->initialReadTimestamp();
+
+ mRadio->updateAlignment(writeTimestamp-10000);
+ mRadio->updateAlignment(writeTimestamp-10000);
+
+ mOn = true;
+ LOG(INFO) << "Radio started";
+ return true;
+}
+
+/*
+ * Stop the radio device
+ *
+ * This is a pass-through call to the device interface. Because the underlying
+ * stop command issuance generally doesn't return confirmation on device status,
+ * this call will only return false if the device is already stopped.
+ */
+bool RadioInterface::stop()
+{
+ if (!mOn || !mRadio->stop())
+ return false;
+
+ mOn = false;
+ return true;
+}
+
+void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
+ std::vector<bool> &zeros)
+{
+ if (!mOn)
+ return;
+
+ for (size_t i = 0; i < mChans; i++)
+ radioifyVector(*bursts[i], i, zeros[i]);
+
+ while (pushBuffer());
+}
+
+int RadioInterface::driveReceiveRadio()
+{
+ radioVector *burst = NULL;
+
+ if (!mOn)
+ return 0;
+
+ if (pullBuffer() < 0)
+ return -1;
+
+ GSM::Time rcvClock = mClock.get();
+ rcvClock.decTN(receiveOffset);
+ unsigned tN = rcvClock.TN();
+ int recvSz = recvBuffer[0]->getAvailSamples();
+ const int symbolsPerSlot = gSlotLen + 8;
+ int burstSize;
+
+ if (mSPSRx == 4)
+ burstSize = 625;
+ else
+ burstSize = symbolsPerSlot + (tN % 4 == 0);
+
+ /*
+ * Pre-allocate head room for the largest correlation size
+ * so we can later avoid a re-allocation and copy
+ * */
+ size_t head = GSM::gRACHSynchSequenceTS0.size();
+
+ /*
+ * Form receive bursts and pass up to transceiver. Use repeating
+ * pattern of 157-156-156-156 symbols per timeslot
+ */
+ while (recvSz > burstSize) {
+ for (size_t i = 0; i < mChans; i++) {
+ burst = new radioVector(rcvClock, burstSize, head);
+ unRadioifyVector(burst->getVector(), i);
+
+ if (mReceiveFIFO[i].size() < 32)
+ mReceiveFIFO[i].write(burst);
+ else
+ delete burst;
+ }
+
+ mClock.incTN();
+ rcvClock.incTN();
+ recvSz -= burstSize;
+
+ tN = rcvClock.TN();
+
+ if (mSPSRx != 4)
+ burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
+ }
+
+ return 1;
+}
+
+bool RadioInterface::isUnderrun()
+{
+ bool retVal = underrun;
+ underrun = false;
+
+ return retVal;
+}
+
+VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
+{
+ if (chan >= mReceiveFIFO.size())
+ return NULL;
+
+ return &mReceiveFIFO[chan];
+}
+
+double RadioInterface::setRxGain(double dB, size_t chan)
+{
+ return mRadio->setRxGain(dB, chan);
+}
+
+double RadioInterface::getRxGain(size_t chan)
+{
+ return mRadio->getRxGain(chan);
+}
+
+/* Receive a timestamped chunk from the device */
+int RadioInterface::pullBuffer()
+{
+ bool local_underrun;
+ int numRecv;
+ size_t segmentLen = recvBuffer[0]->getSegmentLen();
+
+ if (recvBuffer[0]->getFreeSegments() <= 0)
+ return -1;
+
+ /* Outer buffer access size is fixed */
+ numRecv = mRadio->readSamples(convertRecvBuffer,
+ segmentLen,
+ &overrun,
+ readTimestamp,
+ &local_underrun);
+
+ if ((size_t) numRecv != segmentLen) {
+ LOG(ALERT) << "Receive error " << numRecv;
+ return -1;
+ }
+
+ for (size_t i = 0; i < mChans; i++) {
+ convert_short_float(recvBuffer[i]->getWriteSegment(),
+ convertRecvBuffer[i],
+ segmentLen * 2);
+ }
+
+ underrun |= local_underrun;
+ readTimestamp += numRecv;
+ return 0;
+}
+
+/* Send timestamped chunk to the device with arbitrary size */
+bool RadioInterface::pushBuffer()
+{
+ size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
+
+ if (sendBuffer[0]->getAvailSegments() < 1)
+ return false;
+
+ for (size_t i = 0; i < mChans; i++) {
+ convert_float_short(convertSendBuffer[i],
+ (float *) sendBuffer[i]->getReadSegment(),
+ powerScaling[i],
+ segmentLen * 2);
+ }
+
+ /* Send the all samples in the send buffer */
+ numSent = mRadio->writeSamples(convertSendBuffer,
+ segmentLen,
+ &underrun,
+ writeTimestamp);
+ writeTimestamp += numSent;
+
+ return true;
+}
diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h
new file mode 100644
index 0000000..f19a8dc
--- /dev/null
+++ b/Transceiver52M/radioInterface.h
@@ -0,0 +1,188 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+*/
+
+
+
+#include "sigProcLib.h"
+#include "GSMCommon.h"
+#include "LinkedLists.h"
+#include "radioDevice.h"
+#include "radioVector.h"
+#include "radioClock.h"
+#include "radioBuffer.h"
+#include "Resampler.h"
+#include "Channelizer.h"
+#include "Synthesis.h"
+
+static const unsigned gSlotLen = 148; ///< number of symbols per slot, not counting guard periods
+
+/** class to interface the transceiver with the USRP */
+class RadioInterface {
+
+protected:
+
+ Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections
+
+ std::vector<VectorFIFO> mReceiveFIFO; ///< FIFO that holds receive bursts
+
+ RadioDevice *mRadio; ///< the USRP object
+
+ size_t mSPSTx;
+ size_t mSPSRx;
+ size_t mChans;
+
+ std::vector<RadioBuffer *> sendBuffer;
+ std::vector<RadioBuffer *> recvBuffer;
+
+ std::vector<short *> convertRecvBuffer;
+ std::vector<short *> convertSendBuffer;
+ std::vector<float> powerScaling;
+ bool underrun; ///< indicates writes to USRP are too slow
+ bool overrun; ///< indicates reads from USRP are too slow
+ TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
+ TIMESTAMP readTimestamp; ///< sample timestamp of next packet read from USRP
+
+ RadioClock mClock; ///< the basestation clock!
+
+ int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots
+
+ bool mOn; ///< indicates radio is on
+
+private:
+
+ /** format samples to USRP */
+ int radioifyVector(signalVector &wVector, size_t chan, bool zero);
+
+ /** format samples from USRP */
+ int unRadioifyVector(signalVector *wVector, size_t chan);
+
+ /** push GSM bursts into the transmit buffer */
+ virtual bool pushBuffer(void);
+
+ /** pull GSM bursts from the receive buffer */
+ virtual int pullBuffer(void);
+
+public:
+
+ /** start the interface */
+ bool start();
+ bool stop();
+
+ /** intialization */
+ virtual bool init(int type);
+ virtual void close();
+
+ /** constructor */
+ RadioInterface(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps,
+ size_t chans = 1, int receiveOffset = 3,
+ GSM::Time wStartTime = GSM::Time(0));
+
+ /** destructor */
+ virtual ~RadioInterface();
+
+ /** check for underrun, resets underrun value */
+ bool isUnderrun();
+
+ /** return the receive FIFO */
+ VectorFIFO* receiveFIFO(size_t chan = 0);
+
+ /** return the basestation clock */
+ RadioClock* getClock(void) { return &mClock;};
+
+ /** set transmit frequency */
+ virtual bool tuneTx(double freq, size_t chan = 0);
+
+ /** set receive frequency */
+ virtual bool tuneRx(double freq, size_t chan = 0);
+
+ /** set receive gain */
+ double setRxGain(double dB, size_t chan = 0);
+
+ /** get receive gain */
+ double getRxGain(size_t chan = 0);
+
+ /** drive transmission of GSM bursts */
+ void driveTransmitRadio(std::vector<signalVector *> &bursts,
+ std::vector<bool> &zeros);
+
+ /** drive reception of GSM bursts. -1: Error. 0: Radio off. 1: Received something. */
+ int driveReceiveRadio();
+
+ int setPowerAttenuation(int atten, size_t chan = 0);
+
+ /** returns the full-scale transmit amplitude **/
+ double fullScaleInputValue();
+
+ /** returns the full-scale receive amplitude **/
+ double fullScaleOutputValue();
+
+ /** set thread priority on current thread */
+ void setPriority(float prio = 0.5) { mRadio->setPriority(prio); }
+
+ /** get transport window type of attached device */
+ enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
+
+ /** Minimum latency that the device can achieve */
+ GSM::Time minLatency() { return mRadio->minLatency(); }
+
+protected:
+ /** drive synchronization of Tx/Rx of USRP */
+ void alignRadio();
+
+ friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
+};
+
+class RadioInterfaceResamp : public RadioInterface {
+private:
+ signalVector *outerSendBuffer;
+ signalVector *outerRecvBuffer;
+
+ bool pushBuffer();
+ int pullBuffer();
+
+public:
+ RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
+ ~RadioInterfaceResamp();
+
+ bool init(int type);
+ void close();
+};
+
+class RadioInterfaceMulti : public RadioInterface {
+private:
+ bool pushBuffer();
+ int pullBuffer();
+
+ signalVector *outerSendBuffer;
+ signalVector *outerRecvBuffer;
+ std::vector<signalVector *> history;
+ std::vector<bool> active;
+
+ Resampler *dnsampler;
+ Resampler *upsampler;
+ Channelizer *channelizer;
+ Synthesis *synthesis;
+
+public:
+ RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
+ size_t rx_sps, size_t chans = 1);
+ ~RadioInterfaceMulti();
+
+ bool init(int type);
+ void close();
+
+ bool tuneTx(double freq, size_t chan);
+ bool tuneRx(double freq, size_t chan);
+ double setRxGain(double dB, size_t chan);
+};
diff --git a/Transceiver52M/radioInterfaceMulti.cpp b/Transceiver52M/radioInterfaceMulti.cpp
new file mode 100644
index 0000000..17a015b
--- /dev/null
+++ b/Transceiver52M/radioInterfaceMulti.cpp
@@ -0,0 +1,405 @@
+/*
+ * Multi-carrier radio interface
+ *
+ * Copyright (C) 2016 Ettus Research LLC
+ *
+ * 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 <radioInterface.h>
+#include <Logger.h>
+
+#include "Resampler.h"
+
+extern "C" {
+#include "convert.h"
+}
+
+/* Resampling parameters for 64 MHz clocking */
+#define RESAMP_INRATE 65
+#define RESAMP_OUTRATE (96 / 2)
+
+/* Universal resampling parameters */
+#define NUMCHUNKS 24
+
+#define MCHANS 4
+
+RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
+ size_t rx_sps, size_t chans)
+ : RadioInterface(radio, tx_sps, rx_sps, chans),
+ outerSendBuffer(NULL), outerRecvBuffer(NULL),
+ dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
+{
+}
+
+RadioInterfaceMulti::~RadioInterfaceMulti()
+{
+ close();
+}
+
+void RadioInterfaceMulti::close()
+{
+ delete outerSendBuffer;
+ delete outerRecvBuffer;
+ delete dnsampler;
+ delete upsampler;
+ delete channelizer;
+ delete synthesis;
+
+ outerSendBuffer = NULL;
+ outerRecvBuffer = NULL;
+ dnsampler = NULL;
+ upsampler = NULL;
+ channelizer = NULL;
+ synthesis = NULL;
+
+ mReceiveFIFO.resize(0);
+ powerScaling.resize(0);
+ history.resize(0);
+ active.resize(0);
+
+ RadioInterface::close();
+}
+
+static int getLogicalChan(size_t pchan, size_t chans)
+{
+ switch (chans) {
+ case 1:
+ if (pchan == 0)
+ return 0;
+ else
+ return -1;
+ break;
+ case 2:
+ if (pchan == 0)
+ return 0;
+ if (pchan == 3)
+ return 1;
+ else
+ return -1;
+ break;
+ case 3:
+ if (pchan == 1)
+ return 0;
+ if (pchan == 0)
+ return 1;
+ if (pchan == 3)
+ return 2;
+ else
+ return -1;
+ break;
+ default:
+ break;
+ };
+
+ return -1;
+}
+
+static int getFreqShift(size_t chans)
+{
+ switch (chans) {
+ case 1:
+ return 0;
+ case 2:
+ return 0;
+ case 3:
+ return 1;
+ default:
+ break;
+ };
+
+ return -1;
+}
+
+/* Initialize I/O specific objects */
+bool RadioInterfaceMulti::init(int type)
+{
+ float cutoff = 1.0f;
+ size_t inchunk = 0, outchunk = 0;
+
+ if (mChans > MCHANS - 1) {
+ LOG(ALERT) << "Invalid channel configuration " << mChans;
+ return false;
+ }
+
+ close();
+
+ sendBuffer.resize(mChans);
+ recvBuffer.resize(mChans);
+ convertSendBuffer.resize(1);
+ convertRecvBuffer.resize(1);
+
+ mReceiveFIFO.resize(mChans);
+ powerScaling.resize(mChans);
+ history.resize(mChans);
+ active.resize(MCHANS, false);
+
+ inchunk = RESAMP_INRATE * 4;
+ outchunk = RESAMP_OUTRATE * 4;
+
+ if (inchunk * NUMCHUNKS < 625 * 2) {
+ LOG(ALERT) << "Invalid inner chunk size " << inchunk;
+ return false;
+ }
+
+ dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
+ if (!dnsampler->init(1.0)) {
+ LOG(ALERT) << "Rx resampler failed to initialize";
+ return false;
+ }
+
+ upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
+ if (!upsampler->init(cutoff)) {
+ LOG(ALERT) << "Tx resampler failed to initialize";
+ return false;
+ }
+
+ channelizer = new Channelizer(MCHANS, outchunk);
+ if (!channelizer->init()) {
+ LOG(ALERT) << "Rx channelizer failed to initialize";
+ return false;
+ }
+
+ synthesis = new Synthesis(MCHANS, outchunk);
+ if (!synthesis->init()) {
+ LOG(ALERT) << "Tx synthesis filter failed to initialize";
+ return false;
+ }
+
+ /*
+ * Allocate high and low rate buffers. The high rate receive
+ * buffer and low rate transmit vectors feed into the resampler
+ * and requires headroom equivalent to the filter length. Low
+ * rate buffers are allocated in the main radio interface code.
+ */
+ for (size_t i = 0; i < mChans; i++) {
+ sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
+ upsampler->len(), true);
+ recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
+ 0, false);
+ history[i] = new signalVector(dnsampler->len());
+
+ synthesis->resetBuffer(i);
+ }
+
+ outerSendBuffer = new signalVector(synthesis->outputLen());
+ outerRecvBuffer = new signalVector(channelizer->inputLen());
+
+ convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
+ convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
+
+ /* Configure channels */
+ switch (mChans) {
+ case 1:
+ active[0] = true;
+ break;
+ case 2:
+ active[0] = true;
+ active[3] = true;
+ break;
+ case 3:
+ active[0] = true;
+ active[1] = true;
+ active[3] = true;
+ break;
+ default:
+ LOG(ALERT) << "Unsupported channel combination";
+ return false;
+ }
+
+ return true;
+}
+
+/* Receive a timestamped chunk from the device */
+int RadioInterfaceMulti::pullBuffer()
+{
+ bool local_underrun;
+ size_t num;
+ float *buf;
+ unsigned int i;
+
+ if (recvBuffer[0]->getFreeSegments() <= 0)
+ return -1;
+
+ /* Outer buffer access size is fixed */
+ num = mRadio->readSamples(convertRecvBuffer,
+ outerRecvBuffer->size(),
+ &overrun,
+ readTimestamp,
+ &local_underrun);
+ if (num != channelizer->inputLen()) {
+ LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
+ return -1;
+ }
+
+ convert_short_float((float *) outerRecvBuffer->begin(),
+ convertRecvBuffer[0], 2 * outerRecvBuffer->size());
+
+ underrun |= local_underrun;
+ readTimestamp += num;
+
+ channelizer->rotate((float *) outerRecvBuffer->begin(),
+ outerRecvBuffer->size());
+
+ for (size_t pchan = 0; pchan < MCHANS; pchan++) {
+ if (!active[pchan])
+ continue;
+
+ int lchan = getLogicalChan(pchan, mChans);
+ if (lchan < 0) {
+ LOG(ALERT) << "Invalid logical channel " << pchan;
+ continue;
+ }
+
+ /*
+ * Update history by writing into the head portion of the
+ * channelizer output buffer. For this to work, filter length of
+ * the polyphase channelizer partition filter should be equal to
+ * or larger than the resampling filter.
+ */
+ buf = channelizer->outputBuffer(pchan);
+ size_t cLen = channelizer->outputLen();
+ size_t hLen = dnsampler->len();
+
+ float *fdst = &buf[2 * -hLen];
+ complex *src = history[lchan]->begin();
+ for (i = 0; i < hLen; i++) {
+ fdst[0] = src->real();
+ fdst[1] = src->imag();
+ src++;
+ fdst += 2;
+ }
+ complex *dst = history[lchan]->begin();
+ float *fsrc = &buf[2 * (cLen - hLen)];
+ for (i = 0; i < hLen; i++) {
+ *dst = complex(fdst[0], fdst[1]);
+ fsrc += 2;
+ dst++;
+ }
+
+ float *wr_segment = recvBuffer[lchan]->getWriteSegment();
+
+ /* Write to the end of the inner receive buffer */
+ if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
+ channelizer->outputLen(),
+ wr_segment,
+ recvBuffer[lchan]->getSegmentLen())) {
+ LOG(ALERT) << "Sample rate upsampling error";
+ }
+ }
+ return 0;
+}
+
+/* Send a timestamped chunk to the device */
+bool RadioInterfaceMulti::pushBuffer()
+{
+ if (sendBuffer[0]->getAvailSegments() <= 0)
+ return false;
+
+ for (size_t pchan = 0; pchan < MCHANS; pchan++) {
+ if (!active[pchan]) {
+ synthesis->resetBuffer(pchan);
+ continue;
+ }
+
+ int lchan = getLogicalChan(pchan, mChans);
+ if (lchan < 0) {
+ LOG(ALERT) << "Invalid logical channel " << pchan;
+ continue;
+ }
+
+ if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
+ sendBuffer[lchan]->getSegmentLen(),
+ synthesis->inputBuffer(pchan),
+ synthesis->inputLen())) {
+ LOG(ALERT) << "Sample rate downsampling error";
+ }
+ }
+
+ synthesis->rotate((float *) outerSendBuffer->begin(),
+ outerSendBuffer->size());
+
+ convert_float_short(convertSendBuffer[0],
+ (float *) outerSendBuffer->begin(),
+ 1.0 / (float) mChans, 2 * outerSendBuffer->size());
+
+ size_t num = mRadio->writeSamples(convertSendBuffer,
+ outerSendBuffer->size(),
+ &underrun,
+ writeTimestamp);
+ if (num != outerSendBuffer->size()) {
+ LOG(ALERT) << "Transmit error " << num;
+ }
+
+ writeTimestamp += num;
+
+ return true;
+}
+
+/* Frequency comparison limit */
+#define FREQ_DELTA_LIMIT 10.0
+
+static bool fltcmp(double a, double b)
+{
+ return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
+}
+
+bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
+{
+ if (chan >= mChans)
+ return false;
+
+ double shift = (double) getFreqShift(mChans);
+
+ if (!chan)
+ return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
+
+ double center = mRadio->getTxFreq();
+ if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
+ LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
+ << freq / 1e6 << " MHz";
+ }
+
+ return true;
+}
+
+bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
+{
+ if (chan >= mChans)
+ return false;
+
+ double shift = (double) getFreqShift(mChans);
+
+ if (!chan)
+ return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
+
+ double center = mRadio->getRxFreq();
+ if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
+ LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
+ << freq / 1e6 << " MHz";
+ }
+
+ return true;
+}
+
+double RadioInterfaceMulti::setRxGain(double db, size_t chan)
+{
+ if (!chan)
+ return mRadio->setRxGain(db);
+ else
+ return mRadio->getRxGain();
+}
diff --git a/Transceiver52M/radioInterfaceResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp
new file mode 100644
index 0000000..8ae4aa1
--- /dev/null
+++ b/Transceiver52M/radioInterfaceResamp.cpp
@@ -0,0 +1,235 @@
+/*
+ * Radio device interface with sample rate conversion
+ *
+ * Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * Author: Tom Tsou <tom@tsou.cc>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#include <radioInterface.h>
+#include <Logger.h>
+
+#include "Resampler.h"
+
+extern "C" {
+#include "convert.h"
+}
+
+/* Resampling parameters for 64 MHz clocking */
+#define RESAMP_64M_INRATE 65
+#define RESAMP_64M_OUTRATE 96
+
+/* Resampling parameters for 100 MHz clocking */
+#define RESAMP_100M_INRATE 52
+#define RESAMP_100M_OUTRATE 75
+
+/* Universal resampling parameters */
+#define NUMCHUNKS 24
+
+/*
+ * Resampling filter bandwidth scaling factor
+ * This narrows the filter cutoff relative to the output bandwidth
+ * of the polyphase resampler. At 4 samples-per-symbol using the
+ * 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
+ * RMS phase error at the resampler output.
+ */
+#define RESAMP_TX4_FILTER 0.45
+
+static Resampler *upsampler = NULL;
+static Resampler *dnsampler = NULL;
+static size_t resamp_inrate = 0;
+static size_t resamp_inchunk = 0;
+static size_t resamp_outrate = 0;
+static size_t resamp_outchunk = 0;
+
+RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
+ size_t tx_sps, size_t rx_sps)
+ : RadioInterface(wRadio, tx_sps, rx_sps, 1),
+ outerSendBuffer(NULL), outerRecvBuffer(NULL)
+{
+}
+
+RadioInterfaceResamp::~RadioInterfaceResamp()
+{
+ close();
+}
+
+void RadioInterfaceResamp::close()
+{
+ delete outerSendBuffer;
+ delete outerRecvBuffer;
+
+ delete upsampler;
+ delete dnsampler;
+
+ outerSendBuffer = NULL;
+ outerRecvBuffer = NULL;
+
+ upsampler = NULL;
+ dnsampler = NULL;
+
+ if (sendBuffer.size())
+ sendBuffer[0] = NULL;
+ if (recvBuffer.size())
+ recvBuffer[0] = NULL;
+
+ RadioInterface::close();
+}
+
+/* Initialize I/O specific objects */
+bool RadioInterfaceResamp::init(int type)
+{
+ float cutoff = 1.0f;
+
+ close();
+
+ sendBuffer.resize(1);
+ recvBuffer.resize(1);
+ convertSendBuffer.resize(1);
+ convertRecvBuffer.resize(1);
+ mReceiveFIFO.resize(1);
+ powerScaling.resize(1);
+
+ switch (type) {
+ case RadioDevice::RESAMP_64M:
+ resamp_inrate = RESAMP_64M_INRATE;
+ resamp_outrate = RESAMP_64M_OUTRATE;
+ break;
+ case RadioDevice::RESAMP_100M:
+ resamp_inrate = RESAMP_100M_INRATE;
+ resamp_outrate = RESAMP_100M_OUTRATE;
+ break;
+ case RadioDevice::NORMAL:
+ default:
+ LOG(ALERT) << "Invalid device configuration";
+ return false;
+ }
+
+ resamp_inchunk = resamp_inrate * 4 * mSPSRx;
+ resamp_outchunk = resamp_outrate * 4 * mSPSRx;
+
+ if (mSPSTx == 4)
+ cutoff = RESAMP_TX4_FILTER;
+
+ dnsampler = new Resampler(resamp_inrate, resamp_outrate);
+ if (!dnsampler->init()) {
+ LOG(ALERT) << "Rx resampler failed to initialize";
+ return false;
+ }
+
+ upsampler = new Resampler(resamp_outrate, resamp_inrate);
+ if (!upsampler->init(cutoff)) {
+ LOG(ALERT) << "Tx resampler failed to initialize";
+ return false;
+ }
+
+ /*
+ * Allocate high and low rate buffers. The high rate receive
+ * buffer and low rate transmit vectors feed into the resampler
+ * and requires headroom equivalent to the filter length. Low
+ * rate buffers are allocated in the main radio interface code.
+ */
+ sendBuffer[0] = new RadioBuffer(NUMCHUNKS, resamp_inchunk,
+ upsampler->len(), true);
+ recvBuffer[0] = new RadioBuffer(NUMCHUNKS * 20, resamp_inchunk, 0, false);
+
+ outerSendBuffer =
+ new signalVector(NUMCHUNKS * resamp_outchunk);
+ outerRecvBuffer =
+ new signalVector(resamp_outchunk, dnsampler->len());
+
+ convertSendBuffer[0] = new short[outerSendBuffer->size() * 2];
+ convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2];
+
+ return true;
+}
+
+/* Receive a timestamped chunk from the device */
+int RadioInterfaceResamp::pullBuffer()
+{
+ bool local_underrun;
+ int rc, num_recv;
+
+ if (recvBuffer[0]->getFreeSegments() <= 0)
+ return -1;
+
+ /* Outer buffer access size is fixed */
+ num_recv = mRadio->readSamples(convertRecvBuffer,
+ resamp_outchunk,
+ &overrun,
+ readTimestamp,
+ &local_underrun);
+ if (num_recv != (int) resamp_outchunk) {
+ LOG(ALERT) << "Receive error " << num_recv;
+ return -1;
+ }
+
+ convert_short_float((float *) outerRecvBuffer->begin(),
+ convertRecvBuffer[0], 2 * resamp_outchunk);
+
+ underrun |= local_underrun;
+ readTimestamp += (TIMESTAMP) resamp_outchunk;
+
+ /* Write to the end of the inner receive buffer */
+ rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
+ resamp_outchunk,
+ recvBuffer[0]->getWriteSegment(),
+ resamp_inchunk);
+ if (rc < 0) {
+ LOG(ALERT) << "Sample rate upsampling error";
+ }
+
+ /* Set history for the next chunk */
+ outerRecvBuffer->updateHistory();
+ return 0;
+}
+
+/* Send a timestamped chunk to the device */
+bool RadioInterfaceResamp::pushBuffer()
+{
+ int rc;
+ size_t numSent;
+
+ if (sendBuffer[0]->getAvailSegments() <= 0)
+ return false;
+
+ /* Always send from the beginning of the buffer */
+ rc = upsampler->rotate(sendBuffer[0]->getReadSegment(),
+ resamp_inchunk,
+ (float *) outerSendBuffer->begin(),
+ resamp_outchunk);
+ if (rc < 0) {
+ LOG(ALERT) << "Sample rate downsampling error";
+ }
+
+ convert_float_short(convertSendBuffer[0],
+ (float *) outerSendBuffer->begin(),
+ powerScaling[0], 2 * resamp_outchunk);
+
+ numSent = mRadio->writeSamples(convertSendBuffer,
+ resamp_outchunk,
+ &underrun,
+ writeTimestamp);
+ if (numSent != resamp_outchunk) {
+ LOG(ALERT) << "Transmit error " << numSent;
+ }
+
+ writeTimestamp += resamp_outchunk;
+
+ return true;
+}
diff --git a/Transceiver52M/radioVector.cpp b/Transceiver52M/radioVector.cpp
new file mode 100644
index 0000000..2e3af9d
--- /dev/null
+++ b/Transceiver52M/radioVector.cpp
@@ -0,0 +1,155 @@
+/*
+ * Written by Thomas Tsou <ttsou@vt.edu>
+ * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
+ *
+ * Copyright 2011 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#include "radioVector.h"
+
+radioVector::radioVector(GSM::Time &time, size_t size,
+ size_t start, size_t chans)
+ : vectors(chans), mTime(time)
+{
+ for (size_t i = 0; i < vectors.size(); i++)
+ vectors[i] = new signalVector(size, start);
+}
+
+radioVector::radioVector(GSM::Time& wTime, signalVector *vector)
+ : vectors(1), mTime(wTime)
+{
+ vectors[0] = vector;
+}
+
+radioVector::~radioVector()
+{
+ for (size_t i = 0; i < vectors.size(); i++)
+ delete vectors[i];
+}
+
+GSM::Time radioVector::getTime() const
+{
+ return mTime;
+}
+
+void radioVector::setTime(const GSM::Time& wTime)
+{
+ mTime = wTime;
+}
+
+bool radioVector::operator>(const radioVector& other) const
+{
+ return mTime > other.mTime;
+}
+
+signalVector *radioVector::getVector(size_t chan) const
+{
+ if (chan >= vectors.size())
+ return NULL;
+
+ return vectors[chan];
+}
+
+bool radioVector::setVector(signalVector *vector, size_t chan)
+{
+ if (chan >= vectors.size())
+ return false;
+
+ vectors[chan] = vector;
+
+ return true;
+}
+
+noiseVector::noiseVector(size_t size)
+ : std::vector<float>(size), itr(0)
+{
+}
+
+float noiseVector::avg() const
+{
+ float val = 0.0;
+
+ for (size_t i = 0; i < size(); i++)
+ val += (*this)[i];
+
+ return val / (float) size();
+}
+
+bool noiseVector::insert(float val)
+{
+ if (!size())
+ return false;
+
+ if (itr >= this->size())
+ itr = 0;
+
+ (*this)[itr++] = val;
+
+ return true;
+}
+
+GSM::Time VectorQueue::nextTime() const
+{
+ GSM::Time retVal;
+ mLock.lock();
+
+ while (mQ.size()==0)
+ mWriteSignal.wait(mLock);
+
+ retVal = mQ.top()->getTime();
+ mLock.unlock();
+
+ return retVal;
+}
+
+radioVector* VectorQueue::getStaleBurst(const GSM::Time& targTime)
+{
+ mLock.lock();
+ if ((mQ.size()==0)) {
+ mLock.unlock();
+ return NULL;
+ }
+
+ if (mQ.top()->getTime() < targTime) {
+ radioVector* retVal = mQ.top();
+ mQ.pop();
+ mLock.unlock();
+ return retVal;
+ }
+ mLock.unlock();
+
+ return NULL;
+}
+
+radioVector* VectorQueue::getCurrentBurst(const GSM::Time& targTime)
+{
+ mLock.lock();
+ if ((mQ.size()==0)) {
+ mLock.unlock();
+ return NULL;
+ }
+
+ if (mQ.top()->getTime() == targTime) {
+ radioVector* retVal = mQ.top();
+ mQ.pop();
+ mLock.unlock();
+ return retVal;
+ }
+ mLock.unlock();
+
+ return NULL;
+}
diff --git a/Transceiver52M/radioVector.h b/Transceiver52M/radioVector.h
new file mode 100644
index 0000000..0566123
--- /dev/null
+++ b/Transceiver52M/radioVector.h
@@ -0,0 +1,68 @@
+/*
+ * Written by Thomas Tsou <ttsou@vt.edu>
+ * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
+ *
+ * Copyright 2011 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#ifndef RADIOVECTOR_H
+#define RADIOVECTOR_H
+
+#include "sigProcLib.h"
+#include "GSMCommon.h"
+#include "Interthread.h"
+
+class radioVector {
+public:
+ radioVector(GSM::Time& wTime, size_t size = 0,
+ size_t start = 0, size_t chans = 1);
+
+ radioVector(GSM::Time& wTime, signalVector *vector);
+ ~radioVector();
+
+ GSM::Time getTime() const;
+ void setTime(const GSM::Time& wTime);
+ bool operator>(const radioVector& other) const;
+
+ signalVector *getVector(size_t chan = 0) const;
+ bool setVector(signalVector *vector, size_t chan = 0);
+ size_t chans() const { return vectors.size(); }
+private:
+ std::vector<signalVector *> vectors;
+ GSM::Time mTime;
+};
+
+class noiseVector : std::vector<float> {
+public:
+ noiseVector(size_t size = 0);
+ bool insert(float val);
+ float avg() const;
+
+private:
+ size_t itr;
+};
+
+class VectorFIFO : public InterthreadQueue<radioVector> { };
+
+class VectorQueue : public InterthreadPriorityQueue<radioVector> {
+public:
+ GSM::Time nextTime() const;
+ radioVector* getStaleBurst(const GSM::Time& targTime);
+ radioVector* getCurrentBurst(const GSM::Time& targTime);
+};
+
+#endif /* RADIOVECTOR_H */
diff --git a/Transceiver52M/sigProcLib.cpp b/Transceiver52M/sigProcLib.cpp
new file mode 100644
index 0000000..28c4ded
--- /dev/null
+++ b/Transceiver52M/sigProcLib.cpp
@@ -0,0 +1,1892 @@
+/*
+* Copyright 2008, 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/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sigProcLib.h"
+#include "GSMCommon.h"
+#include "Logger.h"
+#include "Resampler.h"
+
+extern "C" {
+#include "convolve.h"
+#include "scale.h"
+#include "mult.h"
+}
+
+using namespace GSM;
+
+#define TABLESIZE 1024
+#define DELAYFILTS 64
+
+/* Clipping detection threshold */
+#define CLIP_THRESH 30000.0f
+
+/** Lookup tables for trigonometric approximation */
+static float sincTable[TABLESIZE+1]; // add 1 element for wrap around
+
+/** Constants */
+static const float M_PI_F = (float)M_PI;
+
+/* Precomputed rotation vectors */
+static signalVector *GMSKRotation4 = NULL;
+static signalVector *GMSKReverseRotation4 = NULL;
+static signalVector *GMSKRotation1 = NULL;
+static signalVector *GMSKReverseRotation1 = NULL;
+
+/* Precomputed fractional delay filters */
+static signalVector *delayFilters[DELAYFILTS];
+
+static const Complex<float> psk8_table[8] = {
+ Complex<float>(-0.70710678, 0.70710678),
+ Complex<float>( 0.0, -1.0),
+ Complex<float>( 0.0, 1.0),
+ Complex<float>( 0.70710678, -0.70710678),
+ Complex<float>(-1.0, 0.0),
+ Complex<float>(-0.70710678, -0.70710678),
+ Complex<float>( 0.70710678, 0.70710678),
+ Complex<float>( 1.0, 0.0),
+};
+
+/* Downsampling filterbank - 4 SPS to 1 SPS */
+#define DOWNSAMPLE_IN_LEN 624
+#define DOWNSAMPLE_OUT_LEN 156
+
+static Resampler *dnsampler = NULL;
+
+/*
+ * RACH and midamble correlation waveforms. Store the buffer separately
+ * because we need to allocate it explicitly outside of the signal vector
+ * constructor. This is because C++ (prior to C++11) is unable to natively
+ * perform 16-byte memory alignment required by many SSE instructions.
+ */
+struct CorrelationSequence {
+ CorrelationSequence() : sequence(NULL), buffer(NULL)
+ {
+ }
+
+ ~CorrelationSequence()
+ {
+ delete sequence;
+ free(buffer);
+ }
+
+ signalVector *sequence;
+ void *buffer;
+ float toa;
+ complex gain;
+};
+
+/*
+ * Gaussian and empty modulation pulses. Like the correlation sequences,
+ * store the runtime (Gaussian) buffer separately because of needed alignment
+ * for SSE instructions.
+ */
+struct PulseSequence {
+ PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL),
+ c0_buffer(NULL), c1_buffer(NULL), c0_inv_buffer(NULL)
+ {
+ }
+
+ ~PulseSequence()
+ {
+ delete c0;
+ delete c1;
+ delete c0_inv;
+ delete empty;
+ free(c0_buffer);
+ free(c1_buffer);
+ }
+
+ signalVector *c0;
+ signalVector *c1;
+ signalVector *c0_inv;
+ signalVector *empty;
+ void *c0_buffer;
+ void *c1_buffer;
+ void *c0_inv_buffer;
+};
+
+static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
+static PulseSequence *GSMPulse1 = NULL;
+static PulseSequence *GSMPulse4 = NULL;
+
+void sigProcLibDestroy()
+{
+ for (int i = 0; i < 8; i++) {
+ delete gMidambles[i];
+ delete gEdgeMidambles[i];
+ gMidambles[i] = NULL;
+ gEdgeMidambles[i] = NULL;
+ }
+
+ for (int i = 0; i < DELAYFILTS; i++) {
+ delete delayFilters[i];
+ delayFilters[i] = NULL;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ delete gRACHSequences[i];
+ gRACHSequences[i] = NULL;
+ }
+
+ delete GMSKRotation1;
+ delete GMSKReverseRotation1;
+ delete GMSKRotation4;
+ delete GMSKReverseRotation4;
+ delete GSMPulse1;
+ delete GSMPulse4;
+ delete dnsampler;
+
+ GMSKRotation1 = NULL;
+ GMSKRotation4 = NULL;
+ GMSKReverseRotation4 = NULL;
+ GMSKReverseRotation1 = NULL;
+ GSMPulse1 = NULL;
+ GSMPulse4 = NULL;
+}
+
+static float vectorNorm2(const signalVector &x)
+{
+ signalVector::const_iterator xPtr = x.begin();
+ float Energy = 0.0;
+ for (;xPtr != x.end();xPtr++) {
+ Energy += xPtr->norm2();
+ }
+ return Energy;
+}
+
+/*
+ * Initialize 4 sps and 1 sps rotation tables
+ */
+static void initGMSKRotationTables()
+{
+ size_t len1 = 157, len4 = 625;
+
+ GMSKRotation4 = new signalVector(len4);
+ GMSKReverseRotation4 = new signalVector(len4);
+ signalVector::iterator rotPtr = GMSKRotation4->begin();
+ signalVector::iterator revPtr = GMSKReverseRotation4->begin();
+ auto phase = 0.0;
+ while (rotPtr != GMSKRotation4->end()) {
+ *rotPtr++ = complex(cos(phase), sin(phase));
+ *revPtr++ = complex(cos(-phase), sin(-phase));
+ phase += M_PI / 2.0 / 4.0;
+ }
+
+ GMSKRotation1 = new signalVector(len1);
+ GMSKReverseRotation1 = new signalVector(len1);
+ rotPtr = GMSKRotation1->begin();
+ revPtr = GMSKReverseRotation1->begin();
+ phase = 0.0;
+ while (rotPtr != GMSKRotation1->end()) {
+ *rotPtr++ = complex(cos(phase), sin(phase));
+ *revPtr++ = complex(cos(-phase), sin(-phase));
+ phase += M_PI / 2.0;
+ }
+}
+
+static void GMSKRotate(signalVector &x, int sps)
+{
+#if HAVE_NEON
+ size_t len;
+ signalVector *a, *b, *out;
+
+ a = &x;
+ out = &x;
+ len = out->size();
+
+ if (len == 157)
+ len--;
+
+ if (sps == 1)
+ b = GMSKRotation1;
+ else
+ b = GMSKRotation4;
+
+ mul_complex((float *) out->begin(),
+ (float *) a->begin(),
+ (float *) b->begin(), len);
+#else
+ signalVector::iterator rotPtr, xPtr = x.begin();
+
+ if (sps == 1)
+ rotPtr = GMSKRotation1->begin();
+ else
+ rotPtr = GMSKRotation4->begin();
+
+ if (x.isReal()) {
+ while (xPtr < x.end()) {
+ *xPtr = *rotPtr++ * (xPtr->real());
+ xPtr++;
+ }
+ }
+ else {
+ while (xPtr < x.end()) {
+ *xPtr = *rotPtr++ * (*xPtr);
+ xPtr++;
+ }
+ }
+#endif
+}
+
+static bool GMSKReverseRotate(signalVector &x, int sps)
+{
+ signalVector::iterator rotPtr, xPtr= x.begin();
+
+ if (sps == 1)
+ rotPtr = GMSKReverseRotation1->begin();
+ else if (sps == 4)
+ rotPtr = GMSKReverseRotation4->begin();
+ else
+ return false;
+
+ if (x.isReal()) {
+ while (xPtr < x.end()) {
+ *xPtr = *rotPtr++ * (xPtr->real());
+ xPtr++;
+ }
+ }
+ else {
+ while (xPtr < x.end()) {
+ *xPtr = *rotPtr++ * (*xPtr);
+ xPtr++;
+ }
+ }
+
+ return true;
+}
+
+/** Convolution type indicator */
+enum ConvType {
+ START_ONLY,
+ NO_DELAY,
+ CUSTOM,
+ UNDEFINED,
+};
+
+static signalVector *convolve(const signalVector *x, const signalVector *h,
+ signalVector *y, ConvType spanType,
+ size_t start = 0, size_t len = 0,
+ size_t step = 1, int offset = 0)
+{
+ int rc;
+ size_t head = 0, tail = 0;
+ bool alloc = false, append = false;
+ const signalVector *_x = NULL;
+
+ if (!x || !h)
+ return NULL;
+
+ switch (spanType) {
+ case START_ONLY:
+ start = 0;
+ head = h->size() - 1;
+ len = x->size();
+
+ if (x->getStart() < head)
+ append = true;
+ break;
+ case NO_DELAY:
+ start = h->size() / 2;
+ head = start;
+ tail = start;
+ len = x->size();
+ append = true;
+ break;
+ case CUSTOM:
+ if (start < h->size() - 1) {
+ head = h->size() - start;
+ append = true;
+ }
+ if (start + len > x->size()) {
+ tail = start + len - x->size();
+ append = true;
+ }
+ break;
+ default:
+ return NULL;
+ }
+
+ /*
+ * Error if the output vector is too small. Create the output vector
+ * if the pointer is NULL.
+ */
+ if (y && (len > y->size()))
+ return NULL;
+ if (!y) {
+ y = new signalVector(len);
+ alloc = true;
+ }
+
+ /* Prepend or post-pend the input vector if the parameters require it */
+ if (append)
+ _x = new signalVector(*x, head, tail);
+ else
+ _x = x;
+
+ /*
+ * Four convovle types:
+ * 1. Complex-Real (aligned)
+ * 2. Complex-Complex (aligned)
+ * 3. Complex-Real (!aligned)
+ * 4. Complex-Complex (!aligned)
+ */
+ if (h->isReal() && h->isAligned()) {
+ rc = convolve_real((float *) _x->begin(), _x->size(),
+ (float *) h->begin(), h->size(),
+ (float *) y->begin(), y->size(),
+ start, len, step, offset);
+ } else if (!h->isReal() && h->isAligned()) {
+ rc = convolve_complex((float *) _x->begin(), _x->size(),
+ (float *) h->begin(), h->size(),
+ (float *) y->begin(), y->size(),
+ start, len, step, offset);
+ } else if (h->isReal() && !h->isAligned()) {
+ rc = base_convolve_real((float *) _x->begin(), _x->size(),
+ (float *) h->begin(), h->size(),
+ (float *) y->begin(), y->size(),
+ start, len, step, offset);
+ } else if (!h->isReal() && !h->isAligned()) {
+ rc = base_convolve_complex((float *) _x->begin(), _x->size(),
+ (float *) h->begin(), h->size(),
+ (float *) y->begin(), y->size(),
+ start, len, step, offset);
+ } else {
+ rc = -1;
+ }
+
+ if (append)
+ delete _x;
+
+ if (rc < 0) {
+ if (alloc)
+ delete y;
+ return NULL;
+ }
+
+ return y;
+}
+
+/*
+ * Generate static EDGE linear equalizer. This equalizer is not adaptive.
+ * Filter taps are generated from the inverted 1 SPS impulse response of
+ * the EDGE pulse shape captured after the downsampling filter.
+ */
+static bool generateInvertC0Pulse(PulseSequence *pulse)
+{
+ if (!pulse)
+ return false;
+
+ pulse->c0_inv_buffer = convolve_h_alloc(5);
+ pulse->c0_inv = new signalVector((complex *) pulse->c0_inv_buffer, 0, 5);
+ pulse->c0_inv->isReal(true);
+ pulse->c0_inv->setAligned(false);
+
+ signalVector::iterator xP = pulse->c0_inv->begin();
+ *xP++ = 0.15884;
+ *xP++ = -0.43176;
+ *xP++ = 1.00000;
+ *xP++ = -0.42608;
+ *xP++ = 0.14882;
+
+ return true;
+}
+
+static bool generateC1Pulse(int sps, PulseSequence *pulse)
+{
+ int len;
+
+ if (!pulse)
+ return false;
+
+ switch (sps) {
+ case 4:
+ len = 8;
+ break;
+ default:
+ return false;
+ }
+
+ pulse->c1_buffer = convolve_h_alloc(len);
+ pulse->c1 = new signalVector((complex *)
+ pulse->c1_buffer, 0, len);
+ pulse->c1->isReal(true);
+
+ /* Enable alignment for SSE usage */
+ pulse->c1->setAligned(true);
+
+ signalVector::iterator xP = pulse->c1->begin();
+
+ switch (sps) {
+ case 4:
+ /* BT = 0.30 */
+ *xP++ = 0.0;
+ *xP++ = 8.16373112e-03;
+ *xP++ = 2.84385729e-02;
+ *xP++ = 5.64158904e-02;
+ *xP++ = 7.05463553e-02;
+ *xP++ = 5.64158904e-02;
+ *xP++ = 2.84385729e-02;
+ *xP++ = 8.16373112e-03;
+ }
+
+ return true;
+}
+
+static PulseSequence *generateGSMPulse(int sps)
+{
+ int len;
+ float arg, avg, center;
+ PulseSequence *pulse;
+
+ if ((sps != 1) && (sps != 4))
+ return NULL;
+
+ /* Store a single tap filter used for correlation sequence generation */
+ pulse = new PulseSequence();
+ pulse->empty = new signalVector(1);
+ pulse->empty->isReal(true);
+ *(pulse->empty->begin()) = 1.0f;
+
+ /*
+ * For 4 samples-per-symbol use a precomputed single pulse Laurent
+ * approximation. This should yields below 2 degrees of phase error at
+ * the modulator output. Use the existing pulse approximation for all
+ * other oversampling factors.
+ */
+ switch (sps) {
+ case 4:
+ len = 16;
+ break;
+ case 1:
+ default:
+ len = 4;
+ }
+
+ pulse->c0_buffer = convolve_h_alloc(len);
+ pulse->c0 = new signalVector((complex *) pulse->c0_buffer, 0, len);
+ pulse->c0->isReal(true);
+
+ /* Enable alingnment for SSE usage */
+ pulse->c0->setAligned(true);
+
+ signalVector::iterator xP = pulse->c0->begin();
+
+ if (sps == 4) {
+ *xP++ = 0.0;
+ *xP++ = 4.46348606e-03;
+ *xP++ = 2.84385729e-02;
+ *xP++ = 1.03184855e-01;
+ *xP++ = 2.56065552e-01;
+ *xP++ = 4.76375085e-01;
+ *xP++ = 7.05961177e-01;
+ *xP++ = 8.71291644e-01;
+ *xP++ = 9.29453645e-01;
+ *xP++ = 8.71291644e-01;
+ *xP++ = 7.05961177e-01;
+ *xP++ = 4.76375085e-01;
+ *xP++ = 2.56065552e-01;
+ *xP++ = 1.03184855e-01;
+ *xP++ = 2.84385729e-02;
+ *xP++ = 4.46348606e-03;
+ generateC1Pulse(sps, pulse);
+ } else {
+ center = (float) (len - 1.0) / 2.0;
+
+ /* GSM pulse approximation */
+ for (int i = 0; i < len; i++) {
+ arg = ((float) i - center) / (float) sps;
+ *xP++ = 0.96 * exp(-1.1380 * arg * arg -
+ 0.527 * arg * arg * arg * arg);
+ }
+
+ avg = sqrtf(vectorNorm2(*pulse->c0) / sps);
+ xP = pulse->c0->begin();
+ for (int i = 0; i < len; i++)
+ *xP++ /= avg;
+ }
+
+ /*
+ * Current form of the EDGE equalization filter non-realizable at 4 SPS.
+ * Load the onto both 1 SPS and 4 SPS objects for convenience. Note that
+ * the EDGE demodulator downsamples to 1 SPS prior to equalization.
+ */
+ generateInvertC0Pulse(pulse);
+
+ return pulse;
+}
+
+bool vectorSlicer(SoftVector *x)
+{
+ SoftVector::iterator xP = x->begin();
+ SoftVector::iterator xPEnd = x->end();
+ while (xP < xPEnd) {
+ *xP = 0.5 * (*xP + 1.0f);
+ if (*xP > 1.0)
+ *xP = 1.0;
+ if (*xP < 0.0)
+ *xP = 0.0;
+ xP++;
+ }
+ return true;
+}
+
+static signalVector *rotateBurst(const BitVector &wBurst,
+ int guardPeriodLength, int sps)
+{
+ int burst_len;
+ signalVector *pulse, rotated;
+ signalVector::iterator itr;
+
+ pulse = GSMPulse1->empty;
+ burst_len = sps * (wBurst.size() + guardPeriodLength);
+ rotated = signalVector(burst_len);
+ itr = rotated.begin();
+
+ for (unsigned i = 0; i < wBurst.size(); i++) {
+ *itr = 2.0 * (wBurst[i] & 0x01) - 1.0;
+ itr += sps;
+ }
+
+ GMSKRotate(rotated, sps);
+ rotated.isReal(false);
+
+ /* Dummy filter operation */
+ return convolve(&rotated, pulse, NULL, START_ONLY);
+}
+
+static void rotateBurst2(signalVector &burst, double phase)
+{
+ Complex<float> rot = Complex<float>(cos(phase), sin(phase));
+
+ for (size_t i = 0; i < burst.size(); i++)
+ burst[i] = burst[i] * rot;
+}
+
+/*
+ * Ignore the guard length argument in the GMSK modulator interface
+ * because it results in 624/628 sized bursts instead of the preferred
+ * burst length of 625. Only 4 SPS is supported.
+ */
+static signalVector *modulateBurstLaurent(const BitVector &bits)
+{
+ int burst_len, sps = 4;
+ float phase;
+ signalVector *c0_pulse, *c1_pulse, *c0_shaped, *c1_shaped;
+ signalVector::iterator c0_itr, c1_itr;
+
+ c0_pulse = GSMPulse4->c0;
+ c1_pulse = GSMPulse4->c1;
+
+ if (bits.size() > 156)
+ return NULL;
+
+ burst_len = 625;
+
+ signalVector c0_burst(burst_len, c0_pulse->size());
+ c0_burst.isReal(true);
+ c0_itr = c0_burst.begin();
+
+ signalVector c1_burst(burst_len, c1_pulse->size());
+ c1_itr = c1_burst.begin();
+
+ /* Padded differential tail bits */
+ *c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
+ c0_itr += sps;
+
+ /* Main burst bits */
+ for (unsigned i = 0; i < bits.size(); i++) {
+ *c0_itr = 2.0 * (bits[i] & 0x01) - 1.0;
+ c0_itr += sps;
+ }
+
+ /* Padded differential tail bits */
+ *c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
+
+ /* Generate C0 phase coefficients */
+ GMSKRotate(c0_burst, sps);
+ c0_burst.isReal(false);
+
+ c0_itr = c0_burst.begin();
+ c0_itr += sps * 2;
+ c1_itr += sps * 2;
+
+ /* Start magic */
+ phase = 2.0 * ((0x01 & 0x01) ^ (0x01 & 0x01)) - 1.0;
+ *c1_itr = *c0_itr * Complex<float>(0, phase);
+ c0_itr += sps;
+ c1_itr += sps;
+
+ /* Generate C1 phase coefficients */
+ for (unsigned i = 2; i < bits.size(); i++) {
+ phase = 2.0 * ((bits[i - 1] & 0x01) ^ (bits[i - 2] & 0x01)) - 1.0;
+ *c1_itr = *c0_itr * Complex<float>(0, phase);
+
+ c0_itr += sps;
+ c1_itr += sps;
+ }
+
+ /* End magic */
+ int i = bits.size();
+ phase = 2.0 * ((bits[i-1] & 0x01) ^ (bits[i-2] & 0x01)) - 1.0;
+ *c1_itr = *c0_itr * Complex<float>(0, phase);
+
+ /* Primary (C0) and secondary (C1) pulse shaping */
+ c0_shaped = convolve(&c0_burst, c0_pulse, NULL, START_ONLY);
+ c1_shaped = convolve(&c1_burst, c1_pulse, NULL, START_ONLY);
+
+ /* Sum shaped outputs into C0 */
+ c0_itr = c0_shaped->begin();
+ c1_itr = c1_shaped->begin();
+ for (unsigned i = 0; i < c0_shaped->size(); i++ )
+ *c0_itr++ += *c1_itr++;
+
+ delete c1_shaped;
+ return c0_shaped;
+}
+
+static signalVector *rotateEdgeBurst(const signalVector &symbols, int sps)
+{
+ signalVector *burst;
+ signalVector::iterator burst_itr;
+
+ burst = new signalVector(symbols.size() * sps);
+ burst_itr = burst->begin();
+
+ for (size_t i = 0; i < symbols.size(); i++) {
+ float phase = i * 3.0f * M_PI / 8.0f;
+ Complex<float> rot = Complex<float>(cos(phase), sin(phase));
+
+ *burst_itr = symbols[i] * rot;
+ burst_itr += sps;
+ }
+
+ return burst;
+}
+
+static signalVector *derotateEdgeBurst(const signalVector &symbols, int sps)
+{
+ signalVector *burst;
+ signalVector::iterator burst_itr;
+
+ if (symbols.size() % sps)
+ return NULL;
+
+ burst = new signalVector(symbols.size() / sps);
+ burst_itr = burst->begin();
+
+ for (size_t i = 0; i < burst->size(); i++) {
+ float phase = (float) (i % 16) * 3.0f * M_PI / 8.0f;
+ Complex<float> rot = Complex<float>(cosf(phase), -sinf(phase));
+
+ *burst_itr = symbols[sps * i] * rot;
+ burst_itr++;
+ }
+
+ return burst;
+}
+
+static signalVector *mapEdgeSymbols(const BitVector &bits)
+{
+ if (bits.size() % 3)
+ return NULL;
+
+ signalVector *symbols = new signalVector(bits.size() / 3);
+
+ for (size_t i = 0; i < symbols->size(); i++) {
+ unsigned index = (((unsigned) bits[3 * i + 0] & 0x01) << 0) |
+ (((unsigned) bits[3 * i + 1] & 0x01) << 1) |
+ (((unsigned) bits[3 * i + 2] & 0x01) << 2);
+
+ (*symbols)[i] = psk8_table[index];
+ }
+
+ return symbols;
+}
+
+/*
+ * EDGE 8-PSK rotate and pulse shape
+ *
+ * Delay the EDGE downlink bursts by one symbol in order to match GMSK pulse
+ * shaping group delay. The difference in group delay arises from the dual
+ * pulse filter combination of the GMSK Laurent represenation whereas 8-PSK
+ * uses a single pulse linear filter.
+ */
+static signalVector *shapeEdgeBurst(const signalVector &symbols)
+{
+ size_t nsyms, nsamps = 625, sps = 4;
+ signalVector::iterator burst_itr;
+
+ nsyms = symbols.size();
+
+ if (nsyms * sps > nsamps)
+ nsyms = 156;
+
+ signalVector burst(nsamps, GSMPulse4->c0->size());
+
+ /* Delay burst by 1 symbol */
+ burst_itr = burst.begin() + sps;
+ for (size_t i = 0; i < nsyms; i++) {
+ float phase = i * 3.0f * M_PI / 8.0f;
+ Complex<float> rot = Complex<float>(cos(phase), sin(phase));
+
+ *burst_itr = symbols[i] * rot;
+ burst_itr += sps;
+ }
+
+ /* Single Gaussian pulse approximation shaping */
+ return convolve(&burst, GSMPulse4->c0, NULL, START_ONLY);
+}
+
+/*
+ * Generate a random GSM normal burst.
+ */
+signalVector *genRandNormalBurst(int tsc, int sps, int tn)
+{
+ if ((tsc < 0) || (tsc > 7) || (tn < 0) || (tn > 7))
+ return NULL;
+ if ((sps != 1) && (sps != 4))
+ return NULL;
+
+ int i = 0;
+ BitVector bits(148);
+
+ /* Tail bits */
+ for (; i < 3; i++)
+ bits[i] = 0;
+
+ /* Random bits */
+ for (; i < 60; i++)
+ bits[i] = rand() % 2;
+
+ /* Stealing bit */
+ bits[i++] = 0;
+
+ /* Training sequence */
+ for (int n = 0; i < 87; i++, n++)
+ bits[i] = gTrainingSequence[tsc][n];
+
+ /* Stealing bit */
+ bits[i++] = 0;
+
+ /* Random bits */
+ for (; i < 145; i++)
+ bits[i] = rand() % 2;
+
+ /* Tail bits */
+ for (; i < 148; i++)
+ bits[i] = 0;
+
+ int guard = 8 + !(tn % 4);
+ return modulateBurst(bits, guard, sps);
+}
+
+/*
+ * Generate a random GSM access burst.
+ */
+signalVector *genRandAccessBurst(int delay, int sps, int tn)
+{
+ if ((tn < 0) || (tn > 7))
+ return NULL;
+ if ((sps != 1) && (sps != 4))
+ return NULL;
+ if (delay > 68)
+ return NULL;
+
+ int i = 0;
+ BitVector bits(88 + delay);
+
+ /* delay */
+ for (; i < delay; i++)
+ bits[i] = 0;
+
+ /* head and synch bits */
+ for (int n = 0; i < 49+delay; i++, n++)
+ bits[i] = gRACHBurst[n];
+
+ /* Random bits */
+ for (; i < 85+delay; i++)
+ bits[i] = rand() % 2;
+
+ /* Tail bits */
+ for (; i < 88+delay; i++)
+ bits[i] = 0;
+
+ int guard = 68-delay + !(tn % 4);
+ return modulateBurst(bits, guard, sps);
+}
+
+signalVector *generateEmptyBurst(int sps, int tn)
+{
+ if ((tn < 0) || (tn > 7))
+ return NULL;
+
+ if (sps == 4)
+ return new signalVector(625);
+ else if (sps == 1)
+ return new signalVector(148 + 8 + !(tn % 4));
+ else
+ return NULL;
+}
+
+signalVector *generateDummyBurst(int sps, int tn)
+{
+ if (((sps != 1) && (sps != 4)) || (tn < 0) || (tn > 7))
+ return NULL;
+
+ return modulateBurst(gDummyBurst, 8 + !(tn % 4), sps);
+}
+
+/*
+ * Generate a random 8-PSK EDGE burst. Only 4 SPS is supported with
+ * the returned burst being 625 samples in length.
+ */
+signalVector *generateEdgeBurst(int tsc)
+{
+ int tail = 9 / 3;
+ int data = 174 / 3;
+ int train = 78 / 3;
+
+ if ((tsc < 0) || (tsc > 7))
+ return NULL;
+
+ signalVector burst(148);
+ const BitVector *midamble = &gEdgeTrainingSequence[tsc];
+
+ /* Tail */
+ int n, i = 0;
+ for (; i < tail; i++)
+ burst[i] = psk8_table[7];
+
+ /* Body */
+ for (; i < tail + data; i++)
+ burst[i] = psk8_table[rand() % 8];
+
+ /* TSC */
+ for (n = 0; i < tail + data + train; i++, n++) {
+ unsigned index = (((unsigned) (*midamble)[3 * n + 0] & 0x01) << 0) |
+ (((unsigned) (*midamble)[3 * n + 1] & 0x01) << 1) |
+ (((unsigned) (*midamble)[3 * n + 2] & 0x01) << 2);
+
+ burst[i] = psk8_table[index];
+ }
+
+ /* Body */
+ for (; i < tail + data + train + data; i++)
+ burst[i] = psk8_table[rand() % 8];
+
+ /* Tail */
+ for (; i < tail + data + train + data + tail; i++)
+ burst[i] = psk8_table[7];
+
+ return shapeEdgeBurst(burst);
+}
+
+/*
+ * Modulate 8-PSK burst. When empty pulse shaping (rotation only)
+ * is enabled, the output vector length will be bit sequence length
+ * times the SPS value. When pulse shaping is enabled, the output
+ * vector length is fixed at 625 samples (156.25 symbols at 4 SPS).
+ * Pulse shaped bit sequences that go beyond one burst are truncated.
+ * Pulse shaping at anything but 4 SPS is not supported.
+ */
+signalVector *modulateEdgeBurst(const BitVector &bits,
+ int sps, bool empty)
+{
+ signalVector *shape, *burst;
+
+ if ((sps != 4) && !empty)
+ return NULL;
+
+ burst = mapEdgeSymbols(bits);
+ if (!burst)
+ return NULL;
+
+ if (empty)
+ shape = rotateEdgeBurst(*burst, sps);
+ else
+ shape = shapeEdgeBurst(*burst);
+
+ delete burst;
+ return shape;
+}
+
+static signalVector *modulateBurstBasic(const BitVector &bits,
+ int guard_len, int sps)
+{
+ int burst_len;
+ signalVector *pulse;
+ signalVector::iterator burst_itr;
+
+ if (sps == 1)
+ pulse = GSMPulse1->c0;
+ else
+ pulse = GSMPulse4->c0;
+
+ burst_len = sps * (bits.size() + guard_len);
+
+ signalVector burst(burst_len, pulse->size());
+ burst.isReal(true);
+ burst_itr = burst.begin();
+
+ /* Raw bits are not differentially encoded */
+ for (unsigned i = 0; i < bits.size(); i++) {
+ *burst_itr = 2.0 * (bits[i] & 0x01) - 1.0;
+ burst_itr += sps;
+ }
+
+ GMSKRotate(burst, sps);
+ burst.isReal(false);
+
+ /* Single Gaussian pulse approximation shaping */
+ return convolve(&burst, pulse, NULL, START_ONLY);
+}
+
+/* Assume input bits are not differentially encoded */
+signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength,
+ int sps, bool emptyPulse)
+{
+ if (emptyPulse)
+ return rotateBurst(wBurst, guardPeriodLength, sps);
+ else if (sps == 4)
+ return modulateBurstLaurent(wBurst);
+ else
+ return modulateBurstBasic(wBurst, guardPeriodLength, sps);
+}
+
+static void generateSincTable()
+{
+ for (int i = 0; i < TABLESIZE; i++) {
+ auto x = (double) i / TABLESIZE * 8 * M_PI;
+ auto y = sin(x) / x;
+ sincTable[i] = std::isnan(y) ? 1.0 : y;
+ }
+}
+
+static float sinc(float x)
+{
+ if (fabs(x) >= 8 * M_PI)
+ return 0.0;
+
+ int index = (int) floorf(fabs(x) / (8 * M_PI) * TABLESIZE);
+
+ return sincTable[index];
+}
+
+/*
+ * Create fractional delay filterbank with Blackman-harris windowed
+ * sinc function generator. The number of filters generated is specified
+ * by the DELAYFILTS value.
+ */
+static void generateDelayFilters()
+{
+ int h_len = 20;
+ complex *data;
+ signalVector *h;
+ signalVector::iterator itr;
+
+ float k, sum;
+ float a0 = 0.35875;
+ float a1 = 0.48829;
+ float a2 = 0.14128;
+ float a3 = 0.01168;
+
+ for (int i = 0; i < DELAYFILTS; i++) {
+ data = (complex *) convolve_h_alloc(h_len);
+ h = new signalVector(data, 0, h_len);
+ h->setAligned(true);
+ h->isReal(true);
+
+ sum = 0.0;
+ itr = h->end();
+ for (int n = 0; n < h_len; n++) {
+ k = (float) n;
+ *--itr = (complex) sinc(M_PI_F *
+ (k - (float) h_len / 2.0 - (float) i / DELAYFILTS));
+ *itr *= a0 -
+ a1 * cos(2 * M_PI * n / (h_len - 1)) +
+ a2 * cos(4 * M_PI * n / (h_len - 1)) -
+ a3 * cos(6 * M_PI * n / (h_len - 1));
+
+ sum += itr->real();
+ }
+
+ itr = h->begin();
+ for (int n = 0; n < h_len; n++)
+ *itr++ /= sum;
+
+ delayFilters[i] = h;
+ }
+}
+
+signalVector *delayVector(const signalVector *in, signalVector *out, float delay)
+{
+ int whole, index;
+ float frac;
+ signalVector *h, *shift, *fshift = NULL;
+
+ whole = floor(delay);
+ frac = delay - whole;
+
+ /* Sinc interpolated fractional shift (if allowable) */
+ if (fabs(frac) > 1e-2) {
+ index = floorf(frac * (float) DELAYFILTS);
+ h = delayFilters[index];
+
+ fshift = convolve(in, h, NULL, NO_DELAY);
+ if (!fshift)
+ return NULL;
+ }
+
+ if (!fshift)
+ shift = new signalVector(*in);
+ else
+ shift = fshift;
+
+ /* Integer sample shift */
+ if (whole < 0) {
+ whole = -whole;
+ signalVector::iterator wBurstItr = shift->begin();
+ signalVector::iterator shiftedItr = shift->begin() + whole;
+
+ while (shiftedItr < shift->end())
+ *wBurstItr++ = *shiftedItr++;
+
+ while (wBurstItr < shift->end())
+ *wBurstItr++ = 0.0;
+ } else if (whole >= 0) {
+ signalVector::iterator wBurstItr = shift->end() - 1;
+ signalVector::iterator shiftedItr = shift->end() - 1 - whole;
+
+ while (shiftedItr >= shift->begin())
+ *wBurstItr-- = *shiftedItr--;
+
+ while (wBurstItr >= shift->begin())
+ *wBurstItr-- = 0.0;
+ }
+
+ if (!out)
+ return shift;
+
+ out->clone(*shift);
+ delete shift;
+ return out;
+}
+
+static complex interpolatePoint(const signalVector &inSig, float ix)
+{
+ int start = (int) (floor(ix) - 10);
+ if (start < 0) start = 0;
+ int end = (int) (floor(ix) + 11);
+ if ((unsigned) end > inSig.size()-1) end = inSig.size()-1;
+
+ complex pVal = 0.0;
+ if (!inSig.isReal()) {
+ for (int i = start; i < end; i++)
+ pVal += inSig[i] * sinc(M_PI_F*(i-ix));
+ }
+ else {
+ for (int i = start; i < end; i++)
+ pVal += inSig[i].real() * sinc(M_PI_F*(i-ix));
+ }
+
+ return pVal;
+}
+
+static complex fastPeakDetect(const signalVector &rxBurst, float *index)
+{
+ float val, max = 0.0f;
+ complex amp;
+ int _index = -1;
+
+ for (size_t i = 0; i < rxBurst.size(); i++) {
+ val = rxBurst[i].norm2();
+ if (val > max) {
+ max = val;
+ _index = i;
+ amp = rxBurst[i];
+ }
+ }
+
+ if (index)
+ *index = (float) _index;
+
+ return amp;
+}
+
+static complex peakDetect(const signalVector &rxBurst,
+ float *peakIndex, float *avgPwr)
+{
+ complex maxVal = 0.0;
+ float maxIndex = -1;
+ float sumPower = 0.0;
+
+ for (unsigned int i = 0; i < rxBurst.size(); i++) {
+ float samplePower = rxBurst[i].norm2();
+ if (samplePower > maxVal.real()) {
+ maxVal = samplePower;
+ maxIndex = i;
+ }
+ sumPower += samplePower;
+ }
+
+ // interpolate around the peak
+ // to save computation, we'll use early-late balancing
+ float earlyIndex = maxIndex-1;
+ float lateIndex = maxIndex+1;
+
+ float incr = 0.5;
+ while (incr > 1.0/1024.0) {
+ complex earlyP = interpolatePoint(rxBurst,earlyIndex);
+ complex lateP = interpolatePoint(rxBurst,lateIndex);
+ if (earlyP < lateP)
+ earlyIndex += incr;
+ else if (earlyP > lateP)
+ earlyIndex -= incr;
+ else break;
+ incr /= 2.0;
+ lateIndex = earlyIndex + 2.0;
+ }
+
+ maxIndex = earlyIndex + 1.0;
+ maxVal = interpolatePoint(rxBurst,maxIndex);
+
+ if (peakIndex!=NULL)
+ *peakIndex = maxIndex;
+
+ if (avgPwr!=NULL)
+ *avgPwr = (sumPower-maxVal.norm2()) / (rxBurst.size()-1);
+
+ return maxVal;
+
+}
+
+void scaleVector(signalVector &x,
+ complex scale)
+{
+#ifdef HAVE_NEON
+ int len = x.size();
+
+ scale_complex((float *) x.begin(),
+ (float *) x.begin(),
+ (float *) &scale, len);
+#else
+ signalVector::iterator xP = x.begin();
+ signalVector::iterator xPEnd = x.end();
+ if (!x.isReal()) {
+ while (xP < xPEnd) {
+ *xP = *xP * scale;
+ xP++;
+ }
+ }
+ else {
+ while (xP < xPEnd) {
+ *xP = xP->real() * scale;
+ xP++;
+ }
+ }
+#endif
+}
+
+/** in-place conjugation */
+static void conjugateVector(signalVector &x)
+{
+ if (x.isReal()) return;
+ signalVector::iterator xP = x.begin();
+ signalVector::iterator xPEnd = x.end();
+ while (xP < xPEnd) {
+ *xP = xP->conj();
+ xP++;
+ }
+}
+
+static bool generateMidamble(int sps, int tsc)
+{
+ bool status = true;
+ float toa;
+ complex *data = NULL;
+ signalVector *autocorr = NULL, *midamble = NULL;
+ signalVector *midMidamble = NULL, *_midMidamble = NULL;
+
+ if ((tsc < 0) || (tsc > 7))
+ return false;
+
+ delete gMidambles[tsc];
+
+ /* Use middle 16 bits of each TSC. Correlation sequence is not pulse shaped */
+ midMidamble = modulateBurst(gTrainingSequence[tsc].segment(5,16), 0, sps, true);
+ if (!midMidamble)
+ return false;
+
+ /* Simulated receive sequence is pulse shaped */
+ midamble = modulateBurst(gTrainingSequence[tsc], 0, sps, false);
+ if (!midamble) {
+ status = false;
+ goto release;
+ }
+
+ // NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst,
+ // the ideal TSC has an + 180 degree phase shift,
+ // due to the pi/2 frequency shift, that
+ // needs to be accounted for.
+ // 26-midamble is 61 symbols into burst, has +90 degree phase shift.
+ scaleVector(*midMidamble, complex(-1.0, 0.0));
+ scaleVector(*midamble, complex(0.0, 1.0));
+
+ conjugateVector(*midMidamble);
+
+ /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
+ data = (complex *) convolve_h_alloc(midMidamble->size());
+ _midMidamble = new signalVector(data, 0, midMidamble->size());
+ _midMidamble->setAligned(true);
+ midMidamble->copyTo(*_midMidamble);
+
+ autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY);
+ if (!autocorr) {
+ status = false;
+ goto release;
+ }
+
+ gMidambles[tsc] = new CorrelationSequence;
+ gMidambles[tsc]->buffer = data;
+ gMidambles[tsc]->sequence = _midMidamble;
+ gMidambles[tsc]->gain = peakDetect(*autocorr, &toa, NULL);
+
+ /* For 1 sps only
+ * (Half of correlation length - 1) + midpoint of pulse shape + remainder
+ * 13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2
+ */
+ if (sps == 1)
+ gMidambles[tsc]->toa = toa - 13.5;
+ else
+ gMidambles[tsc]->toa = 0;
+
+release:
+ delete autocorr;
+ delete midamble;
+ delete midMidamble;
+
+ if (!status) {
+ delete _midMidamble;
+ free(data);
+ gMidambles[tsc] = NULL;
+ }
+
+ return status;
+}
+
+static CorrelationSequence *generateEdgeMidamble(int tsc)
+{
+ complex *data = NULL;
+ signalVector *midamble = NULL, *_midamble = NULL;
+ CorrelationSequence *seq;
+
+ if ((tsc < 0) || (tsc > 7))
+ return NULL;
+
+ /* Use middle 48 bits of each TSC. Correlation sequence is not pulse shaped */
+ const BitVector *bits = &gEdgeTrainingSequence[tsc];
+ midamble = modulateEdgeBurst(bits->segment(15, 48), 1, true);
+ if (!midamble)
+ return NULL;
+
+ conjugateVector(*midamble);
+
+ data = (complex *) convolve_h_alloc(midamble->size());
+ _midamble = new signalVector(data, 0, midamble->size());
+ _midamble->setAligned(true);
+ midamble->copyTo(*_midamble);
+
+ /* Channel gain is an empirically measured value */
+ seq = new CorrelationSequence;
+ seq->buffer = data;
+ seq->sequence = _midamble;
+ seq->gain = Complex<float>(-19.6432, 19.5006) / 1.18;
+ seq->toa = 0;
+
+ delete midamble;
+
+ return seq;
+}
+
+static bool generateRACHSequence(CorrelationSequence **seq, const BitVector &bv, int sps)
+{
+ bool status = true;
+ float toa;
+ complex *data = NULL;
+ signalVector *autocorr = NULL;
+ signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
+
+ if (*seq != NULL)
+ delete *seq;
+
+ seq0 = modulateBurst(bv, 0, sps, false);
+ if (!seq0)
+ return false;
+
+ seq1 = modulateBurst(bv.segment(0, 40), 0, sps, true);
+ if (!seq1) {
+ status = false;
+ goto release;
+ }
+
+ conjugateVector(*seq1);
+
+ /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
+ data = (complex *) convolve_h_alloc(seq1->size());
+ _seq1 = new signalVector(data, 0, seq1->size());
+ _seq1->setAligned(true);
+ seq1->copyTo(*_seq1);
+
+ autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
+ if (!autocorr) {
+ status = false;
+ goto release;
+ }
+
+ *seq = new CorrelationSequence;
+ (*seq)->sequence = _seq1;
+ (*seq)->buffer = data;
+ (*seq)->gain = peakDetect(*autocorr, &toa, NULL);
+
+ /* For 1 sps only
+ * (Half of correlation length - 1) + midpoint of pulse shaping filer
+ * 20.5 = (40 / 2 - 1) + 1.5
+ */
+ if (sps == 1)
+ (*seq)->toa = toa - 20.5;
+ else
+ (*seq)->toa = 0.0;
+
+release:
+ delete autocorr;
+ delete seq0;
+ delete seq1;
+
+ if (!status) {
+ delete _seq1;
+ free(data);
+ *seq = NULL;
+ }
+
+ return status;
+}
+
+/*
+ * Peak-to-average computation +/- range from peak in symbols
+ */
+#define COMPUTE_PEAK_MIN 2
+#define COMPUTE_PEAK_MAX 5
+
+/*
+ * Minimum number of values needed to compute peak-to-average
+ */
+#define COMPUTE_PEAK_CNT 5
+
+static float computePeakRatio(signalVector *corr,
+ int sps, float toa, complex amp)
+{
+ int num = 0;
+ complex *peak;
+ float rms, avg = 0.0;
+
+ /* Check for bogus results */
+ if ((toa < 0.0) || (toa > corr->size()))
+ return 0.0;
+
+ peak = corr->begin() + (int) rint(toa);
+
+ for (int i = COMPUTE_PEAK_MIN * sps; i <= COMPUTE_PEAK_MAX * sps; i++) {
+ if (peak - i >= corr->begin()) {
+ avg += (peak - i)->norm2();
+ num++;
+ }
+ if (peak + i < corr->end()) {
+ avg += (peak + i)->norm2();
+ num++;
+ }
+ }
+
+ if (num < COMPUTE_PEAK_CNT)
+ return 0.0;
+
+ rms = sqrtf(avg / (float) num) + 0.00001;
+
+ return (amp.abs()) / rms;
+}
+
+float energyDetect(const signalVector &rxBurst, unsigned windowLength)
+{
+
+ signalVector::const_iterator windowItr = rxBurst.begin(); //+rxBurst.size()/2 - 5*windowLength/2;
+ float energy = 0.0;
+ if (windowLength == 0) return 0.0;
+ if (windowLength > rxBurst.size()) windowLength = rxBurst.size();
+ for (unsigned i = 0; i < windowLength; i++) {
+ energy += windowItr->norm2();
+ windowItr+=4;
+ }
+ return energy/windowLength;
+}
+
+static signalVector *downsampleBurst(const signalVector &burst)
+{
+ signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len());
+ signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN);
+ burst.copyToSegment(in, 0, DOWNSAMPLE_IN_LEN);
+
+ if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
+ (float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
+ delete out;
+ out = NULL;
+ }
+
+ return out;
+};
+
+/*
+ * Detect a burst based on correlation and peak-to-average ratio
+ *
+ * For one sampler-per-symbol, perform fast peak detection (no interpolation)
+ * for initial gating. We do this because energy detection should be disabled.
+ * For higher oversampling values, we assume the energy detector is in place
+ * and we run full interpolating peak detection.
+ */
+static int detectBurst(const signalVector &burst,
+ signalVector &corr, CorrelationSequence *sync,
+ float thresh, int sps, complex *amp, float *toa,
+ int start, int len)
+{
+ const signalVector *corr_in;
+ signalVector *dec = NULL;
+
+ if (sps == 4) {
+ dec = downsampleBurst(burst);
+ corr_in = dec;
+ sps = 1;
+ } else {
+ corr_in = &burst;
+ }
+
+ /* Correlate */
+ if (!convolve(corr_in, sync->sequence, &corr,
+ CUSTOM, start, len, 1, 0)) {
+ delete dec;
+ return -1;
+ }
+
+ delete dec;
+
+ /* Running at the downsampled rate at this point */
+ sps = 1;
+
+ /* Peak detection - place restrictions at correlation edges */
+ *amp = fastPeakDetect(corr, toa);
+
+ if ((*toa < 3 * sps) || (*toa > len - 3 * sps))
+ return 0;
+
+ /* Peak -to-average ratio */
+ if (computePeakRatio(&corr, sps, *toa, *amp) < thresh)
+ return 0;
+
+ /* Compute peak-to-average ratio. Reject if we don't have enough values */
+ *amp = peakDetect(corr, toa, NULL);
+
+ /* Normalize our channel gain */
+ *amp = *amp / sync->gain;
+
+ /* Compensate for residuate time lag */
+ *toa = *toa - sync->toa;
+
+ return 1;
+}
+
+static float maxAmplitude(const signalVector &burst)
+{
+ float max = 0.0;
+ for (size_t i = 0; i < burst.size(); i++) {
+ if (fabs(burst[i].real()) > max)
+ max = fabs(burst[i].real());
+ if (fabs(burst[i].imag()) > max)
+ max = fabs(burst[i].imag());
+ }
+
+ return max;
+}
+
+/*
+ * RACH/Normal burst detection with clipping detection
+ *
+ * Correlation window parameters:
+ * target: Tail bits + burst length
+ * head: Search symbols before target
+ * tail: Search symbols after target
+ */
+static int detectGeneralBurst(const signalVector &rxBurst,
+ float thresh,
+ int sps,
+ complex &amp,
+ float &toa,
+ int target, int head, int tail,
+ CorrelationSequence *sync)
+{
+ int rc, start, len;
+ bool clipping = false;
+
+ if ((sps != 1) && (sps != 4))
+ return -SIGERR_UNSUPPORTED;
+
+ // Detect potential clipping
+ // We still may be able to demod the burst, so we'll give it a try
+ // and only report clipping if we can't demod.
+ float maxAmpl = maxAmplitude(rxBurst);
+ if (maxAmpl > CLIP_THRESH) {
+ LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
+ clipping = true;
+ }
+
+ start = target - head - 1;
+ len = head + tail;
+ signalVector corr(len);
+
+ rc = detectBurst(rxBurst, corr, sync,
+ thresh, sps, &amp, &toa, start, len);
+ if (rc < 0) {
+ return -SIGERR_INTERNAL;
+ } else if (!rc) {
+ amp = 0.0f;
+ toa = 0.0f;
+ return clipping?-SIGERR_CLIP:SIGERR_NONE;
+ }
+
+ /* Subtract forward search bits from delay */
+ toa -= head;
+
+ return 1;
+}
+
+
+/*
+ * RACH burst detection
+ *
+ * Correlation window parameters:
+ * target: Tail bits + RACH length (reduced from 41 to a multiple of 4)
+ * head: Search 8 symbols before target
+ * tail: Search 8 symbols + maximum expected delay
+ */
+static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
+ complex &amplitude, float &toa, unsigned max_toa, bool ext)
+{
+ int rc, target, head, tail;
+ int i, num_seq;
+
+ target = 8 + 40;
+ head = 8;
+ tail = 8 + max_toa;
+ num_seq = ext ? 3 : 1;
+
+ for (i = 0; i < num_seq; i++) {
+ rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
+ target, head, tail, gRACHSequences[i]);
+ if (rc > 0)
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Normal burst detection
+ *
+ * Correlation window parameters:
+ * target: Tail + data + mid-midamble + 1/2 remaining midamblebits
+ * head: Search 6 symbols before target
+ * tail: Search 6 symbols + maximum expected delay
+ */
+static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold,
+ int sps, complex &amplitude, float &toa, unsigned max_toa)
+{
+ int rc, target, head, tail;
+ CorrelationSequence *sync;
+
+ if (tsc > 7)
+ return -SIGERR_UNSUPPORTED;
+
+ target = 3 + 58 + 16 + 5;
+ head = 6;
+ tail = 6 + max_toa;
+ sync = gMidambles[tsc];
+
+ rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
+ target, head, tail, sync);
+ return rc;
+}
+
+static int detectEdgeBurst(const signalVector &burst, unsigned tsc, float threshold,
+ int sps, complex &amplitude, float &toa, unsigned max_toa)
+{
+ int rc, target, head, tail;
+ CorrelationSequence *sync;
+
+ if (tsc > 7)
+ return -SIGERR_UNSUPPORTED;
+
+ target = 3 + 58 + 16 + 5;
+ head = 6;
+ tail = 6 + max_toa;
+ sync = gEdgeMidambles[tsc];
+
+ rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
+ target, head, tail, sync);
+ return rc;
+}
+
+int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
+ int sps, CorrType type, complex &amp, float &toa,
+ unsigned max_toa)
+{
+ int rc = 0;
+
+ switch (type) {
+ case EDGE:
+ rc = detectEdgeBurst(burst, tsc, threshold, sps,
+ amp, toa, max_toa);
+ if (rc > 0)
+ break;
+ else
+ type = TSC;
+ case TSC:
+ rc = analyzeTrafficBurst(burst, tsc, threshold, sps,
+ amp, toa, max_toa);
+ break;
+ case EXT_RACH:
+ case RACH:
+ rc = detectRACHBurst(burst, threshold, sps, amp, toa,
+ max_toa, type == EXT_RACH);
+ break;
+ default:
+ LOG(ERR) << "Invalid correlation type";
+ }
+
+ if (rc > 0)
+ return type;
+
+ return rc;
+}
+
+/*
+ * Soft 8-PSK decoding using Manhattan distance metric
+ */
+static SoftVector *softSliceEdgeBurst(signalVector &burst)
+{
+ size_t nsyms = 148;
+
+ if (burst.size() < nsyms)
+ return NULL;
+
+ signalVector::iterator itr;
+ SoftVector *bits = new SoftVector(nsyms * 3);
+
+ /*
+ * Bits 0 and 1 - First and second bits of the symbol respectively
+ */
+ rotateBurst2(burst, -M_PI / 8.0);
+ itr = burst.begin();
+ for (size_t i = 0; i < nsyms; i++) {
+ (*bits)[3 * i + 0] = -itr->imag();
+ (*bits)[3 * i + 1] = itr->real();
+ itr++;
+ }
+
+ /*
+ * Bit 2 - Collapse symbols into quadrant 0 (positive X and Y).
+ * Decision area is then simplified to X=Y axis. Rotate again to
+ * place decision boundary on X-axis.
+ */
+ itr = burst.begin();
+ for (size_t i = 0; i < burst.size(); i++) {
+ burst[i] = Complex<float>(fabs(itr->real()), fabs(itr->imag()));
+ itr++;
+ }
+
+ rotateBurst2(burst, -M_PI / 4.0);
+ itr = burst.begin();
+ for (size_t i = 0; i < nsyms; i++) {
+ (*bits)[3 * i + 2] = -itr->imag();
+ itr++;
+ }
+
+ signalVector soft(bits->size());
+ for (size_t i = 0; i < bits->size(); i++)
+ soft[i] = (*bits)[i];
+
+ return bits;
+}
+
+/*
+ * Convert signalVector to SoftVector by taking real part of the signal.
+ */
+static SoftVector *signalToSoftVector(signalVector *dec)
+{
+ SoftVector *bits = new SoftVector(dec->size());
+
+ SoftVector::iterator bit_itr = bits->begin();
+ signalVector::iterator burst_itr = dec->begin();
+
+ for (; burst_itr < dec->end(); burst_itr++)
+ *bit_itr++ = burst_itr->real();
+
+ return bits;
+}
+
+/*
+ * Shared portion of GMSK and EDGE demodulators consisting of timing
+ * recovery and single tap channel correction. For 4 SPS (if activated),
+ * the output is downsampled prior to the 1 SPS modulation specific
+ * stages.
+ */
+static signalVector *demodCommon(const signalVector &burst, int sps,
+ complex chan, float toa)
+{
+ signalVector *delay, *dec;
+
+ if ((sps != 1) && (sps != 4))
+ return NULL;
+
+ delay = delayVector(&burst, NULL, -toa * (float) sps);
+ scaleVector(*delay, (complex) 1.0 / chan);
+
+ if (sps == 1)
+ return delay;
+
+ dec = downsampleBurst(*delay);
+
+ delete delay;
+ return dec;
+}
+
+/*
+ * Demodulate GSMK burst. Prior to symbol rotation, operate at
+ * 4 SPS (if activated) to minimize distortion through the fractional
+ * delay filters. Symbol rotation and after always operates at 1 SPS.
+ */
+static SoftVector *demodGmskBurst(const signalVector &rxBurst,
+ int sps, complex channel, float TOA)
+{
+ SoftVector *bits;
+ signalVector *dec;
+
+ dec = demodCommon(rxBurst, sps, channel, TOA);
+ if (!dec)
+ return NULL;
+
+ /* Shift up by a quarter of a frequency */
+ GMSKReverseRotate(*dec, 1);
+ /* Take real part of the signal */
+ bits = signalToSoftVector(dec);
+ delete dec;
+
+ return bits;
+}
+
+/*
+ * Demodulate an 8-PSK burst. Prior to symbol rotation, operate at
+ * 4 SPS (if activated) to minimize distortion through the fractional
+ * delay filters. Symbol rotation and after always operates at 1 SPS.
+ *
+ * Allow 1 SPS demodulation here, but note that other parts of the
+ * transceiver restrict EDGE operatoin to 4 SPS - 8-PSK distortion
+ * through the fractional delay filters at 1 SPS renders signal
+ * nearly unrecoverable.
+ */
+static SoftVector *demodEdgeBurst(const signalVector &burst,
+ int sps, complex chan, float toa)
+{
+ SoftVector *bits;
+ signalVector *dec, *rot, *eq;
+
+ dec = demodCommon(burst, sps, chan, toa);
+ if (!dec)
+ return NULL;
+
+ /* Equalize and derotate */
+ eq = convolve(dec, GSMPulse4->c0_inv, NULL, NO_DELAY);
+ rot = derotateEdgeBurst(*eq, 1);
+
+ /* Soft slice and normalize */
+ bits = softSliceEdgeBurst(*rot);
+
+ delete dec;
+ delete eq;
+ delete rot;
+
+ return bits;
+}
+
+SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp,
+ float toa, CorrType type)
+{
+ if (type == EDGE)
+ return demodEdgeBurst(burst, sps, amp, toa);
+ else
+ return demodGmskBurst(burst, sps, amp, toa);
+}
+
+bool sigProcLibSetup()
+{
+ generateSincTable();
+ initGMSKRotationTables();
+
+ GSMPulse1 = generateGSMPulse(1);
+ GSMPulse4 = generateGSMPulse(4);
+
+ generateRACHSequence(&gRACHSequences[0], gRACHSynchSequenceTS0, 1);
+ generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
+ generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
+
+ for (int tsc = 0; tsc < 8; tsc++) {
+ generateMidamble(1, tsc);
+ gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
+ }
+
+ generateDelayFilters();
+
+ dnsampler = new Resampler(1, 4);
+ if (!dnsampler->init()) {
+ LOG(ALERT) << "Rx resampler failed to initialize";
+ goto fail;
+ }
+
+ return true;
+
+fail:
+ sigProcLibDestroy();
+ return false;
+}
diff --git a/Transceiver52M/sigProcLib.h b/Transceiver52M/sigProcLib.h
new file mode 100644
index 0000000..79a5c3f
--- /dev/null
+++ b/Transceiver52M/sigProcLib.h
@@ -0,0 +1,130 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* 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 SIGPROCLIB_H
+#define SIGPROCLIB_H
+
+#include "Vector.h"
+#include "Complex.h"
+#include "BitVector.h"
+#include "signalVector.h"
+
+/* Burst lengths */
+#define NORMAL_BURST_NBITS 148
+#define EDGE_BURST_NBITS 444
+#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
+
+/** Codes for burst types of received bursts*/
+enum CorrType{
+ OFF, ///< timeslot is off
+ TSC, ///< timeslot should contain a normal burst
+ EXT_RACH, ///< timeslot should contain an extended access burst
+ RACH, ///< timeslot should contain an access burst
+ EDGE, ///< timeslot should contain an EDGE burst
+ IDLE ///< timeslot is an idle (or dummy) burst
+};
+
+enum SignalError {
+ SIGERR_NONE,
+ SIGERR_BOUNDS,
+ SIGERR_CLIP,
+ SIGERR_UNSUPPORTED,
+ SIGERR_INTERNAL,
+};
+
+/*
+ * Burst detection threshold
+ *
+ * Decision threshold value for burst gating on peak-to-average value of
+ * correlated synchronization sequences. Lower values pass more bursts up
+ * to upper layers but will increase the false detection rate.
+ */
+#define BURST_THRESH 4.0
+
+/** Setup the signal processing library */
+bool sigProcLibSetup();
+
+/** Destroy the signal processing library */
+void sigProcLibDestroy(void);
+
+/** Operate soft slicer on a soft-bit vector */
+bool vectorSlicer(SoftVector *x);
+
+/** GMSK modulate a GSM burst of bits */
+signalVector *modulateBurst(const BitVector &wBurst,
+ int guardPeriodLength,
+ int sps, bool emptyPulse = false);
+
+/** 8-PSK modulate a burst of bits */
+signalVector *modulateEdgeBurst(const BitVector &bits,
+ int sps, bool emptyPulse = false);
+
+/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
+signalVector *generateEdgeBurst(int tsc);
+
+/** Generate an empty burst - 4 or 1 SPS */
+signalVector *generateEmptyBurst(int sps, int tn);
+
+/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
+signalVector *genRandNormalBurst(int tsc, int sps, int tn);
+
+/** Generate an access GSM burst with random payload - 4 or 1 SPS */
+signalVector *genRandAccessBurst(int delay, int sps, int tn);
+
+/** Generate a dummy GSM burst - 4 or 1 SPS */
+signalVector *generateDummyBurst(int sps, int tn);
+
+/**
+ Apply a scalar to a vector.
+ @param x The vector of interest.
+ @param scale The scalar.
+*/
+void scaleVector(signalVector &x,
+ complex scale);
+
+/**
+ Rough energy estimator.
+ @param rxBurst A GSM burst.
+ @param windowLength The number of burst samples used to compute burst energy
+ @return The average power of the received burst.
+*/
+float energyDetect(const signalVector &rxBurst,
+ unsigned windowLength);
+/**
+ 8-PSK/GMSK/RACH burst detector
+ @param burst The received GSM burst of interest
+ @param tsc Midamble type (0..7) also known as TSC
+ @param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
+ @param sps The number of samples per GSM symbol.
+ @param amplitude The estimated amplitude of received TSC burst.
+ @param toa The estimate time-of-arrival of received TSC burst (in symbols).
+ @param max_toa The maximum expected time-of-arrival (in symbols).
+ @return positive value (CorrType) if threshold value is reached,
+ negative value (-SignalError) on error,
+ zero (SIGERR_NONE) if no burst is detected
+*/
+int detectAnyBurst(const signalVector &burst,
+ unsigned tsc,
+ float threshold,
+ int sps,
+ CorrType type,
+ complex &amp,
+ float &toa,
+ unsigned max_toa);
+
+/** Demodulate burst basde on type and output soft bits */
+SoftVector *demodAnyBurst(const signalVector &burst, int sps,
+ complex amp, float toa, CorrType type);
+
+#endif /* SIGPROCLIB_H */
diff --git a/Transceiver52M/signalVector.cpp b/Transceiver52M/signalVector.cpp
new file mode 100644
index 0000000..fc8157e
--- /dev/null
+++ b/Transceiver52M/signalVector.cpp
@@ -0,0 +1,107 @@
+#include "signalVector.h"
+
+signalVector::signalVector(size_t size)
+ : Vector<complex>(size),
+ real(false), aligned(false), symmetry(NONE)
+{
+}
+
+signalVector::signalVector(size_t size, size_t start)
+ : Vector<complex>(size + start),
+ real(false), aligned(false), symmetry(NONE)
+{
+ mStart = mData + start;
+}
+
+signalVector::signalVector(complex *data, size_t start, size_t span)
+ : Vector<complex>(NULL, data + start, data + start + span),
+ real(false), aligned(false), symmetry(NONE)
+{
+}
+
+signalVector::signalVector(const signalVector &vector)
+ : Vector<complex>(vector.size() + vector.getStart()), aligned(false)
+{
+ mStart = mData + vector.getStart();
+ vector.copyTo(*this);
+ symmetry = vector.getSymmetry();
+ real = vector.isReal();
+};
+
+signalVector::signalVector(const signalVector &vector,
+ size_t start, size_t tail)
+ : Vector<complex>(start + vector.size() + tail), aligned(false)
+{
+ mStart = mData + start;
+ vector.copyTo(*this);
+ symmetry = vector.getSymmetry();
+ real = vector.isReal();
+};
+
+void signalVector::operator=(const signalVector& vector)
+{
+ resize(vector.size() + vector.getStart());
+
+ unsigned int i;
+ complex *dst = mData;
+ complex *src = vector.mData;
+ for (i = 0; i < size(); i++, src++, dst++)
+ *dst = *src;
+ /* TODO: optimize for non non-trivially copyable types: */
+ /*memcpy(mData, vector.mData, bytes()); */
+ mStart = mData + vector.getStart();
+}
+
+signalVector signalVector::segment(size_t start, size_t span)
+{
+ return signalVector(mData, start, span);
+}
+
+size_t signalVector::getStart() const
+{
+ return mStart - mData;
+}
+
+size_t signalVector::updateHistory()
+{
+ size_t num = getStart();
+ unsigned int i;
+ complex *dst = mData;
+ complex *src = mStart + this->size() - num;
+ for (i = 0; i < num; i++, src++, dst++)
+ *dst = *src;
+ /* TODO: optimize for non non-trivially copyable types: */
+ /*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */
+
+ return num;
+}
+
+Symmetry signalVector::getSymmetry() const
+{
+ return symmetry;
+}
+
+void signalVector::setSymmetry(Symmetry symmetry)
+{
+ this->symmetry = symmetry;
+}
+
+bool signalVector::isReal() const
+{
+ return real;
+}
+
+void signalVector::isReal(bool wOnly)
+{
+ real = wOnly;
+}
+
+bool signalVector::isAligned() const
+{
+ return aligned;
+}
+
+void signalVector::setAligned(bool aligned)
+{
+ this->aligned = aligned;
+}
diff --git a/Transceiver52M/signalVector.h b/Transceiver52M/signalVector.h
new file mode 100644
index 0000000..83f141e
--- /dev/null
+++ b/Transceiver52M/signalVector.h
@@ -0,0 +1,55 @@
+#ifndef _SIGNALVECTOR_H_
+#define _SIGNALVECTOR_H_
+
+#include <Vector.h>
+#include <Complex.h>
+
+/** Vector symmetry */
+enum Symmetry {
+ NONE = 0,
+ ABSSYM = 1
+};
+
+class signalVector: public Vector<complex> {
+public:
+ /** Default constructor */
+ signalVector(size_t size = 0);
+
+ /** Construct with head room */
+ signalVector(size_t size, size_t start);
+
+ /** Construct from existing buffer data (buffer not managed) */
+ signalVector(complex *data, size_t start, size_t span);
+
+ /** Construct by from existing vector */
+ signalVector(const signalVector &vector);
+
+ /** Construct by from existing vector and append head-tail room */
+ signalVector(const signalVector &vector, size_t start, size_t tail = 0);
+
+ /** Override base assignment operator to include start offsets */
+ void operator=(const signalVector& vector);
+
+ /** Return an alias to a segment of this signalVector. */
+ signalVector segment(size_t start, size_t span);
+
+ /** Return head room */
+ size_t getStart() const;
+ size_t updateHistory();
+
+ Symmetry getSymmetry() const;
+ void setSymmetry(Symmetry symmetry);
+
+ bool isReal() const;
+ void isReal(bool real);
+
+ bool isAligned() const;
+ void setAligned(bool aligned);
+
+private:
+ bool real;
+ bool aligned;
+ Symmetry symmetry;
+};
+
+#endif /* _SIGNALVECTOR_H_ */
diff --git a/Transceiver52M/std_inband.rbf b/Transceiver52M/std_inband.rbf
new file mode 100755
index 0000000..63842b7
--- /dev/null
+++ b/Transceiver52M/std_inband.rbf
Binary files differ
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..b2ed160
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,1578 @@
+#!/bin/sh
+# a u t o g e n . s h
+#
+# Copyright (c) 2005-2009 United States Government as represented by
+# the U.S. Army Research Laboratory.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+###
+#
+# Script for automatically preparing the sources for compilation by
+# performing the myrid of necessary steps. The script attempts to
+# detect proper version support, and outputs warnings about particular
+# systems that have autotool peculiarities.
+#
+# Basically, if everything is set up and installed correctly, the
+# script will validate that minimum versions of the GNU Build System
+# tools are installed, account for several common configuration
+# issues, and then simply run autoreconf for you.
+#
+# If autoreconf fails, which can happen for many valid configurations,
+# this script proceeds to run manual preparation steps effectively
+# providing a POSIX shell script (mostly complete) reimplementation of
+# autoreconf.
+#
+# The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER
+# environment variables and corresponding _OPTIONS variables (e.g.
+# AUTORECONF_OPTIONS) may be used to override the default automatic
+# detection behaviors. Similarly the _VERSION variables will override
+# the minimum required version numbers.
+#
+# Examples:
+#
+# To obtain help on usage:
+# ./autogen.sh --help
+#
+# To obtain verbose output:
+# ./autogen.sh --verbose
+#
+# To skip autoreconf and prepare manually:
+# AUTORECONF=false ./autogen.sh
+#
+# To verbosely try running with an older (unsupported) autoconf:
+# AUTOCONF_VERSION=2.50 ./autogen.sh --verbose
+#
+# Author:
+# Christopher Sean Morrison <morrison@brlcad.org>
+#
+# Patches:
+# Sebastian Pipping <sebastian@pipping.org>
+#
+######################################################################
+
+# set to minimum acceptible version of autoconf
+if [ "x$AUTOCONF_VERSION" = "x" ] ; then
+ AUTOCONF_VERSION=2.52
+fi
+# set to minimum acceptible version of automake
+if [ "x$AUTOMAKE_VERSION" = "x" ] ; then
+ AUTOMAKE_VERSION=1.6.0
+fi
+# set to minimum acceptible version of libtool
+if [ "x$LIBTOOL_VERSION" = "x" ] ; then
+ LIBTOOL_VERSION=1.4.2
+fi
+
+
+##################
+# ident function #
+##################
+ident ( ) {
+ # extract copyright from header
+ __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`"
+ if [ "x$__copyright" = "x" ] ; then
+ __copyright="`date +%Y`"
+ fi
+
+ # extract version from CVS Id string
+ __id="$Id: autogen.sh 33925 2009-03-01 23:27:06Z brlcad $"
+ __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`"
+ if [ "x$__version" = "x" ] ; then
+ __version=""
+ fi
+
+ echo "autogen.sh build preparation script by Christopher Sean Morrison"
+ echo " + config.guess download patch by Sebastian Pipping (2008-12-03)"
+ echo "revised 3-clause BSD-style license, copyright (c) $__copyright"
+ echo "script version $__version, ISO/IEC 9945 POSIX shell script"
+}
+
+
+##################
+# USAGE FUNCTION #
+##################
+usage ( ) {
+ echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]"
+ echo " --help Help on $NAME_OF_AUTOGEN usage"
+ echo " --verbose Verbose progress output"
+ echo " --quiet Quiet suppressed progress output"
+ echo " --download Download the latest config.guess from gnulib"
+ echo " --version Only perform GNU Build System version checks"
+ echo
+ echo "Description: This script will validate that minimum versions of the"
+ echo "GNU Build System tools are installed and then run autoreconf for you."
+ echo "Should autoreconf fail, manual preparation steps will be run"
+ echo "potentially accounting for several common preparation issues. The"
+
+ echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER,"
+ echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS"
+ echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the"
+ echo "default automatic detection behavior."
+ echo
+
+ ident
+
+ return 0
+}
+
+
+##########################
+# VERSION_ERROR FUNCTION #
+##########################
+version_error ( ) {
+ if [ "x$1" = "x" ] ; then
+ echo "INTERNAL ERROR: version_error was not provided a version"
+ exit 1
+ fi
+ if [ "x$2" = "x" ] ; then
+ echo "INTERNAL ERROR: version_error was not provided an application name"
+ exit 1
+ fi
+ $ECHO
+ $ECHO "ERROR: To prepare the ${PROJECT} build system from scratch,"
+ $ECHO " at least version $1 of $2 must be installed."
+ $ECHO
+ $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will"
+ $ECHO "run configure or make. Either the GNU Autotools will need to be installed"
+ $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source"
+ $ECHO "code on another system and then transferred to here. -- Cheers!"
+ $ECHO
+}
+
+##########################
+# VERSION_CHECK FUNCTION #
+##########################
+version_check ( ) {
+ if [ "x$1" = "x" ] ; then
+ echo "INTERNAL ERROR: version_check was not provided a minimum version"
+ exit 1
+ fi
+ _min="$1"
+ if [ "x$2" = "x" ] ; then
+ echo "INTERNAL ERROR: version check was not provided a comparison version"
+ exit 1
+ fi
+ _cur="$2"
+
+ # needed to handle versions like 1.10 and 1.4-p6
+ _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+ _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+
+ _min_major="`echo $_min | cut -d. -f1`"
+ _min_minor="`echo $_min | cut -d. -f2`"
+ _min_patch="`echo $_min | cut -d. -f3`"
+
+ _cur_major="`echo $_cur | cut -d. -f1`"
+ _cur_minor="`echo $_cur | cut -d. -f2`"
+ _cur_patch="`echo $_cur | cut -d. -f3`"
+
+ if [ "x$_min_major" = "x" ] ; then
+ _min_major=0
+ fi
+ if [ "x$_min_minor" = "x" ] ; then
+ _min_minor=0
+ fi
+ if [ "x$_min_patch" = "x" ] ; then
+ _min_patch=0
+ fi
+ if [ "x$_cur_minor" = "x" ] ; then
+ _cur_major=0
+ fi
+ if [ "x$_cur_minor" = "x" ] ; then
+ _cur_minor=0
+ fi
+ if [ "x$_cur_patch" = "x" ] ; then
+ _cur_patch=0
+ fi
+
+ $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}"
+
+ if [ $_min_major -lt $_cur_major ] ; then
+ return 0
+ elif [ $_min_major -eq $_cur_major ] ; then
+ if [ $_min_minor -lt $_cur_minor ] ; then
+ return 0
+ elif [ $_min_minor -eq $_cur_minor ] ; then
+ if [ $_min_patch -lt $_cur_patch ] ; then
+ return 0
+ elif [ $_min_patch -eq $_cur_patch ] ; then
+ return 0
+ fi
+ fi
+ fi
+ return 1
+}
+
+
+######################################
+# LOCATE_CONFIGURE_TEMPLATE FUNCTION #
+######################################
+locate_configure_template ( ) {
+ _pwd="`pwd`"
+ if test -f "./configure.ac" ; then
+ echo "./configure.ac"
+ elif test -f "./configure.in" ; then
+ echo "./configure.in"
+ elif test -f "$_pwd/configure.ac" ; then
+ echo "$_pwd/configure.ac"
+ elif test -f "$_pwd/configure.in" ; then
+ echo "$_pwd/configure.in"
+ elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then
+ echo "$PATH_TO_AUTOGEN/configure.ac"
+ elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then
+ echo "$PATH_TO_AUTOGEN/configure.in"
+ fi
+}
+
+
+##################
+# argument check #
+##################
+ARGS="$*"
+PATH_TO_AUTOGEN="`dirname $0`"
+NAME_OF_AUTOGEN="`basename $0`"
+AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN"
+
+LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4"
+
+if [ "x$HELP" = "x" ] ; then
+ HELP=no
+fi
+if [ "x$QUIET" = "x" ] ; then
+ QUIET=no
+fi
+if [ "x$VERBOSE" = "x" ] ; then
+ VERBOSE=no
+fi
+if [ "x$VERSION_ONLY" = "x" ] ; then
+ VERSION_ONLY=no
+fi
+if [ "x$DOWNLOAD" = "x" ] ; then
+ DOWNLOAD=no
+fi
+if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then
+ AUTORECONF_OPTIONS="-i -f"
+fi
+if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then
+ AUTOCONF_OPTIONS="-f"
+fi
+if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then
+ AUTOMAKE_OPTIONS="-a -c -f"
+fi
+ALT_AUTOMAKE_OPTIONS="-a -c"
+if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then
+ LIBTOOLIZE_OPTIONS="--automake -c -f"
+fi
+ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force"
+if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then
+ ACLOCAL_OPTIONS=""
+fi
+if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then
+ AUTOHEADER_OPTIONS=""
+fi
+if [ "x$CONFIG_GUESS_URL" = "x" ] ; then
+ CONFIG_GUESS_URL="http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD"
+fi
+for arg in $ARGS ; do
+ case "x$arg" in
+ x--help) HELP=yes ;;
+ x-[hH]) HELP=yes ;;
+ x--quiet) QUIET=yes ;;
+ x-[qQ]) QUIET=yes ;;
+ x--verbose) VERBOSE=yes ;;
+ x-[dD]) DOWNLOAD=yes ;;
+ x--download) DOWNLOAD=yes ;;
+ x-[vV]) VERBOSE=yes ;;
+ x--version) VERSION_ONLY=yes ;;
+ *)
+ echo "Unknown option: $arg"
+ echo
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+
+#####################
+# environment check #
+#####################
+
+# sanity check before recursions potentially begin
+if [ ! -f "$AUTOGEN_SH" ] ; then
+ echo "INTERNAL ERROR: $AUTOGEN_SH does not exist"
+ if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then
+ echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH"
+ fi
+ exit 1
+fi
+
+# force locale setting to C so things like date output as expected
+LC_ALL=C
+
+# commands that this script expects
+for __cmd in echo head tail pwd ; do
+ echo "test" | $__cmd > /dev/null 2>&1
+ if [ $? != 0 ] ; then
+ echo "INTERNAL ERROR: '${__cmd}' command is required"
+ exit 2
+ fi
+done
+echo "test" | grep "test" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+ echo "INTERNAL ERROR: grep command is required"
+ exit 1
+fi
+echo "test" | sed "s/test/test/" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+ echo "INTERNAL ERROR: sed command is required"
+ exit 1
+fi
+
+
+# determine the behavior of echo
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+# determine the behavior of head
+case "x`echo 'head' | head -n 1 2>&1`" in
+ *xhead*) HEAD_N="n " ;;
+ *) HEAD_N="" ;;
+esac
+
+# determine the behavior of tail
+case "x`echo 'tail' | tail -n 1 2>&1`" in
+ *xtail*) TAIL_N="n " ;;
+ *) TAIL_N="" ;;
+esac
+
+VERBOSE_ECHO=:
+ECHO=:
+if [ "x$QUIET" = "xyes" ] ; then
+ if [ "x$VERBOSE" = "xyes" ] ; then
+ echo "Verbose output quelled by quiet option. Further output disabled."
+ fi
+else
+ ECHO=echo
+ if [ "x$VERBOSE" = "xyes" ] ; then
+ echo "Verbose output enabled"
+ VERBOSE_ECHO=echo
+ fi
+fi
+
+
+# allow a recursive run to disable further recursions
+if [ "x$RUN_RECURSIVE" = "x" ] ; then
+ RUN_RECURSIVE=yes
+fi
+
+
+################################################
+# check for help arg and bypass version checks #
+################################################
+if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then
+ HELP=yes
+fi
+if [ "x$HELP" = "xyes" ] ; then
+ usage
+ $ECHO "---"
+ $ECHO "Help was requested. No preparation or configuration will be performed."
+ exit 0
+fi
+
+
+#######################
+# set up signal traps #
+#######################
+untrap_abnormal ( ) {
+ for sig in 1 2 13 15; do
+ trap - $sig
+ done
+}
+
+# do this cleanup whenever we exit.
+trap '
+ # start from the root
+ if test -d "$START_PATH" ; then
+ cd "$START_PATH"
+ fi
+
+ # restore/delete backup files
+ if test "x$PFC_INIT" = "x1" ; then
+ recursive_restore
+ fi
+' 0
+
+# trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15)
+for sig in 1 2 13 15; do
+ trap '
+ $ECHO ""
+ $ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'"
+
+ # start from the root
+ if test -d "$START_PATH" ; then
+ cd "$START_PATH"
+ fi
+
+ # clean up on abnormal exit
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+
+ if test -f "acinclude.m4.$$.backup" ; then
+ $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4"
+ chmod u+w acinclude.m4
+ cat acinclude.m4.$$.backup > acinclude.m4
+
+ $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup"
+ rm -f acinclude.m4.$$.backup
+ fi
+
+ { (exit 1); exit 1; }
+' $sig
+done
+
+
+#############################
+# look for a configure file #
+#############################
+if [ "x$CONFIGURE" = "x" ] ; then
+ CONFIGURE="`locate_configure_template`"
+ if [ ! "x$CONFIGURE" = "x" ] ; then
+ $VERBOSE_ECHO "Found a configure template: $CONFIGURE"
+ fi
+else
+ $ECHO "Using CONFIGURE environment variable override: $CONFIGURE"
+fi
+if [ "x$CONFIGURE" = "x" ] ; then
+ if [ "x$VERSION_ONLY" = "xyes" ] ; then
+ CONFIGURE=/dev/null
+ else
+ $ECHO
+ $ECHO "A configure.ac or configure.in file could not be located implying"
+ $ECHO "that the GNU Build System is at least not used in this directory. In"
+ $ECHO "any case, there is nothing to do here without one of those files."
+ $ECHO
+ $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+ exit 1
+ fi
+fi
+
+####################
+# get project name #
+####################
+if [ "x$PROJECT" = "x" ] ; then
+ PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if [ "x$PROJECT" = "xAC_INIT" ] ; then
+ # projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead
+ PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ ]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ fi
+ if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then
+ PROJECT="project"
+ fi
+ if [ "x$PROJECT" = "x" ] ; then
+ PROJECT="project"
+ fi
+else
+ $ECHO "Using PROJECT environment variable override: $PROJECT"
+fi
+$ECHO "Preparing the $PROJECT build system...please wait"
+$ECHO
+
+
+########################
+# check for autoreconf #
+########################
+HAVE_AUTORECONF=no
+if [ "x$AUTORECONF" = "x" ] ; then
+ for AUTORECONF in autoreconf ; do
+ $VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version"
+ $AUTORECONF --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ HAVE_AUTORECONF=yes
+ break
+ fi
+ done
+else
+ HAVE_AUTORECONF=yes
+ $ECHO "Using AUTORECONF environment variable override: $AUTORECONF"
+fi
+
+
+##########################
+# autoconf version check #
+##########################
+_acfound=no
+if [ "x$AUTOCONF" = "x" ] ; then
+ for AUTOCONF in autoconf ; do
+ $VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version"
+ $AUTOCONF --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ _acfound=yes
+ break
+ fi
+ done
+else
+ _acfound=yes
+ $ECHO "Using AUTOCONF environment variable override: $AUTOCONF"
+fi
+
+_report_error=no
+if [ ! "x$_acfound" = "xyes" ] ; then
+ $ECHO "ERROR: Unable to locate GNU Autoconf."
+ _report_error=yes
+else
+ _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Autoconf version $_version"
+ version_check "$AUTOCONF_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$AUTOCONF_VERSION" "GNU Autoconf"
+ exit 1
+fi
+
+
+##########################
+# automake version check #
+##########################
+_amfound=no
+if [ "x$AUTOMAKE" = "x" ] ; then
+ for AUTOMAKE in automake ; do
+ $VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version"
+ $AUTOMAKE --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ _amfound=yes
+ break
+ fi
+ done
+else
+ _amfound=yes
+ $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE"
+fi
+
+
+_report_error=no
+if [ ! "x$_amfound" = "xyes" ] ; then
+ $ECHO
+ $ECHO "ERROR: Unable to locate GNU Automake."
+ _report_error=yes
+else
+ _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Automake version $_version"
+ version_check "$AUTOMAKE_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$AUTOMAKE_VERSION" "GNU Automake"
+ exit 1
+fi
+
+
+########################
+# check for libtoolize #
+########################
+HAVE_LIBTOOLIZE=yes
+HAVE_ALT_LIBTOOLIZE=no
+_ltfound=no
+if [ "x$LIBTOOLIZE" = "x" ] ; then
+ LIBTOOLIZE=libtoolize
+ $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version"
+ $LIBTOOLIZE --version > /dev/null 2>&1
+ if [ ! $? = 0 ] ; then
+ HAVE_LIBTOOLIZE=no
+ $ECHO
+ if [ "x$HAVE_AUTORECONF" = "xno" ] ; then
+ $ECHO "Warning: libtoolize does not appear to be available."
+ else
+ $ECHO "Warning: libtoolize does not appear to be available. This means that"
+ $ECHO "the automatic build preparation via autoreconf will probably not work."
+ $ECHO "Preparing the build by running each step individually, however, should"
+ $ECHO "work and will be done automatically for you if autoreconf fails."
+ fi
+
+ # look for some alternates
+ for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do
+ $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version"
+ _glibtoolize="`$tool --version > /dev/null 2>&1`"
+ if [ $? = 0 ] ; then
+ $VERBOSE_ECHO "Found $tool --version"
+ _glti="`which $tool`"
+ if [ "x$_glti" = "x" ] ; then
+ $VERBOSE_ECHO "Cannot find $tool with which"
+ continue;
+ fi
+ if test ! -f "$_glti" ; then
+ $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file"
+ continue;
+ fi
+ _gltidir="`dirname $_glti`"
+ if [ "x$_gltidir" = "x" ] ; then
+ $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti"
+ continue;
+ fi
+ if test ! -d "$_gltidir" ; then
+ $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory"
+ continue;
+ fi
+ HAVE_ALT_LIBTOOLIZE=yes
+ LIBTOOLIZE="$tool"
+ $ECHO
+ $ECHO "Fortunately, $tool was found which means that your system may simply"
+ $ECHO "have a non-standard or incomplete GNU Autotools install. If you have"
+ $ECHO "sufficient system access, it may be possible to quell this warning by"
+ $ECHO "running:"
+ $ECHO
+ sudo -V > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ $ECHO " sudo ln -s $_glti $_gltidir/libtoolize"
+ $ECHO
+ else
+ $ECHO " ln -s $_glti $_gltidir/libtoolize"
+ $ECHO
+ $ECHO "Run that as root or with proper permissions to the $_gltidir directory"
+ $ECHO
+ fi
+ _ltfound=yes
+ break
+ fi
+ done
+ else
+ _ltfound=yes
+ fi
+else
+ _ltfound=yes
+ $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE"
+fi
+
+
+############################
+# libtoolize version check #
+############################
+_report_error=no
+if [ ! "x$_ltfound" = "xyes" ] ; then
+ $ECHO
+ $ECHO "ERROR: Unable to locate GNU Libtool."
+ _report_error=yes
+else
+ _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Libtool version $_version"
+ version_check "$LIBTOOL_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$LIBTOOL_VERSION" "GNU Libtool"
+ exit 1
+fi
+
+
+#####################
+# check for aclocal #
+#####################
+if [ "x$ACLOCAL" = "x" ] ; then
+ for ACLOCAL in aclocal ; do
+ $VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version"
+ $ACLOCAL --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ break
+ fi
+ done
+else
+ $ECHO "Using ACLOCAL environment variable override: $ACLOCAL"
+fi
+
+
+########################
+# check for autoheader #
+########################
+if [ "x$AUTOHEADER" = "x" ] ; then
+ for AUTOHEADER in autoheader ; do
+ $VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version"
+ $AUTOHEADER --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ break
+ fi
+ done
+else
+ $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER"
+fi
+
+
+#########################
+# check if version only #
+#########################
+$VERBOSE_ECHO "Checking whether to only output version information"
+if [ "x$VERSION_ONLY" = "xyes" ] ; then
+ $ECHO
+ ident
+ $ECHO "---"
+ $ECHO "Version requested. No preparation or configuration will be performed."
+ exit 0
+fi
+
+
+#################################
+# PROTECT_FROM_CLOBBER FUNCTION #
+#################################
+protect_from_clobber ( ) {
+ PFC_INIT=1
+
+ # protect COPYING & INSTALL from overwrite by automake. the
+ # automake force option will (inappropriately) ignore the existing
+ # contents of a COPYING and/or INSTALL files (depending on the
+ # version) instead of just forcing *missing* files like it does
+ # for AUTHORS, NEWS, and README. this is broken but extremely
+ # prevalent behavior, so we protect against it by keeping a backup
+ # of the file that can later be restored.
+
+ for file in COPYING INSTALL ; do
+ if test -f ${file} ; then
+ if test -f ${file}.$$.protect_from_automake.backup ; then
+ $VERBOSE_ECHO "Already backed up ${file} in `pwd`"
+ else
+ $VERBOSE_ECHO "Backing up ${file} in `pwd`"
+ $VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup"
+ cp -p ${file} ${file}.$$.protect_from_automake.backup
+ fi
+ fi
+ done
+}
+
+
+##############################
+# RECURSIVE_PROTECT FUNCTION #
+##############################
+recursive_protect ( ) {
+
+ # for projects using recursive configure, run the build
+ # preparation steps for the subdirectories. this function assumes
+ # START_PATH was set to pwd before recursion begins so that
+ # relative paths work.
+
+ # git 'r done, protect COPYING and INSTALL from being clobbered
+ protect_from_clobber
+
+ if test -d autom4te.cache ; then
+ $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+ fi
+
+ # find configure template
+ _configure="`locate_configure_template`"
+ if [ "x$_configure" = "x" ] ; then
+ return
+ fi
+ # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure"
+
+ # look for subdirs
+ # $VERBOSE_ECHO "Looking for subdirs in `pwd`"
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ CHECK_DIRS=""
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+ fi
+ done
+
+ # process subdirs
+ if [ ! "x$CHECK_DIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively scanning the following directories:"
+ $VERBOSE_ECHO " $CHECK_DIRS"
+ for dir in $CHECK_DIRS ; do
+ $VERBOSE_ECHO "Protecting files from automake in $dir"
+ cd "$START_PATH"
+ eval "cd $dir"
+
+ # recursively git 'r done
+ recursive_protect
+ done
+ fi
+} # end of recursive_protect
+
+
+#############################
+# RESTORE_CLOBBERED FUNCION #
+#############################
+restore_clobbered ( ) {
+
+ # The automake (and autoreconf by extension) -f/--force-missing
+ # option may overwrite COPYING and INSTALL even if they do exist.
+ # Here we restore the files if necessary.
+
+ spacer=no
+
+ for file in COPYING INSTALL ; do
+ if test -f ${file}.$$.protect_from_automake.backup ; then
+ if test -f ${file} ; then
+ # compare entire content, restore if needed
+ if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then
+ if test "x$spacer" = "xno" ; then
+ $VERBOSE_ECHO
+ spacer=yes
+ fi
+ # restore the backup
+ $VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)"
+ $VERBOSE_ECHO "rm -f ${file}"
+ rm -f ${file}
+ $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+ mv ${file}.$$.protect_from_automake.backup ${file}
+ fi # check contents
+ elif test -f ${file}.$$.protect_from_automake.backup ; then
+ $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+ mv ${file}.$$.protect_from_automake.backup ${file}
+ fi # -f ${file}
+
+ # just in case
+ $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup"
+ rm -f ${file}.$$.protect_from_automake.backup
+ fi # -f ${file}.$$.protect_from_automake.backup
+ done
+
+ CONFIGURE="`locate_configure_template`"
+ if [ "x$CONFIGURE" = "x" ] ; then
+ return
+ fi
+
+ _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if test ! -d "$_aux_dir" ; then
+ _aux_dir=.
+ fi
+
+ for file in config.guess config.sub ltmain.sh ; do
+ if test -f "${_aux_dir}/${file}" ; then
+ $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\""
+ rm -f "${_aux_dir}/${file}.backup"
+ fi
+ done
+} # end of restore_clobbered
+
+
+##############################
+# RECURSIVE_RESTORE FUNCTION #
+##############################
+recursive_restore ( ) {
+
+ # restore COPYING and INSTALL from backup if they were clobbered
+ # for each directory recursively.
+
+ # git 'r undone
+ restore_clobbered
+
+ # find configure template
+ _configure="`locate_configure_template`"
+ if [ "x$_configure" = "x" ] ; then
+ return
+ fi
+
+ # look for subdirs
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ CHECK_DIRS=""
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+ fi
+ done
+
+ # process subdirs
+ if [ ! "x$CHECK_DIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively scanning the following directories:"
+ $VERBOSE_ECHO " $CHECK_DIRS"
+ for dir in $CHECK_DIRS ; do
+ $VERBOSE_ECHO "Checking files for automake damage in $dir"
+ cd "$START_PATH"
+ eval "cd $dir"
+
+ # recursively git 'r undone
+ recursive_restore
+ done
+ fi
+} # end of recursive_restore
+
+
+#######################
+# INITIALIZE FUNCTION #
+#######################
+initialize ( ) {
+
+ # this routine performs a variety of directory-specific
+ # initializations. some are sanity checks, some are preventive,
+ # and some are necessary setup detection.
+ #
+ # this function sets:
+ # CONFIGURE
+ # SEARCH_DIRS
+ # CONFIG_SUBDIRS
+
+ ##################################
+ # check for a configure template #
+ ##################################
+ CONFIGURE="`locate_configure_template`"
+ if [ "x$CONFIGURE" = "x" ] ; then
+ $ECHO
+ $ECHO "A configure.ac or configure.in file could not be located implying"
+ $ECHO "that the GNU Build System is at least not used in this directory. In"
+ $ECHO "any case, there is nothing to do here without one of those files."
+ $ECHO
+ $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+ exit 1
+ fi
+
+ #####################
+ # detect an aux dir #
+ #####################
+ _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if test ! -d "$_aux_dir" ; then
+ _aux_dir=.
+ else
+ $VERBOSE_ECHO "Detected auxillary directory: $_aux_dir"
+ fi
+
+ ################################
+ # detect a recursive configure #
+ ################################
+ CONFIG_SUBDIRS=""
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir"
+ CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir"
+ fi
+ done
+
+ ###########################################################
+ # make sure certain required files exist for GNU projects #
+ ###########################################################
+ _marker_found=""
+ _marker_found_message_intro='Detected non-GNU marker "'
+ _marker_found_message_mid='" in '
+ for marker in foreign cygnus ; do
+ _marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid}
+ _marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`"
+ if [ ! "x$_marker_found" = "x" ] ; then
+ $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`"
+ break
+ fi
+ if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+ _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`"
+ if [ ! "x$_marker_found" = "x" ] ; then
+ $VERBOSE_ECHO "${_marker_found_message}Makefile.am"
+ break
+ fi
+ fi
+ done
+ if [ "x${_marker_found}" = "x" ] ; then
+ _suggest_foreign=no
+ for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do
+ if [ ! -f $file ] ; then
+ $VERBOSE_ECHO "Touching ${file} since it does not exist"
+ _suggest_foreign=yes
+ touch $file
+ fi
+ done
+
+ if [ "x${_suggest_foreign}" = "xyes" ] ; then
+ $ECHO
+ $ECHO "Warning: Several files expected of projects that conform to the GNU"
+ $ECHO "coding standards were not found. The files were automatically added"
+ $ECHO "for you since you do not have a 'foreign' declaration specified."
+ $ECHO
+ $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`"
+ if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+ $ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file."
+ fi
+ $ECHO
+ fi
+ fi
+
+ ##################################################
+ # make sure certain generated files do not exist #
+ ##################################################
+ for file in config.guess config.sub ltmain.sh ; do
+ if test -f "${_aux_dir}/${file}" ; then
+ $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\""
+ mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup"
+ fi
+ done
+
+ ############################
+ # search alternate m4 dirs #
+ ############################
+ SEARCH_DIRS=""
+ for dir in m4 ; do
+ if [ -d $dir ] ; then
+ $VERBOSE_ECHO "Found extra aclocal search directory: $dir"
+ SEARCH_DIRS="$SEARCH_DIRS -I $dir"
+ fi
+ done
+
+ ######################################
+ # remove any previous build products #
+ ######################################
+ if test -d autom4te.cache ; then
+ $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+ fi
+# tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it
+# if test -f aclocal.m4 ; then
+# $VERBOSE_ECHO "Found an aclocal.m4 file, deleting it"
+# $VERBOSE_ECHO "rm -f aclocal.m4"
+# rm -f aclocal.m4
+# fi
+
+} # end of initialize()
+
+
+##############
+# initialize #
+##############
+
+# stash path
+START_PATH="`pwd`"
+
+# Before running autoreconf or manual steps, some prep detection work
+# is necessary or useful. Only needs to occur once per directory, but
+# does need to traverse the entire subconfigure hierarchy to protect
+# files from being clobbered even by autoreconf.
+recursive_protect
+
+# start from where we started
+cd "$START_PATH"
+
+# get ready to process
+initialize
+
+
+#########################################
+# DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION #
+#########################################
+
+# TODO - should make sure wget/curl exist and/or work before trying to
+# use them.
+
+download_gnulib_config_guess () {
+ # abuse gitweb to download gnulib's latest config.guess via HTTP
+ config_guess_temp="config.guess.$$.download"
+ ret=1
+ for __cmd in wget curl fetch ; do
+ $VERBOSE_ECHO "Checking for command ${__cmd}"
+ ${__cmd} --version > /dev/null 2>&1
+ ret=$?
+ if [ ! $ret = 0 ] ; then
+ continue
+ fi
+
+ __cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'`
+ $VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}"
+
+ opts=""
+ case ${__cmd} in
+ wget)
+ opts="-O"
+ ;;
+ curl)
+ opts="-o"
+ ;;
+ fetch)
+ opts="-t 5 -f"
+ ;;
+ esac
+
+ $VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\""
+ eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ mv -f "${config_guess_temp}" ${_aux_dir}/config.guess
+ ret=0
+ break
+ fi
+ done
+
+ if [ ! $ret = 0 ] ; then
+ $ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL"
+ rm -f "${config_guess_temp}"
+ fi
+}
+
+
+##############################
+# LIBTOOLIZE_NEEDED FUNCTION #
+##############################
+libtoolize_needed () {
+ ret=1 # means no, don't need libtoolize
+ for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ ret=0 # means yes, need to run libtoolize
+ break
+ fi
+ done
+ return ${ret}
+}
+
+
+
+############################################
+# prepare build via autoreconf or manually #
+############################################
+reconfigure_manually=no
+if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then
+ $ECHO
+ $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C"
+
+ $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS"
+ autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoreconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+ if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then
+ $ECHO
+ $ECHO "Warning: autoreconf failed but due to what is usually a common libtool"
+ $ECHO "misconfiguration issue. This problem is encountered on systems that"
+ $ECHO "have installed libtoolize under a different name without providing a"
+ $ECHO "symbolic link or without setting the LIBTOOLIZE environment variable."
+ $ECHO
+ $ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE"
+
+ export LIBTOOLIZE
+ RUN_RECURSIVE=no
+ export RUN_RECURSIVE
+ untrap_abnormal
+
+ $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ exit $?
+ fi
+ fi
+
+ $ECHO "Warning: $AUTORECONF failed"
+
+ if test -f ltmain.sh ; then
+ $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should"
+ fi
+
+ $ECHO "Attempting to run the preparation steps individually"
+ reconfigure_manually=yes
+ else
+ if [ "x$DOWNLOAD" = "xyes" ] ; then
+ if libtoolize_needed ; then
+ download_gnulib_config_guess
+ fi
+ fi
+ fi
+else
+ reconfigure_manually=yes
+fi
+
+
+############################
+# LIBTOOL_FAILURE FUNCTION #
+############################
+libtool_failure ( ) {
+
+ # libtool is rather error-prone in comparison to the other
+ # autotools and this routine attempts to compensate for some
+ # common failures. the output after a libtoolize failure is
+ # parsed for an error related to AC_PROG_LIBTOOL and if found, we
+ # attempt to inject a project-provided libtool.m4 file.
+
+ _autoconf_output="$1"
+
+ if [ "x$RUN_RECURSIVE" = "xno" ] ; then
+ # we already tried the libtool.m4, don't try again
+ return 1
+ fi
+
+ if test -f "$LIBTOOL_M4" ; then
+ found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`"
+ if test ! "x$found_libtool" = "x" ; then
+ if test -f acinclude.m4 ; then
+ rm -f acinclude.m4.$$.backup
+ $VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup"
+ cat acinclude.m4 > acinclude.m4.$$.backup
+ fi
+ $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4"
+ chmod u+w acinclude.m4
+ cat "$LIBTOOL_M4" >> acinclude.m4
+
+ # don't keep doing this
+ RUN_RECURSIVE=no
+ export RUN_RECURSIVE
+ untrap_abnormal
+
+ $ECHO
+ $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4"
+ $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ exit $?
+ fi
+ fi
+}
+
+
+###########################
+# MANUAL_AUTOGEN FUNCTION #
+###########################
+manual_autogen ( ) {
+
+ ##################################################
+ # Manual preparation steps taken are as follows: #
+ # aclocal [-I m4] #
+ # libtoolize --automake -c -f #
+ # aclocal [-I m4] #
+ # autoconf -f #
+ # autoheader #
+ # automake -a -c -f #
+ ##################################################
+
+ ###########
+ # aclocal #
+ ###########
+ $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+ aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$aclocal_output"
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi
+
+ ##############
+ # libtoolize #
+ ##############
+ if libtoolize_needed ; then
+ if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then
+ $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS"
+ libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$libtoolize_output"
+
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+ else
+ if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+ $VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS"
+ libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$libtoolize_output"
+
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+ fi
+ fi
+
+ ###########
+ # aclocal #
+ ###########
+ # re-run again as instructed by libtoolize
+ $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+ aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$aclocal_output"
+
+ # libtoolize might put ltmain.sh in the wrong place
+ if test -f ltmain.sh ; then
+ if test ! -f "${_aux_dir}/ltmain.sh" ; then
+ $ECHO
+ $ECHO "Warning: $LIBTOOLIZE is creating ltmain.sh in the wrong directory"
+ $ECHO
+ $ECHO "Fortunately, the problem can be worked around by simply copying the"
+ $ECHO "file to the appropriate location (${_aux_dir}/). This has been done for you."
+ $ECHO
+ $VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\""
+ cp -p ltmain.sh "${_aux_dir}/ltmain.sh"
+ $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+ fi
+ fi # ltmain.sh
+
+ if [ "x$DOWNLOAD" = "xyes" ] ; then
+ download_gnulib_config_guess
+ fi
+ fi # libtoolize_needed
+
+ ############
+ # autoconf #
+ ############
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS"
+ autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ # retry without the -f and check for usage of macros that are too new
+ ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE"
+ ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE"
+ ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T"
+
+ macros_to_search=""
+ ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`"
+ ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`"
+
+ if [ $ac_major -lt 2 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+ else
+ if [ $ac_minor -lt 54 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+ elif [ $ac_minor -lt 55 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros"
+ elif [ $ac_minor -lt 59 ] ; then
+ macros_to_search="$ac2_59_macros"
+ fi
+ fi
+
+ configure_ac_macros=__none__
+ for feature in $macros_to_search ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ if [ "x$configure_ac_macros" = "x__none__" ] ; then
+ configure_ac_macros="$feature"
+ else
+ configure_ac_macros="$feature $configure_ac_macros"
+ fi
+ fi
+ done
+ if [ ! "x$configure_ac_macros" = "x__none__" ] ; then
+ $ECHO
+ $ECHO "Warning: Unsupported macros were found in $CONFIGURE"
+ $ECHO
+ $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any"
+ $ECHO "unsupported macros are used that exceed the minimum version"
+ $ECHO "settings specified within this file. As such, the following macros"
+ $ECHO "should be removed from configure.ac or the version numbers in this"
+ $ECHO "file should be increased:"
+ $ECHO
+ $ECHO "$configure_ac_macros"
+ $ECHO
+ $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C"
+ fi
+
+ ###################
+ # autoconf, retry #
+ ###################
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOCONF"
+ autoconf_output="`$AUTOCONF 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ # test if libtool is busted
+ libtool_failure "$autoconf_output"
+
+ # let the user know what went wrong
+ cat <<EOF
+$autoconf_output
+EOF
+ $ECHO "ERROR: $AUTOCONF failed"
+ exit 2
+ else
+ # autoconf sans -f and possibly sans unsupported options succeed so warn verbosely
+ $ECHO
+ $ECHO "Warning: autoconf seems to have succeeded by removing the following options:"
+ $ECHO " AUTOCONF_OPTIONS=\"$AUTOCONF_OPTIONS\""
+ $ECHO
+ $ECHO "Removing those options should not be necessary and indicate some other"
+ $ECHO "problem with the build system. The build preparation is highly suspect"
+ $ECHO "and may result in configuration or compilation errors. Consider"
+ if [ "x$VERBOSE_ECHO" = "x:" ] ; then
+ $ECHO "rerunning the build preparation with verbose output enabled."
+ $ECHO " $AUTOGEN_SH --verbose"
+ else
+ $ECHO "reviewing the minimum GNU Autotools version settings contained in"
+ $ECHO "this script along with the macros being used in your `basename \"$CONFIGURE\"` file."
+ fi
+ $ECHO
+ $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+ fi # autoconf ret = 0
+ fi # autoconf ret = 0
+
+ ##############
+ # autoheader #
+ ##############
+ need_autoheader=no
+ for feature in AM_CONFIG_HEADER AC_CONFIG_HEADER ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ need_autoheader=yes
+ break
+ fi
+ done
+ if [ "x$need_autoheader" = "xyes" ] ; then
+ $VERBOSE_ECHO "$AUTOHEADER $AUTOHEADER_OPTIONS"
+ autoheader_output="`$AUTOHEADER $AUTOHEADER_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoheader_output"
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $AUTOHEADER failed" && exit 2 ; fi
+ fi # need_autoheader
+
+ ############
+ # automake #
+ ############
+ need_automake=no
+ for feature in AM_INIT_AUTOMAKE ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ need_automake=yes
+ break
+ fi
+ done
+
+ if [ "x$need_automake" = "xyes" ] ; then
+ $VERBOSE_ECHO "$AUTOMAKE $AUTOMAKE_OPTIONS"
+ automake_output="`$AUTOMAKE $AUTOMAKE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$automake_output"
+
+ if [ ! $ret = 0 ] ; then
+
+ ###################
+ # automake, retry #
+ ###################
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOMAKE $ALT_AUTOMAKE_OPTIONS"
+ # retry without the -f
+ automake_output="`$AUTOMAKE $ALT_AUTOMAKE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$automake_output"
+
+ if [ ! $ret = 0 ] ; then
+ # test if libtool is busted
+ libtool_failure "$automake_output"
+
+ # let the user know what went wrong
+ cat <<EOF
+$automake_output
+EOF
+ $ECHO "ERROR: $AUTOMAKE failed"
+ exit 2
+ fi # automake retry
+ fi # automake ret = 0
+ fi # need_automake
+} # end of manual_autogen
+
+
+#####################################
+# RECURSIVE_MANUAL_AUTOGEN FUNCTION #
+#####################################
+recursive_manual_autogen ( ) {
+
+ # run the build preparation steps manually for this directory
+ manual_autogen
+
+ # for projects using recursive configure, run the build
+ # preparation steps for the subdirectories.
+ if [ ! "x$CONFIG_SUBDIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively configuring the following directories:"
+ $VERBOSE_ECHO " $CONFIG_SUBDIRS"
+ for dir in $CONFIG_SUBDIRS ; do
+ $VERBOSE_ECHO "Processing recursive configure in $dir"
+ cd "$START_PATH"
+ cd "$dir"
+
+ # new directory, prepare
+ initialize
+
+ # run manual steps for the subdir and any others below
+ recursive_manual_autogen
+ done
+ fi
+}
+
+
+################################
+# run manual preparation steps #
+################################
+if [ "x$reconfigure_manually" = "xyes" ] ; then
+ $ECHO
+ $ECHO $ECHO_N "Preparing build ... $ECHO_C"
+
+ recursive_manual_autogen
+fi
+
+
+#########################
+# restore and summarize #
+#########################
+cd "$START_PATH"
+
+# restore COPYING and INSTALL from backup if necessary
+recursive_restore
+
+# make sure we end up with a configure script
+config_ac="`locate_configure_template`"
+config="`echo $config_ac | sed 's/\.ac$//' | sed 's/\.in$//'`"
+if [ "x$config" = "x" ] ; then
+ $VERBOSE_ECHO "Could not locate the configure template (from `pwd`)"
+fi
+
+# summarize
+$ECHO "done"
+$ECHO
+if test "x$config" = "x" -o ! -f "$config" ; then
+ $ECHO "WARNING: The $PROJECT build system should now be prepared but there"
+ $ECHO "does not seem to be a resulting configure file. This is unexpected"
+ $ECHO "and likely the result of an error. You should run $NAME_OF_AUTOGEN"
+ $ECHO "with the --verbose option to get more details on a potential"
+ $ECHO "misconfiguration."
+else
+ $ECHO "The $PROJECT build system is now prepared. To build here, run:"
+ $ECHO " $config"
+ $ECHO " make"
+fi
+
+
+# Local Variables:
+# mode: sh
+# tab-width: 8
+# sh-basic-offset: 4
+# sh-indentation: 4
+# indent-tabs-mode: t
+# End:
+# ex: shiftwidth=4 tabstop=8
diff --git a/config/ax_check_compile_flag.m4 b/config/ax_check_compile_flag.m4
new file mode 100644
index 0000000..c3a8d69
--- /dev/null
+++ b/config/ax_check_compile_flag.m4
@@ -0,0 +1,72 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the current language's compiler
+# or gives an error. (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the current language's default
+# flags (e.g. CFLAGS) when the check is done. The check is thus made with
+# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
+# force the compiler to issue an error when a bad flag is given.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 2
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+ _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/config/ax_cxx_compile_stdcxx.m4 b/config/ax_cxx_compile_stdcxx.m4
new file mode 100644
index 0000000..5032bba
--- /dev/null
+++ b/config/ax_cxx_compile_stdcxx.m4
@@ -0,0 +1,982 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the specified
+# version of the C++ standard. If necessary, add switches to CXX and
+# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
+# or '14' (for the C++14 standard).
+#
+# The second argument, if specified, indicates whether you insist on an
+# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+# -std=c++11). If neither is specified, you get whatever works, with
+# preference for an extended mode.
+#
+# The third argument, if specified 'mandatory' or if left unspecified,
+# indicates that baseline support for the specified C++ standard is
+# required and that the macro should error out if no mode with that
+# support is found. If specified 'optional', then configuration proceeds
+# regardless, after defining HAVE_CXX${VERSION} if and only if a
+# supporting mode is found.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+# Copyright (c) 2015 Paul Norman <penorman@mac.com>
+# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+# Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 7
+
+dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl (serial version number 13).
+
+AX_REQUIRE_DEFINED([AC_MSG_WARN])
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+ m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
+ [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
+ [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+ [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$2], [], [],
+ [$2], [ext], [],
+ [$2], [noext], [],
+ [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+ [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+ AC_LANG_PUSH([C++])dnl
+ ac_success=no
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
+ ax_cv_cxx_compile_cxx$1,
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [ax_cv_cxx_compile_cxx$1=yes],
+ [ax_cv_cxx_compile_cxx$1=no])])
+ if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
+ ac_success=yes
+ fi
+
+ m4_if([$2], [noext], [], [dnl
+ if test x$ac_success = xno; then
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ switch="-std=gnu++${alternative}"
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ fi])
+
+ m4_if([$2], [ext], [], [dnl
+ if test x$ac_success = xno; then
+ dnl HP's aCC needs +std=c++11 according to:
+ dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+ dnl Cray's crayCC needs "-h std=c++11"
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ if test x$ac_success = xyes; then
+ break
+ fi
+ done
+ fi])
+ AC_LANG_POP([C++])
+ if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+ if test x$ac_success = xno; then
+ AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+ fi
+ fi
+ if test x$ac_success = xno; then
+ HAVE_CXX$1=0
+ AC_MSG_NOTICE([No compiler with C++$1 support was found])
+ else
+ HAVE_CXX$1=1
+ AC_DEFINE(HAVE_CXX$1,1,
+ [define if the compiler supports basic C++$1 syntax])
+ fi
+ AC_SUBST(HAVE_CXX$1)
+ m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])])
+])
+
+
+dnl Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+)
+
+dnl Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+ namespace test_static_assert
+ {
+
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ }
+
+ namespace test_final_override
+ {
+
+ struct Base
+ {
+ virtual void f() {}
+ };
+
+ struct Derived : public Base
+ {
+ virtual void f() override {}
+ };
+
+ }
+
+ namespace test_double_right_angle_brackets
+ {
+
+ template < typename T >
+ struct check {};
+
+ typedef check<void> single_type;
+ typedef check<check<void>> double_type;
+ typedef check<check<check<void>>> triple_type;
+ typedef check<check<check<check<void>>>> quadruple_type;
+
+ }
+
+ namespace test_decltype
+ {
+
+ int
+ f()
+ {
+ int a = 1;
+ decltype(a) b = 2;
+ return a + b;
+ }
+
+ }
+
+ namespace test_type_deduction
+ {
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static const bool value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static const bool value = true;
+ };
+
+ template < typename T1, typename T2 >
+ auto
+ add(T1 a1, T2 a2) -> decltype(a1 + a2)
+ {
+ return a1 + a2;
+ }
+
+ int
+ test(const int c, volatile int v)
+ {
+ static_assert(is_same<int, decltype(0)>::value == true, "");
+ static_assert(is_same<int, decltype(c)>::value == false, "");
+ static_assert(is_same<int, decltype(v)>::value == false, "");
+ auto ac = c;
+ auto av = v;
+ auto sumi = ac + av + 'x';
+ auto sumf = ac + av + 1.0;
+ static_assert(is_same<int, decltype(ac)>::value == true, "");
+ static_assert(is_same<int, decltype(av)>::value == true, "");
+ static_assert(is_same<int, decltype(sumi)>::value == true, "");
+ static_assert(is_same<int, decltype(sumf)>::value == false, "");
+ static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+ return (sumf > 0.0) ? sumi : add(c, v);
+ }
+
+ }
+
+ namespace test_noexcept
+ {
+
+ int f() { return 0; }
+ int g() noexcept { return 0; }
+
+ static_assert(noexcept(f()) == false, "");
+ static_assert(noexcept(g()) == true, "");
+
+ }
+
+ namespace test_constexpr
+ {
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+ {
+ return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+ }
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c(const CharT *const s) noexcept
+ {
+ return strlen_c_r(s, 0UL);
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("1") == 1UL, "");
+ static_assert(strlen_c("example") == 7UL, "");
+ static_assert(strlen_c("another\0example") == 7UL, "");
+
+ }
+
+ namespace test_rvalue_references
+ {
+
+ template < int N >
+ struct answer
+ {
+ static constexpr int value = N;
+ };
+
+ answer<1> f(int&) { return answer<1>(); }
+ answer<2> f(const int&) { return answer<2>(); }
+ answer<3> f(int&&) { return answer<3>(); }
+
+ void
+ test()
+ {
+ int i = 0;
+ const int c = 0;
+ static_assert(decltype(f(i))::value == 1, "");
+ static_assert(decltype(f(c))::value == 2, "");
+ static_assert(decltype(f(0))::value == 3, "");
+ }
+
+ }
+
+ namespace test_uniform_initialization
+ {
+
+ struct test
+ {
+ static const int zero {};
+ static const int one {1};
+ };
+
+ static_assert(test::zero == 0, "");
+ static_assert(test::one == 1, "");
+
+ }
+
+ namespace test_lambdas
+ {
+
+ void
+ test1()
+ {
+ auto lambda1 = [](){};
+ auto lambda2 = lambda1;
+ lambda1();
+ lambda2();
+ }
+
+ int
+ test2()
+ {
+ auto a = [](int i, int j){ return i + j; }(1, 2);
+ auto b = []() -> int { return '0'; }();
+ auto c = [=](){ return a + b; }();
+ auto d = [&](){ return c; }();
+ auto e = [a, &b](int x) mutable {
+ const auto identity = [](int y){ return y; };
+ for (auto i = 0; i < a; ++i)
+ a += b--;
+ return x + identity(a + b);
+ }(0);
+ return a + b + c + d + e;
+ }
+
+ int
+ test3()
+ {
+ const auto nullary = [](){ return 0; };
+ const auto unary = [](int x){ return x; };
+ using nullary_t = decltype(nullary);
+ using unary_t = decltype(unary);
+ const auto higher1st = [](nullary_t f){ return f(); };
+ const auto higher2nd = [unary](nullary_t f1){
+ return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+ };
+ return higher1st(nullary) + higher2nd(nullary)(unary);
+ }
+
+ }
+
+ namespace test_variadic_templates
+ {
+
+ template <int...>
+ struct sum;
+
+ template <int N0, int... N1toN>
+ struct sum<N0, N1toN...>
+ {
+ static constexpr auto value = N0 + sum<N1toN...>::value;
+ };
+
+ template <>
+ struct sum<>
+ {
+ static constexpr auto value = 0;
+ };
+
+ static_assert(sum<>::value == 0, "");
+ static_assert(sum<1>::value == 1, "");
+ static_assert(sum<23>::value == 23, "");
+ static_assert(sum<1, 2>::value == 3, "");
+ static_assert(sum<5, 5, 11>::value == 21, "");
+ static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+ }
+
+ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+ // because of this.
+ namespace test_template_alias_sfinae
+ {
+
+ struct foo {};
+
+ template<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() { func<foo>(0); }
+
+ }
+
+} // namespace cxx11
+
+#endif // __cplusplus >= 201103L
+
+]])
+
+
+dnl Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+ namespace test_polymorphic_lambdas
+ {
+
+ int
+ test()
+ {
+ const auto lambda = [](auto&&... args){
+ const auto istiny = [](auto x){
+ return (sizeof(x) == 1UL) ? 1 : 0;
+ };
+ const int aretiny[] = { istiny(args)... };
+ return aretiny[0];
+ };
+ return lambda(1, 1L, 1.0f, '1');
+ }
+
+ }
+
+ namespace test_binary_literals
+ {
+
+ constexpr auto ivii = 0b0000000000101010;
+ static_assert(ivii == 42, "wrong value");
+
+ }
+
+ namespace test_generalized_constexpr
+ {
+
+ template < typename CharT >
+ constexpr unsigned long
+ strlen_c(const CharT *const s) noexcept
+ {
+ auto length = 0UL;
+ for (auto p = s; *p; ++p)
+ ++length;
+ return length;
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("x") == 1UL, "");
+ static_assert(strlen_c("test") == 4UL, "");
+ static_assert(strlen_c("another\0test") == 7UL, "");
+
+ }
+
+ namespace test_lambda_init_capture
+ {
+
+ int
+ test()
+ {
+ auto x = 0;
+ const auto lambda1 = [a = x](int b){ return a + b; };
+ const auto lambda2 = [a = lambda1(x)](){ return a; };
+ return lambda2();
+ }
+
+ }
+
+ namespace test_digit_separators
+ {
+
+ constexpr auto ten_million = 100'000'000;
+ static_assert(ten_million == 100000000, "");
+
+ }
+
+ namespace test_return_type_deduction
+ {
+
+ auto f(int& x) { return x; }
+ decltype(auto) g(int& x) { return x; }
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static constexpr auto value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static constexpr auto value = true;
+ };
+
+ int
+ test()
+ {
+ auto x = 0;
+ static_assert(is_same<int, decltype(f(x))>::value, "");
+ static_assert(is_same<int&, decltype(g(x))>::value, "");
+ return x;
+ }
+
+ }
+
+} // namespace cxx14
+
+#endif // __cplusplus >= 201402L
+
+]])
+
+
+dnl Tests for new features in C++17
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
+
+// If the compiler admits that it is not ready for C++17, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus <= 201402L
+
+#error "This is not a C++17 compiler"
+
+#else
+
+#if defined(__clang__)
+ #define REALLY_CLANG
+#else
+ #if defined(__GNUC__)
+ #define REALLY_GCC
+ #endif
+#endif
+
+#include <initializer_list>
+#include <utility>
+#include <type_traits>
+
+namespace cxx17
+{
+
+#if !defined(REALLY_CLANG)
+ namespace test_constexpr_lambdas
+ {
+
+ // TODO: test it with clang++ from git
+
+ constexpr int foo = [](){return 42;}();
+
+ }
+#endif // !defined(REALLY_CLANG)
+
+ namespace test::nested_namespace::definitions
+ {
+
+ }
+
+ namespace test_fold_expression
+ {
+
+ template<typename... Args>
+ int multiply(Args... args)
+ {
+ return (args * ... * 1);
+ }
+
+ template<typename... Args>
+ bool all(Args... args)
+ {
+ return (args && ...);
+ }
+
+ }
+
+ namespace test_extended_static_assert
+ {
+
+ static_assert (true);
+
+ }
+
+ namespace test_auto_brace_init_list
+ {
+
+ auto foo = {5};
+ auto bar {5};
+
+ static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
+ static_assert(std::is_same<int, decltype(bar)>::value);
+ }
+
+ namespace test_typename_in_template_template_parameter
+ {
+
+ template<template<typename> typename X> struct D;
+
+ }
+
+ namespace test_fallthrough_nodiscard_maybe_unused_attributes
+ {
+
+ int f1()
+ {
+ return 42;
+ }
+
+ [[nodiscard]] int f2()
+ {
+ [[maybe_unused]] auto unused = f1();
+
+ switch (f1())
+ {
+ case 17:
+ f1();
+ [[fallthrough]];
+ case 42:
+ f1();
+ }
+ return f1();
+ }
+
+ }
+
+ namespace test_extended_aggregate_initialization
+ {
+
+ struct base1
+ {
+ int b1, b2 = 42;
+ };
+
+ struct base2
+ {
+ base2() {
+ b3 = 42;
+ }
+ int b3;
+ };
+
+ struct derived : base1, base2
+ {
+ int d;
+ };
+
+ derived d1 {{1, 2}, {}, 4}; // full initialization
+ derived d2 {{}, {}, 4}; // value-initialized bases
+
+ }
+
+ namespace test_general_range_based_for_loop
+ {
+
+ struct iter
+ {
+ int i;
+
+ int& operator* ()
+ {
+ return i;
+ }
+
+ const int& operator* () const
+ {
+ return i;
+ }
+
+ iter& operator++()
+ {
+ ++i;
+ return *this;
+ }
+ };
+
+ struct sentinel
+ {
+ int i;
+ };
+
+ bool operator== (const iter& i, const sentinel& s)
+ {
+ return i.i == s.i;
+ }
+
+ bool operator!= (const iter& i, const sentinel& s)
+ {
+ return !(i == s);
+ }
+
+ struct range
+ {
+ iter begin() const
+ {
+ return {0};
+ }
+
+ sentinel end() const
+ {
+ return {5};
+ }
+ };
+
+ void f()
+ {
+ range r {};
+
+ for (auto i : r)
+ {
+ [[maybe_unused]] auto v = i;
+ }
+ }
+
+ }
+
+ namespace test_lambda_capture_asterisk_this_by_value
+ {
+
+ struct t
+ {
+ int i;
+ int foo()
+ {
+ return [*this]()
+ {
+ return i;
+ }();
+ }
+ };
+
+ }
+
+ namespace test_enum_class_construction
+ {
+
+ enum class byte : unsigned char
+ {};
+
+ byte foo {42};
+
+ }
+
+ namespace test_constexpr_if
+ {
+
+ template <bool cond>
+ int f ()
+ {
+ if constexpr(cond)
+ {
+ return 13;
+ }
+ else
+ {
+ return 42;
+ }
+ }
+
+ }
+
+ namespace test_selection_statement_with_initializer
+ {
+
+ int f()
+ {
+ return 13;
+ }
+
+ int f2()
+ {
+ if (auto i = f(); i > 0)
+ {
+ return 3;
+ }
+
+ switch (auto i = f(); i + 4)
+ {
+ case 17:
+ return 2;
+
+ default:
+ return 1;
+ }
+ }
+
+ }
+
+#if !defined(REALLY_CLANG)
+ namespace test_template_argument_deduction_for_class_templates
+ {
+
+ // TODO: test it with clang++ from git
+
+ template <typename T1, typename T2>
+ struct pair
+ {
+ pair (T1 p1, T2 p2)
+ : m1 {p1},
+ m2 {p2}
+ {}
+
+ T1 m1;
+ T2 m2;
+ };
+
+ void f()
+ {
+ [[maybe_unused]] auto p = pair{13, 42u};
+ }
+
+ }
+#endif // !defined(REALLY_CLANG)
+
+ namespace test_non_type_auto_template_parameters
+ {
+
+ template <auto n>
+ struct B
+ {};
+
+ B<5> b1;
+ B<'a'> b2;
+
+ }
+
+#if !defined(REALLY_CLANG)
+ namespace test_structured_bindings
+ {
+
+ // TODO: test it with clang++ from git
+
+ int arr[2] = { 1, 2 };
+ std::pair<int, int> pr = { 1, 2 };
+
+ auto f1() -> int(&)[2]
+ {
+ return arr;
+ }
+
+ auto f2() -> std::pair<int, int>&
+ {
+ return pr;
+ }
+
+ struct S
+ {
+ int x1 : 2;
+ volatile double y1;
+ };
+
+ S f3()
+ {
+ return {};
+ }
+
+ auto [ x1, y1 ] = f1();
+ auto& [ xr1, yr1 ] = f1();
+ auto [ x2, y2 ] = f2();
+ auto& [ xr2, yr2 ] = f2();
+ const auto [ x3, y3 ] = f3();
+
+ }
+#endif // !defined(REALLY_CLANG)
+
+#if !defined(REALLY_CLANG)
+ namespace test_exception_spec_type_system
+ {
+
+ // TODO: test it with clang++ from git
+
+ struct Good {};
+ struct Bad {};
+
+ void g1() noexcept;
+ void g2();
+
+ template<typename T>
+ Bad
+ f(T*, T*);
+
+ template<typename T1, typename T2>
+ Good
+ f(T1*, T2*);
+
+ static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+ }
+#endif // !defined(REALLY_CLANG)
+
+ namespace test_inline_variables
+ {
+
+ template<class T> void f(T)
+ {}
+
+ template<class T> inline T g(T)
+ {
+ return T{};
+ }
+
+ template<> inline void f<>(int)
+ {}
+
+ template<> int g<>(int)
+ {
+ return 5;
+ }
+
+ }
+
+} // namespace cxx17
+
+#endif // __cplusplus <= 201402L
+
+]])
diff --git a/config/ax_cxx_compile_stdcxx_11.m4 b/config/ax_cxx_compile_stdcxx_11.m4
new file mode 100644
index 0000000..1733fd8
--- /dev/null
+++ b/config/ax_cxx_compile_stdcxx_11.m4
@@ -0,0 +1,39 @@
+# =============================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
+# =============================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the C++11
+# standard; if necessary, add switches to CXX and CXXCPP to enable
+# support.
+#
+# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
+# macro with the version set to C++11. The two optional arguments are
+# forwarded literally as the second and third argument respectively.
+# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
+# more information. If you want to use this macro, you also need to
+# download the ax_cxx_compile_stdcxx.m4 file.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+# Copyright (c) 2015 Paul Norman <penorman@mac.com>
+# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 18
+
+AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])
diff --git a/config/ax_sse.m4 b/config/ax_sse.m4
new file mode 100644
index 0000000..de97124
--- /dev/null
+++ b/config/ax_sse.m4
@@ -0,0 +1,71 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_ext.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_SSE
+#
+# DESCRIPTION
+#
+# Find SIMD extensions supported by compiler. The -m"simdextensionname" is
+# added to SIMD_FLAGS if compiler supports it. For example, if "sse2" is
+# available, then "-msse2" is added to SIMD_FLAGS.
+#
+# This macro calls:
+#
+# AC_SUBST(SIMD_FLAGS)
+#
+# And defines:
+#
+# HAVE_SSE3 / HAVE_SSE4.1
+#
+# LICENSE
+#
+# Copyright (c) 2007 Christophe Tournayre <turn3r@users.sourceforge.net>
+# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+#
+# NOTE: The functionality that requests the cpuid has been stripped because
+# this project detects the CPU capabilities during runtime. However, we
+# still need to check if the compiler supports the requested SIMD flag
+
+#serial 12
+
+AC_DEFUN([AX_SSE],
+[
+ AC_REQUIRE([AC_CANONICAL_HOST])
+
+ AM_CONDITIONAL(HAVE_SSE3, false)
+ AM_CONDITIONAL(HAVE_SSE4_1, false)
+
+ case $host_cpu in
+ i[[3456]]86*|x86_64*|amd64*)
+ AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, [])
+ if test x"$ax_cv_support_sse3_ext" = x"yes"; then
+ SIMD_FLAGS="$SIMD_FLAGS -msse3"
+ AC_DEFINE(HAVE_SSE3,,
+ [Support SSE3 (Streaming SIMD Extensions 3) instructions])
+ AM_CONDITIONAL(HAVE_SSE3, true)
+ else
+ AC_MSG_WARN([Your compiler does not support SSE3 instructions])
+ fi
+
+ AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, [])
+ if test x"$ax_cv_support_sse41_ext" = x"yes"; then
+ SIMD_FLAGS="$SIMD_FLAGS -msse4.1"
+ AC_DEFINE(HAVE_SSE4_1,,
+ [Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions])
+ AM_CONDITIONAL(HAVE_SSE4_1, true)
+ else
+ AC_MSG_WARN([Your compiler does not support SSE4.1])
+ fi
+ ;;
+ esac
+
+ AC_SUBST(SIMD_FLAGS)
+])
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..64e3b35
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,270 @@
+dnl
+dnl Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+dnl
+dnl This software is distributed under the terms of the GNU Public License.
+dnl See the COPYING file in the main directory for details.
+dnl
+dnl This program is free software: you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation, either version 3 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program. If not, see <http://www.gnu.org/licenses/>.
+dnl
+
+AC_INIT([osmo-trx],
+ m4_esyscmd([./git-version-gen .tarball-version]),
+ [openbsc@lists.osmocom.org])
+AC_PREREQ(2.57)
+AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am])
+AC_CONFIG_AUX_DIR([.])
+AC_CONFIG_MACRO_DIR([config])
+AM_CONFIG_HEADER(config.h)
+AC_CONFIG_TESTDIR(tests)
+
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+AC_CANONICAL_TARGET
+
+AM_INIT_AUTOMAKE([subdir-objects])
+
+dnl Linux kernel KBuild style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl include release helper
+RELMAKE='-include osmo-release.mk'
+AC_SUBST([RELMAKE])
+
+AM_PROG_AS
+AC_PROG_CC
+AC_PROG_CXX
+AX_CXX_COMPILE_STDCXX_11
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PROG_INSTALL
+AC_PATH_PROG([RM_PROG], [rm])
+AC_LANG([C++])
+
+dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
+AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
+if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
+ AC_MSG_WARN([You need to install pkg-config])
+fi
+PKG_PROG_PKG_CONFIG([0.20])
+
+AC_LIBTOOL_WIN32_DLL
+AC_ENABLE_SHARED dnl do build shared libraries
+AC_DISABLE_STATIC dnl don't build static libraries
+AC_PROG_LIBTOOL
+
+dnl Checks for header files.
+AC_HEADER_STDC
+dnl This is required for GnuRadio includes to understand endianess correctly:
+AC_CHECK_HEADERS([byteswap.h])
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_INLINE
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_C_BIGENDIAN
+
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
+
+AC_ARG_ENABLE(sanitize,
+ [AS_HELP_STRING(
+ [--enable-sanitize],
+ [Compile with address sanitizer enabled],
+ )],
+ [sanitize=$enableval], [sanitize="no"])
+if test x"$sanitize" = x"yes"
+then
+ CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
+ LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined"
+fi
+
+AC_ARG_ENABLE(werror,
+ [AS_HELP_STRING(
+ [--enable-werror],
+ [Turn all compiler warnings into errors, with exceptions:
+ a) deprecation (allow upstream to mark deprecation without breaking builds);
+ b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
+ ]
+ )],
+ [werror=$enableval], [werror="no"])
+if test x"$werror" = x"yes"
+then
+ WERROR_FLAGS="-Werror"
+ WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
+ WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
+ CFLAGS="$CFLAGS $WERROR_FLAGS"
+ CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
+fi
+
+
+AC_ARG_WITH(uhd, [
+ AS_HELP_STRING([--with-uhd],
+ [enable UHD based transceiver])
+])
+
+AC_ARG_WITH(usrp1, [
+ AS_HELP_STRING([--with-usrp1],
+ [enable USRP1 gnuradio based transceiver])
+])
+
+AC_ARG_WITH(lms, [
+ AS_HELP_STRING([--with-lms],
+ [enable LimeSuite based transceiver])
+])
+
+AC_ARG_WITH(singledb, [
+ AS_HELP_STRING([--with-singledb],
+ [enable single daughterboard use on USRP1])
+])
+
+AC_ARG_WITH(neon, [
+ AS_HELP_STRING([--with-neon],
+ [enable ARM NEON support])
+])
+
+AC_ARG_WITH(neon-vfpv4, [
+ AS_HELP_STRING([--with-neon-vfpv4],
+ [enable ARM NEON FMA support])
+])
+
+AC_ARG_WITH(sse, [
+ AS_HELP_STRING([--with-sse],
+ [enable x86 SSE support (default)])
+])
+
+AS_IF([test "x$with_neon" = "xyes"], [
+ AC_DEFINE(HAVE_NEON, 1, Support ARM NEON)
+])
+
+AS_IF([test "x$with_neon_vfpv4" = "xyes"], [
+ AC_DEFINE(HAVE_NEON, 1, Support ARM NEON)
+ AC_DEFINE(HAVE_NEON_FMA, 1, Support ARM NEON with FMA)
+])
+
+AS_IF([test "x$with_usrp1" = "xyes"], [
+ PKG_CHECK_MODULES(USRP, usrp >= 3.3)
+])
+
+AS_IF([test "x$with_lms" = "xyes"], [
+ PKG_CHECK_MODULES(LMS, LimeSuite)
+])
+
+AS_IF([test "x$with_uhd" != "xno"],[
+ PKG_CHECK_MODULES(UHD, uhd >= 003.011,
+ [AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)],
+ [PKG_CHECK_MODULES(UHD, uhd >= 003.009,
+ [AC_DEFINE(USE_UHD_3_9, 1, UHD version 3.9.0 or higher)],
+ [PKG_CHECK_MODULES(UHD, uhd >= 003.005)]
+ )]
+ )
+])
+
+AS_IF([test "x$with_singledb" = "xyes"], [
+ AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
+])
+
+# Find and define supported SIMD extensions
+AS_IF([test "x$with_sse" != "xno"], [
+ AX_SSE
+], [
+ AM_CONDITIONAL(HAVE_SSE3, false)
+ AM_CONDITIONAL(HAVE_SSE4_1, false)
+])
+
+dnl Check if the compiler supports specified GCC's built-in function
+AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
+ AC_CACHE_CHECK(
+ [whether ${CC} has $1 built-in],
+ [osmo_cv_cc_has_builtin], [
+ AC_LINK_IFELSE([
+ AC_LANG_PROGRAM([], [
+ __builtin_cpu_supports("sse");
+ ])
+ ],
+ [AS_VAR_SET([osmo_cv_cc_has_builtin], [yes])],
+ [AS_VAR_SET([osmo_cv_cc_has_builtin], [no])])
+ ]
+ )
+
+ AS_IF([test yes = AS_VAR_GET([osmo_cv_cc_has_builtin])], [
+ AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
+ [Define to 1 if compiler has the '$1' built-in function])
+ ], [
+ AC_MSG_WARN($2)
+ ])
+])
+
+dnl Check if the compiler supports runtime SIMD detection
+CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
+ [Runtime SIMD detection will be disabled])
+
+AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" != "xno"])
+AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
+AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
+AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
+AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
+
+PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
+PKG_CHECK_MODULES(FFTWF, fftw3f)
+
+AC_CHECK_HEADER([boost/config.hpp],[],
+ [AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])])
+
+ # https://www.freedesktop.org/software/systemd/man/daemon.html
+ AC_ARG_WITH([systemdsystemunitdir],
+ [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
+ [with_systemdsystemunitdir=auto])
+ AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
+ def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
+
+ AS_IF([test "x$def_systemdsystemunitdir" = "x"],
+ [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
+ [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
+ with_systemdsystemunitdir=no],
+ [with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
+ AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
+ [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
+ AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
+
+AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
+AC_MSG_RESULT([CFLAGS="$CFLAGS"])
+AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
+AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
+
+dnl Output files
+AC_CONFIG_FILES([\
+ Makefile \
+ CommonLibs/Makefile \
+ GSM/Makefile \
+ Transceiver52M/Makefile \
+ Transceiver52M/arch/Makefile \
+ Transceiver52M/arch/common/Makefile \
+ Transceiver52M/arch/arm/Makefile \
+ Transceiver52M/arch/x86/Makefile \
+ Transceiver52M/device/Makefile \
+ Transceiver52M/device/uhd/Makefile \
+ Transceiver52M/device/usrp1/Makefile \
+ Transceiver52M/device/lms/Makefile \
+ tests/Makefile \
+ tests/CommonLibs/Makefile \
+ tests/Transceiver52M/Makefile \
+ doc/Makefile \
+ doc/examples/Makefile \
+ contrib/Makefile \
+ contrib/systemd/Makefile \
+])
+
+AC_OUTPUT
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
new file mode 100644
index 0000000..3439c97
--- /dev/null
+++ b/contrib/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = systemd
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
new file mode 100755
index 0000000..029f0e1
--- /dev/null
+++ b/contrib/jenkins.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+set -ex
+
+substr() { [ -z "${2##*$1*}" ]; }
+
+#apt-get install qemu qemu-user-static qemu-system-arm debootstrap fakeroot proot
+mychroot_nocwd() {
+ # LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container
+ # PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH.
+ # PROOT_NO_SECCOMP is requried due to proot bug #106
+ LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@"
+}
+
+mychroot() {
+ mychroot_nocwd -w / "$@"
+}
+
+base="$PWD"
+deps="$base/deps"
+inst="$deps/install"
+export deps inst
+
+if [ -z "${INSIDE_CHROOT}" ]; then
+
+ osmo-clean-workspace.sh
+
+ # Only use ARM chroot if host is not ARM and the target is ARM:
+ if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
+
+ OSMOTRX_DIR="$PWD" # we assume we are called as contrib/jenkins.sh
+ ROOTFS_PREFIX="${ROOTFS_PREFIX:-$HOME}"
+ ROOTFS="${ROOTFS_PREFIX}/qemu-img"
+ mkdir -p "${ROOTFS_PREFIX}"
+
+ # Prepare chroot:
+ if [ ! -d "$ROOTFS" ]; then
+ mkdir -p "$ROOTFS"
+ if [ "x${USE_DEBOOTSTRAP}" = "x1" ]; then
+ fakeroot qemu-debootstrap --foreign --include="linux-image-armmp-lpae" --arch=armhf stretch "$ROOTFS" http://ftp.de.debian.org/debian/
+ # Hack to avoid debootstrap trying to mount /proc, as it will fail with "no permissions" and anyway proot takes care of it:
+ sed -i "s/setup_proc//g" "$ROOTFS/debootstrap/suite-script"
+ mychroot /debootstrap/debootstrap --second-stage --verbose http://ftp.de.debian.org/debian/
+ else
+ YESTERDAY=$(python -c 'import datetime ; print((datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y%m%d"))')
+ wget -nc -q "https://uk.images.linuxcontainers.org/images/debian/stretch/armhf/default/${YESTERDAY}_22:42/rootfs.tar.xz"
+ tar -xf rootfs.tar.xz -C "$ROOTFS/" || true
+ echo "nameserver 8.8.8.8" > "$ROOTFS/etc/resolv.conf"
+ fi
+ mychroot -b /dev apt-get update
+ mychroot apt-get -y install build-essential dh-autoreconf pkg-config libuhd-dev libusb-1.0-0-dev libusb-dev git libtalloc-dev libgnutls28-dev stow
+ fi
+ # Run jenkins.sh inside the chroot:
+ INSIDE_CHROOT=1 mychroot_nocwd \
+ -w /osmo-trx \
+ -b "$OSMOTRX_DIR:/osmo-trx" \
+ -b "$(which osmo-clean-workspace.sh):/usr/bin/osmo-clean-workspace.sh" \
+ -b "$(which osmo-build-dep.sh):/usr/bin/osmo-build-dep.sh" \
+ -b "$(which osmo-deps.sh):/usr/bin/osmo-deps.sh" \
+ ./contrib/jenkins.sh
+ exit 0
+ fi
+fi
+
+mkdir "$deps" || true
+
+osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc"
+osmo-build-dep.sh libusrp
+
+export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
+export LD_LIBRARY_PATH="$inst/lib"
+
+set +x
+echo
+echo
+echo
+echo " =============================== osmo-trx ==============================="
+echo
+set -x
+
+cd "$base"
+autoreconf --install --force
+./configure --enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms $INSTR
+$MAKE $PARALLEL_MAKE
+$MAKE check \
+ || cat-testlogs.sh
+
+osmo-clean-workspace.sh
diff --git a/contrib/systemd/Makefile.am b/contrib/systemd/Makefile.am
new file mode 100644
index 0000000..21fbb53
--- /dev/null
+++ b/contrib/systemd/Makefile.am
@@ -0,0 +1,18 @@
+if HAVE_SYSTEMD
+SYSTEMD_SERVICES =
+
+if DEVICE_UHD
+SYSTEMD_SERVICES += osmo-trx-uhd.service
+endif
+
+if DEVICE_USRP1
+SYSTEMD_SERVICES += osmo-trx-usrp1.service
+endif
+
+if DEVICE_LMS
+SYSTEMD_SERVICES += osmo-trx-lms.service
+endif
+
+EXTRA_DIST = $(SYSTEMD_SERVICES)
+systemdsystemunit_DATA = $(SYSTEMD_SERVICES)
+endif # HAVE_SYSTEMD
diff --git a/contrib/systemd/osmo-trx-lms.service b/contrib/systemd/osmo-trx-lms.service
new file mode 100644
index 0000000..df63e21
--- /dev/null
+++ b/contrib/systemd/osmo-trx-lms.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
+
+[Service]
+Type=simple
+Restart=always
+ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg
+RestartSec=2
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/systemd/osmo-trx-uhd.service b/contrib/systemd/osmo-trx-uhd.service
new file mode 100644
index 0000000..ba27f37
--- /dev/null
+++ b/contrib/systemd/osmo-trx-uhd.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
+
+[Service]
+Type=simple
+Restart=always
+ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
+RestartSec=2
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/systemd/osmo-trx-usrp1.service b/contrib/systemd/osmo-trx-usrp1.service
new file mode 100644
index 0000000..fbff631
--- /dev/null
+++ b/contrib/systemd/osmo-trx-usrp1.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
+
+[Service]
+Type=simple
+Restart=always
+ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg
+RestartSec=2
+
+[Install]
+WantedBy=multi-user.target
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..a9724d8
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,300 @@
+osmo-trx (0.4.0) unstable; urgency=medium
+
+ [ Neels Hofmeyr ]
+ * jenkins: use osmo-clean-workspace.sh before and after build
+
+ [ Harald Welte ]
+ * SocketsTest: Fix printing of non-nul-terminated string
+ * Revert "debian: Remove osmo-trx-usrp1 until we can build libusrp1.deb"
+ * debian/control: Remove "Maintainer" from binary package section
+ * debian/rules: Make sure we always require libusrp
+ * debian: Ensure USRP1 firmware is part of osmo-trx-usrp1
+ * debian/control: Add build dependency to libusrp-dev
+ * update .gitignore for new executable names
+ * osmo-trx: s/GSM Core Address/GSM BTS Address/
+
+ [ Piotr Krysik ]
+ * UHDDevice.cpp: add USRP B205mini support
+
+ [ Max ]
+ * Mark release target as virtual
+ * Remove outdated references to OpenBTS
+ * Remove unused headers
+ * Update installation instructions
+ * Update legal disclaimer
+ * Update license notes
+ * tests: null-terminate buffer
+
+ [ Pau Espin Pedrol ]
+ * cosmetic: Remove trailing whitespace
+ * Logger: Stop using Log.Alarms.Max from config
+ * Logger: Stop using Log.File and Log.Level from config
+ * Drop use of ConfigurationTable gConfig
+ * Remove Configuration module and libsqlite dependency
+ * cosmetic: AUTHORS: fix trailing whitespace
+ * Set up GNU Autotest infrastructure
+ * tests: InterThread: adapt to have reproducible output and enable autotest
+ * tests: Timeval: adapt to have reproducible output and enable autotest
+ * tests: Log: adapt to have reproducible output and enable autotest
+ * Sockets.cpp: Fix initialization of UDD socket
+ * tests: Sockets: adapt to have reproducible output and enable autotest
+ * utils/convolvtest: Remove uneeded libosmocore dependency
+ * Move ARCH_LA to Makefile.common
+ * tests: Migrate convtest util to autotest infrastructure
+ * arm/convert.c: Fix compilation error
+ * arm/convert.c: Add missing convert_init implementation
+ * .gitignore: Add missing test related files
+ * Remove UDDSocket class
+ * tests: SocketTests: Pick OS-assigned instead of setting one manually
+ * tests: SocketsTest: Avoid hang forever if test fails
+ * tests: SocketsTest: Fail test on write fail
+ * tests: TimevalTest: refactor and avoid double comparison
+ * contrib/jenkins.sh: Use qemu+proot+debootstrap to run tests with ARM instruction set
+ * tests: convolve: Disable due to difference in output in different archs
+ * Remove unneeded libdl dependency
+ * Fix whitespace
+ * Add support to set Rx/TxAntenna
+ * UHDDevice: Initialize async_event_thrd in constructor
+ * Logger: Drop unused gLogEarly
+ * Logger: Remove unused logging macros
+ * Logger: get rid of alarm APIs
+ * Logger: Drop syslog support
+ * Logger: Drop support to log into file
+ * Logger: Remove unused includes
+ * Logger: Remove gLogToConsole flag
+ * configure.ac: Check for pkg-config
+ * Depend on libosmocore
+ * osmo-trx: set up signals using libosmocore helpers
+ * osmo-trx: Set up talloc ctx
+ * debian: Depend on libtalloc and libosmocore
+ * Add initial support for logging, vty, ctrl
+ * Logger: Use libosmocore logging system
+ * osmo-trx.cpp: Move trx start and stop to helper functions
+ * Move enums required by VTY to a separate header
+ * vty: Implement VTY cfg parsing for current parameters
+ * doc: Add sample cfg file for LimeSDR
+ * osmo-trx: Use VTY cfg structures while still allowing cmd line options
+ * osmo-trx: Re-introduce -l cmd line parameter
+ * Makefile.am: Avoid using subdir if arch is not required
+ * Build Transceiver52M/common as an .la lib
+ * use osmo_init_logging2()
+ * tests: Makefile.am: Fix typo in include path
+ * configure.ac: Add --enable-sanitize option
+ * Move arch specific fiels to arch subdir
+ * Move device specific files to device subdir
+ * Change configure define USRP1 to DEVICE_USRP1
+ * Move device specific code out of radioInterface
+ * Transceiver: Move device specific code to radioDevice class
+ * Build one osmo-trx binary for each device support enabled
+ * Logger: Print correct source file and line number
+ * Transceiver: log timing info of stale bursts
+ * build: Fix make distcheck
+ * build: More OBS build failure fixes
+ * jenkins.sh: Enable build of osmo-trx-usrp1
+ * debian: Remove osmo-trx-usrp1 until we can build libusrp1.deb
+ * debian: Fix OBS build
+ * build: Fix OBS build for ARM
+ * git-version-gen: Take into account tags not in master
+
+ [ Alexander Huemer ]
+ * Unbreak `./configure --with-usrp1` build
+ * Fix USRP1 build with support for setting Rx/TxAntenna
+
+ [ Alexander Couzens ]
+ * jenkins.sh: fix the download url if the qemu image wasn't setup
+ * jenkins.sh: cleanup always the workspace
+ * jenkins.sh: change qemu-img default location to $HOME/qemu-img instead of /opt/qemu-img
+
+ [ Vadim Yanitskiy ]
+ * Transceiver.cpp: use a define for the MAX_PACKET_LENGTH
+ * Transceiver.cpp: properly zero-terminate received commands
+ * Transceiver.cpp: use pointer arithmetics for CMD parsing
+ * Transceiver.cpp: fix incorrect format string for SETTSC
+ * Transceiver.cpp: prevent out-of-range array access
+
+ [ Martin Hauke ]
+ * configure.ac: Fix typo
+
+ [ Philipp Maier ]
+ * doc: add example config for usrp B200 series
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 16:23:29 +0200
+
+osmo-trx (0.2.0) unstable; urgency=medium
+
+ [ Alexander Chemeris ]
+ * EDGE: Add support for UmTRX.
+ * Common: Get rid of a compilation warning.
+ * Common: Make sure gLogEarly() log to the same facilities as the normal log.
+ * transceiver: Properly handle MAXDLY.
+ * transceiver: Add an option to generate random Access Bursts.
+ * osmo-trx: Output Rx SPS as a part of configuration output.
+ * transceiver: Do not pass transceiver state struct to function where it's not used.
+ * makefile: Fix build from an external path.
+ * radioDevice: GSMRATE macro must have parentheses around its definition.
+ * uhd: Fix comment.
+ * radioInterface: Initialize power scale with a meaningful default.
+ * transceiver: Log channel number in DEBUG output of demoded bursts.
+ * transceiver: Add an option to emulate a RACH delay in random filler mode.
+ * UHD: Initial LimeSDR support.
+ * CommonLibs: Remove unused files.
+ * sigProcLib: Typo sybols -> symbols
+ * radioBuffer: Remove extra ; at the end of inline function definitions.
+ * sigProcLib: Fix documentation, sync argument names in .cpp and .h files.
+ * sigProcLib: make energyDetect() simpler by returning actual energy.
+ * sigProcLib: Rename demodulateBurst() to demodGmskBurst() for clarity.
+ * sigProcLib: Slice SoftVector instead of signalVector for GMSK demod.
+ * Call vectorSlicer() right before packing bits for transmission to osmo-bts.
+ * CommonLibs: Print soft bits with less confidence to console when printing a soft vector.
+ * BitVector: Remove convolutional codec - we don't use it in osmo-trx.
+ * BitVector: Convert SoftVector from 0..1 to -1..+1 soft bits.
+ * signalVector: Implement segment().
+ * vector: Introduce segmentMove() method to move data inside of a vector.
+ * vector: Introduce shrink() function to shrink vector size without loosing data.
+ * Move CorrType type from Transceiver to sigProcLib.
+ * sigProcLib: rename signalError type to SignalError.
+ * Move Transceiver::detectBurst() to sigProcLib to make it reusable.
+ * Move BURST_THRESH from Transceiver.cpp to sigProcLib.h to make it reusable.
+ * sigProcLib: Add operator<< to print CorrType to a string.
+ * sigProcLib.h: Fix whitespaces. No non-whitespace changes.
+ * Move Transceiver::demodulate() to sigProcLib to make it reusable.
+ * sigProcLib: constify signalVector arguments for detectBurst() functions.
+ * sigProcLib: Constify demodulation functions burst argument.
+ * sigProcLib: Fix number of tail bits in random Normal Bursts and zero Stealing Bits.
+ * Configuration: Variables allocated with 'new' must be freed with 'delete'.
+ * BitVector: Remove Generator class.
+ * PRBS: a Pseudo-random binary sequence (PRBS) generator class.
+
+ [ Tom Tsou ]
+ * EDGE: Fix USRP B210 device support
+ * uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts
+ * EDGE: Fix demodulation slicer input
+ * common: Restrict UDP binding to localhost only
+ * common: Add mandatory length field to UDP receive calls
+ * uhd: Update default E3XX settings
+ * uhd: Set default Tx sampling to 4 sps
+ * uhd: Make device offset check a private method
+ * uhd: Set minimum UHD version requirement for E3XX
+ * sigproc: Expand RACH, TSC, and EDGE correlation windows
+ * transceiver: Do not report error on SETTSC when radio is on
+ * transceiver: Add Rx samples-per-symbol option
+ * radioInterface: Convert diversity argument to general type
+ * iface: Add inner ring-buffer implementation
+ * mcbts: Add multi-ARFCN channelizing filters
+ * mcbts: Add multi-ARFCN radio support
+ * sigproc: Adjust burst detection threshold criteria
+ * egprs: Enable 8-PSK length vectors on the Tx interface
+ * egprs: Enable 8-PSK burst detection when EDGE is enabled
+ * transceiver: Remove HANDOVER warnings
+ * mcbts: Allow out of order channel setup
+ * radioInterface: Fix multi-channel buffer index bug
+ * uhd: Add command line option for GPS reference
+ * transceiver: Fix mixed GSMK / 8-PSK transmission
+ * transceiver: Fix 4 SPS receive TOA value
+ * sigproc: Fix missing 8-PSK tail symbols
+ * uhd: Update USRP2/N200/N210 for 4 SPS Rx
+ * sigproc: Match differential GMSK start/end bits to tail bits
+ * uhd: Add missing B200 sample timing for 4 SPS receive
+ * transceiver: Fix command build warning
+ * uhd: Set minimum supported version to 3.9.0
+ * uhd: Add X300 sample timing for 4 SPS
+ * Revert "uhd: Set minimum supported version to 3.9.0"
+ * uhd: Add support for UHD-3.11 logging control
+ * uhd: Increase MC-BTS FPGA clock rate to 51.2 MHz
+ * Resampler: Fix initialization return checking
+ * sigProcLib: Remove unreachable code and no-effect checks
+ * sigProcLib: Check return status on downsampling
+ * sigProcLib: Fix negative value check on unsigned value
+ * Resampler: Fix non-array delete for filter taps
+ * Transceiver: Remove unsigned negative compares
+ * Configuration: Fix const and signedness compile warnings
+ * config: Remove OpenBTS style sqlite configuration
+ * radioInterface: Remove UmTRX 'diversity' option
+ * build: Require and check for gcc C++11 support
+ * uhd: Use map container for for device parameter access
+ * sigProcLib: Remove unused functions from public interface
+ * uhd: Add non-UmTRX channel swap support
+ * uhd: Fix Tx-RX timing offset setting
+ * uhd: Fix USRP2/N200/N210 device detection
+ * transceiver: Fix POWEROFF crash on USRP2/N200/X300 devices
+ * sigProcLib: Fix complex/real vector flag in Laurent modulator
+ * sigProcLib: Remove heap based signal vector allocations
+ * common: Declare explicit Vector move constructor
+ * sigProcLib: Remove trigonometric tables
+ * sigProcLib: Use explicit NaN check in sinc table generation
+ * sigProcLib: Replace dynamically allocated resampling buffers
+ * sigProcLib: Specify standard namespace for isnan()
+ * uhd: Always specify samples-per-symbol for device lookup
+ * LimeSDR: set approximate tx offset value to make GSM work
+
+ [ Neels Hofmeyr ]
+ * add basic .gitignore
+ * configure.ac: check for boost/config.hpp header
+ * The INSTALL file is being overwritten by autoreconf, but it is committed as empty file. As a result, the INSTALL file always shows as modified. Instead, remove INSTALL from git and ignore it.
+ * add contrib/jenkins.sh, for gerrit build bot
+
+ [ pierre.baudry ]
+ * transceiver: Fix mismatched allocations and deallocations
+
+ [ Holger Hans Peter Freyther ]
+ * debian: Require fftw3 header files for osmo-trx
+
+ [ Max ]
+ * Add gerrit settings
+ * Integrate Debian packaging changes
+ * Remove embedded sqlite3
+ * Fix building against sqlite3
+ * Add autoconf-archive to dependencies
+ * debian: remove obsolete dependency
+ * deb: remove unused dependency
+ * Remove redundant explicit dependency
+ * Use release helper from libosmocore
+
+ [ Ruben Undheim ]
+ * Do not embed sqlite3 when building
+
+ [ Philipp Maier ]
+ * buildenv: Turn off native architecture builds
+ * cosmetic: Make parameter lists uniform
+ * Add test program to verify convolution implementation
+ * ssedetect: Add runtime CPU detection
+ * cosmetic: remove code duplication
+ * buildenv: Make build CPU invariant
+ * buildenv: Split up SSE3 and SSE4.1 code
+ * cosmetic: Add info about SSE support
+
+ [ Vadim Yanitskiy ]
+ * buildenv: correct the ax_sse macro description
+ * buildenv: actually strip unused cpuid functionality
+ * buildenv: fix build on systems without SIMD support
+ * buildenv: cosmetic changes
+ * buildenv: check for __builtin_cpu_supports call support
+ * ssedetect: call __builtin_cpu_supports() only if supported
+
+ [ Pau Espin Pedrol ]
+ * cosmetic: transciever: Remove trailing whitespaces
+ * transceiver: Avoid sending clock indications when trx is not powered on
+ * Add -j option to bind to specific address
+
+ [ ignasj ]
+ * LimeSDR: Change device detection to work with USB and PCIe versions
+ * LimeSDR: change tx window type to TX_WINDOW_FIXED
+ * LimeSDR: Fix sample value range
+
+ [ Harald Welte ]
+ * Add '-t' command line option to enable SCHED_RR
+ * Import git-version-gen and update AC_INIT()
+
+ -- Harald Welte <laforge@gnumonks.org> Sat, 28 Oct 2017 17:52:32 +0200
+
+osmo-trx (0.1.9) trusty; urgency=medium
+
+ * Ask Ivan, really
+
+ -- Kirill Zakharenko <earwin@gmail.com> Thu, 16 Jul 2015 12:13:46 +0000
+
+osmo-trx (0.1.8) precise; urgency=low
+
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP
+
+ -- Ivan Klyuchnikov <Ivan.Kluchnikov@fairwaves.ru> Sun, 9 Mar 2014 14:10:10 +0400
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..ffb9ac1
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,91 @@
+Source: osmo-trx
+Section: net
+Priority: optional
+Maintainer: Ivan Klyuchnikov <ivan.kluchnikov@fairwaves.ru>
+Build-Depends: debhelper (>= 9),
+ autotools-dev,
+ autoconf-archive,
+ pkg-config,
+ dh-autoreconf,
+ libuhd-dev,
+ libusb-1.0-0-dev,
+ libboost-all-dev,
+ libfftw3-dev,
+ libtalloc-dev,
+ libusrp-dev,
+ liblimesuite-dev,
+ libosmocore-dev (>= 0.10.0)
+Standards-Version: 3.9.6
+Vcs-Browser: http://cgit.osmocom.org/osmo-trx
+Vcs-Git: git://git.osmocom.org/osmo-trx
+Homepage: https://projects.osmocom.org/projects/osmotrx
+
+Package: osmo-trx
+Depends: osmo-trx-uhd
+Architecture: all
+Description: Metapackage for osmo-trx-uhd
+
+Package: osmo-trx-dbg
+Architecture: any
+Section: debug
+Priority: extra
+Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), ${misc:Depends}
+Description: Debug symbols for the osmo-trx-*
+ Make debugging possible
+
+Package: osmo-trx-uhd
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: SDR transceiver that implements Layer 1 of a GSM BTS (UHD)
+ OsmoTRX is a software-defined radio transceiver that implements the Layer 1
+ physical layer of a BTS comprising the following 3GPP specifications:
+ .
+ TS 05.01 "Physical layer on the radio path"
+ TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
+ TS 05.04 "Modulation"
+ TS 05.10 "Radio subsystem synchronization"
+ .
+ In this context, BTS is "Base transceiver station". It's the stations that
+ connect mobile phones to the mobile network.
+ .
+ 3GPP is the "3rd Generation Partnership Project" which is the collaboration
+ between different telecommunication associations for developing new
+ generations of mobile phone networks. (post-2G/GSM)
+
+Package: osmo-trx-usrp1
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: SDR transceiver that implements Layer 1 of a GSM BTS (USRP1)
+ OsmoTRX is a software-defined radio transceiver that implements the Layer 1
+ physical layer of a BTS comprising the following 3GPP specifications:
+ .
+ TS 05.01 "Physical layer on the radio path"
+ TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
+ TS 05.04 "Modulation"
+ TS 05.10 "Radio subsystem synchronization"
+ .
+ In this context, BTS is "Base transceiver station". It's the stations that
+ connect mobile phones to the mobile network.
+ .
+ 3GPP is the "3rd Generation Partnership Project" which is the collaboration
+ between different telecommunication associations for developing new
+ generations of mobile phone networks. (post-2G/GSM)
+
+Package: osmo-trx-lms
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
+ OsmoTRX is a software-defined radio transceiver that implements the Layer 1
+ physical layer of a BTS comprising the following 3GPP specifications:
+ .
+ TS 05.01 "Physical layer on the radio path"
+ TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
+ TS 05.04 "Modulation"
+ TS 05.10 "Radio subsystem synchronization"
+ .
+ In this context, BTS is "Base transceiver station". It's the stations that
+ connect mobile phones to the mobile network.
+ .
+ 3GPP is the "3rd Generation Partnership Project" which is the collaboration
+ between different telecommunication associations for developing new
+ generations of mobile phone networks. (post-2G/GSM)
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..b7790f2
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,147 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: OsmoTRX
+Source: http://cgit.osmocom.org/osmo-trx/
+Files-Excluded: Transceiver52M/std_inband.rbf
+
+Files: *
+Copyright: 2008-2013 Free Software Foundation
+ 2010 Kestrel Signal Processing, Inc.
+ 2010-2012 Range Networks, Inc.
+License: AGPL-3+
+
+Files: Transceiver52M/arm/*
+ Transceiver52M/x86/*
+ Transceiver52M/common/*
+ Transceiver52M/Resampler.cpp
+ Transceiver52M/Resampler.h
+ Transceiver52M/osmo-trx.cpp
+ Transceiver52M/radioInterfaceDiversity.cpp
+Copyright: 2012-2013 Thomas Tsou <tom@tsou.cc>
+License: LGPL-2.1+
+
+Files: config/ax_check_compile_flag.m4
+Copyright: 2008 Guido U. Draheim <guidod@gmx.de>
+ 2011 Maarten Bosmans <mkbosmans@gmail.com>
+License: GPL-3+
+
+Files: config/ax_gcc_x86_cpuid.m4
+Copyright: 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+ 2008 Matteo Frigo
+License: GPL-3+
+
+Files: config/ax_ext.m4
+Copyright: 2007 Christophe Tournayre <turn3r@users.sourceforge.net>
+ 2013 Michael Petch <mpetch@capp-sysware.com>
+License: license_for_ax_ext_m4
+
+Files: config/ax_gcc_x86_avx_xgetbv.m4
+Copyright: 2013 Michael Petch <mpetch@capp-sysware.com>
+License: GPL-3+
+
+Files: CommonLibs/Makefile.am
+ GSM/Makefile.am
+ Transceiver52M/Makefile.am
+ Transceiver52M/Transceiver.h
+ Transceiver52M/Transceiver.cpp
+Copyright: 2008-2010 Free Software Foundation
+ 2010-2012 Range Networks, Inc.
+License: GPL-3+
+
+Files: autogen.sh
+Copyright: 2005-2009 United States Government as represented by
+ the U.S. Army Research Laboratory.
+License: BSD-3-clause
+
+Files: debian/*
+Copyright: 2015 Ruben Undheim <ruben.undheim@gmail.com>
+License: GPL-3+
+
+
+License: AGPL-3+
+ 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/>.
+
+
+License: GPL-3+
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This package 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/>
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
+
+
+License: LGPL-2.1+
+ 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 program. If not, see
+ <http://www.gnu.org/licenses/>
+ .
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License version 2.1 can be found in
+ "/usr/share/common-licenses/LGPL-2.1".
+
+
+License: license_for_ax_ext_m4
+ Copying and distribution of this file, with or without modification, are
+ permitted in any medium without royalty provided the copyright notice
+ and this notice are preserved. This file is offered as-is, without any
+ warranty.
+
+
+License: BSD-3-clause
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ .
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ .
+ 2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ .
+ 3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/debian/osmo-trx-lms.install b/debian/osmo-trx-lms.install
new file mode 100644
index 0000000..b89506a
--- /dev/null
+++ b/debian/osmo-trx-lms.install
@@ -0,0 +1,4 @@
+etc/osmocom/osmo-trx-lms.cfg
+lib/systemd/system/osmo-trx-lms.service
+/usr/bin/osmo-trx-lms
+/usr/share/doc/osmo-trx/examples/osmo-trx-lms/osmo-trx-limesdr.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-lms/
diff --git a/debian/osmo-trx-uhd.install b/debian/osmo-trx-uhd.install
new file mode 100644
index 0000000..77236ca
--- /dev/null
+++ b/debian/osmo-trx-uhd.install
@@ -0,0 +1,6 @@
+etc/osmocom/osmo-trx-uhd.cfg
+lib/systemd/system/osmo-trx-uhd.service
+/usr/bin/osmo-trx-uhd
+/usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/
+/usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/
+/usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/
diff --git a/debian/osmo-trx-usrp1.install b/debian/osmo-trx-usrp1.install
new file mode 100644
index 0000000..112a149
--- /dev/null
+++ b/debian/osmo-trx-usrp1.install
@@ -0,0 +1,4 @@
+lib/systemd/system/osmo-trx-usrp1.service
+/usr/bin/osmo-trx-usrp1
+/usr/share/usrp/rev2/std_inband.rbf
+/usr/share/usrp/rev4/std_inband.rbf
diff --git a/debian/osmo-trx.install b/debian/osmo-trx.install
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/debian/osmo-trx.install
diff --git a/debian/patches/build-for-debian8.patch b/debian/patches/build-for-debian8.patch
new file mode 100644
index 0000000..cd1f356
--- /dev/null
+++ b/debian/patches/build-for-debian8.patch
@@ -0,0 +1,57 @@
+Index: osmo-trx/debian/control
+===================================================================
+--- osmo-trx.orig/debian/control
++++ osmo-trx/debian/control
+@@ -13,7 +13,6 @@ Build-Depends: debhelper (>= 9),
+ libfftw3-dev,
+ libtalloc-dev,
+ libusrp-dev,
+- liblimesuite-dev,
+ libosmocore-dev (>= 0.10.0)
+ Standards-Version: 3.9.6
+ Vcs-Browser: http://cgit.osmocom.org/osmo-trx
+@@ -29,7 +28,7 @@ Package: osmo-trx-dbg
+ Architecture: any
+ Section: debug
+ Priority: extra
+-Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), ${misc:Depends}
++Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), ${misc:Depends}
+ Description: Debug symbols for the osmo-trx-*
+ Make debugging possible
+
+@@ -70,22 +70,3 @@ Description: SDR transceiver that implem
+ 3GPP is the "3rd Generation Partnership Project" which is the collaboration
+ between different telecommunication associations for developing new
+ generations of mobile phone networks. (post-2G/GSM)
+-
+-Package: osmo-trx-lms
+-Architecture: any
+-Depends: ${shlibs:Depends}, ${misc:Depends}
+-Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
+- OsmoTRX is a software-defined radio transceiver that implements the Layer 1
+- physical layer of a BTS comprising the following 3GPP specifications:
+- .
+- TS 05.01 "Physical layer on the radio path"
+- TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
+- TS 05.04 "Modulation"
+- TS 05.10 "Radio subsystem synchronization"
+- .
+- In this context, BTS is "Base transceiver station". It's the stations that
+- connect mobile phones to the mobile network.
+- .
+- 3GPP is the "3rd Generation Partnership Project" which is the collaboration
+- between different telecommunication associations for developing new
+- generations of mobile phone networks. (post-2G/GSM)
+Index: osmo-trx/debian/rules
+===================================================================
+--- osmo-trx.orig/debian/rules
++++ osmo-trx/debian/rules
+@@ -9,7 +9,7 @@ override_dh_shlibdeps:
+ dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
+
+ override_dh_auto_configure:
+- dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system
++ dh_auto_configure -- --with-uhd --with-usrp1 --with-systemdsystemunitdir=/lib/systemd/system
+
+ override_dh_strip:
+ dh_strip --dbg-package=osmo-trx-dbg
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..82063b7
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+# build-for-debian8.patch
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..68de706
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,15 @@
+#!/usr/bin/make -f
+
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+
+%:
+ dh $@ --with autoreconf
+
+override_dh_shlibdeps:
+ dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
+
+override_dh_auto_configure:
+ dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system
+
+override_dh_strip:
+ dh_strip --dbg-package=osmo-trx-dbg
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..9f67427
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native) \ No newline at end of file
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..1d42b0a
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ examples \
+ $(NULL)
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
new file mode 100644
index 0000000..88d9142
--- /dev/null
+++ b/doc/examples/Makefile.am
@@ -0,0 +1,41 @@
+OSMOCONF_FILES =
+osmoconfdir = $(sysconfdir)/osmocom
+
+if DEVICE_UHD
+OSMOCONF_FILES += osmo-trx-uhd/osmo-trx-uhd.cfg
+endif
+
+# if DEVICE_USRP1
+# TODO: no usrp1 sample file yet
+# OSMOCONF_FILES += osmo-trx-usrp1/osmo-trx-usrp1.cfg
+# endif
+
+if DEVICE_LMS
+OSMOCONF_FILES += osmo-trx-lms/osmo-trx-lms.cfg
+endif
+
+osmoconf_DATA = $(OSMOCONF_FILES)
+EXTRA_DIST = $(OSMOCONF_FILES)
+
+CFG_FILES = find $(srcdir) -type f -name '*.cfg*' | sed -e 's,^$(srcdir),,'
+
+dist-hook:
+ for f in $$($(CFG_FILES)); do \
+ j="$(distdir)/$$f" && \
+ mkdir -p "$$(dirname $$j)" && \
+ $(INSTALL_DATA) $(srcdir)/$$f $$j; \
+ done
+
+install-data-hook:
+ for f in $$($(CFG_FILES)); do \
+ j="$(DESTDIR)$(docdir)/examples/$$f" && \
+ mkdir -p "$$(dirname $$j)" && \
+ $(INSTALL_DATA) $(srcdir)/$$f $$j; \
+ done
+
+uninstall-hook:
+ @$(PRE_UNINSTALL)
+ for f in $$($(CFG_FILES)); do \
+ j="$(DESTDIR)$(docdir)/examples/$$f" && \
+ $(RM) $$j; \
+ done
diff --git a/doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg b/doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg
new file mode 100644
index 0000000..e2c67d6
--- /dev/null
+++ b/doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg
@@ -0,0 +1,22 @@
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging timestamp 1
+ logging print file basename
+ logging level set-all info
+!
+line vty
+ no login
+!
+trx
+ bind-ip 127.0.0.1
+ remote-ip 127.0.0.1
+ base-port 5700
+ egprs disable
+ tx-sps 4
+ rx-sps 4
+ rt-prio 18
+ chan 0
+ tx-path BAND1
+ rx-path LNAW
diff --git a/doc/examples/osmo-trx-lms/osmo-trx-lms.cfg b/doc/examples/osmo-trx-lms/osmo-trx-lms.cfg
new file mode 120000
index 0000000..13ae04a
--- /dev/null
+++ b/doc/examples/osmo-trx-lms/osmo-trx-lms.cfg
@@ -0,0 +1 @@
+osmo-trx-limesdr.cfg \ No newline at end of file
diff --git a/doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg b/doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg
new file mode 100644
index 0000000..e2c67d6
--- /dev/null
+++ b/doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg
@@ -0,0 +1,22 @@
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging timestamp 1
+ logging print file basename
+ logging level set-all info
+!
+line vty
+ no login
+!
+trx
+ bind-ip 127.0.0.1
+ remote-ip 127.0.0.1
+ base-port 5700
+ egprs disable
+ tx-sps 4
+ rx-sps 4
+ rt-prio 18
+ chan 0
+ tx-path BAND1
+ rx-path LNAW
diff --git a/doc/examples/osmo-trx-uhd/osmo-trx-uhd.cfg b/doc/examples/osmo-trx-uhd/osmo-trx-uhd.cfg
new file mode 120000
index 0000000..11f2320
--- /dev/null
+++ b/doc/examples/osmo-trx-uhd/osmo-trx-uhd.cfg
@@ -0,0 +1 @@
+osmo-trx-usrp_b200.cfg \ No newline at end of file
diff --git a/doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg b/doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg
new file mode 100644
index 0000000..1b15b47
--- /dev/null
+++ b/doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg
@@ -0,0 +1,23 @@
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging timestamp 1
+ logging print file basename
+ logging level set-all info
+!
+line vty
+ no login
+!
+trx
+ bind-ip 127.0.0.1
+ remote-ip 127.0.0.1
+ base-port 5700
+ dev-args addr=192.168.10.2,pa=NONE,pa_power_max_dbm=23,fifo_ctrl_window=0,status_port=12345
+ egprs disable
+ tx-sps 4
+ rx-sps 4
+ rssi-offset 38
+ rt-prio 18
+ chan 0
+ chan 1
diff --git a/doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg b/doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg
new file mode 100644
index 0000000..7d02506
--- /dev/null
+++ b/doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg
@@ -0,0 +1,22 @@
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging timestamp 1
+ logging print file basename
+ logging level set-all info
+!
+line vty
+ no login
+!
+trx
+ bind-ip 127.0.0.1
+ remote-ip 127.0.0.1
+ base-port 5700
+ egprs disable
+ tx-sps 4
+ rx-sps 4
+ clock-ref external
+ rt-prio 18
+ chan 0
+
diff --git a/git-version-gen b/git-version-gen
new file mode 100755
index 0000000..42cf3d2
--- /dev/null
+++ b/git-version-gen
@@ -0,0 +1,151 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2010-01-28.01
+
+# Copyright (C) 2007-2010 Free Software Foundation, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+# produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+# presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+# a checked-out repository. Created with contents that were learned at
+# the last time autoconf was run, and used by git-version-gen. Must not
+# be present in either $(srcdir) or $(builddir) for git-version-gen to
+# give accurate answers during normal development with a checked out tree,
+# but must be present in a tarball when there is no version control system.
+# Therefore, it cannot be used in any dependencies. GNUmakefile has
+# hooks to force a reconfigure at distribution time to get the value
+# correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+# tarball. Usable in dependencies, particularly for files that don't
+# want to depend on config.h but do want to track version changes.
+# Delete this file prior to any autoconf run where you want to rebuild
+# files to pick up a version string change; and leave it stale to
+# minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+# [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+# echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+# echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+ 1) ;;
+ *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+tarball_version_file=$1
+nl='
+'
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+ v=`cat $tarball_version_file` || exit 1
+ case $v in
+ *$nl*) v= ;; # reject multi-line output
+ [0-9]*) ;;
+ *) v= ;;
+ esac
+ test -z "$v" \
+ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+ : # use $v
+elif
+ v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+ || git describe --abbrev=4 HEAD 2>/dev/null` \
+ && case $v in
+ [0-9]*) ;;
+ v[0-9]*) ;;
+ *) (exit 1) ;;
+ esac
+then
+ # Is this a new git that lists number of commits since the last
+ # tag or the previous older version that did not?
+ # Newer: v6.10-77-g0f8faeb
+ # Older: v6.10-g0f8faeb
+ case $v in
+ *-*-*) : git describe is okay three part flavor ;;
+ *-*)
+ : git describe is older two part flavor
+ # Recreate the number of commits and rewrite such that the
+ # result is the same as if we were using the newer version
+ # of git describe.
+ vtag=`echo "$v" | sed 's/-.*//'`
+ numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+ v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+ ;;
+ esac
+
+ # Change the first '-' to a '.', so version-comparing tools work properly.
+ # Remove the "g" in git describe's output string, to save a byte.
+ v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+else
+ v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+ '') ;;
+ *) # Append the suffix only if there isn't one already.
+ case $v in
+ *-dirty) ;;
+ *) v="$v-dirty" ;;
+ esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/tests/CommonLibs/BitVectorTest.cpp b/tests/CommonLibs/BitVectorTest.cpp
new file mode 100644
index 0000000..063138f
--- /dev/null
+++ b/tests/CommonLibs/BitVectorTest.cpp
@@ -0,0 +1,60 @@
+/*
+* 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 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;
+
+
+ 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;
+}
diff --git a/tests/CommonLibs/BitVectorTest.ok b/tests/CommonLibs/BitVectorTest.ok
new file mode 100644
index 0000000..f959a08
--- /dev/null
+++ b/tests/CommonLibs/BitVectorTest.ok
@@ -0,0 +1,8 @@
+15 15 240
+000011110000
+101011110000
+175
+111101010000
+ts=abcdefgh
+tp=0110000101100010011000110110010001100101011001100110011101101000000000
+ts=abcdefgh
diff --git a/tests/CommonLibs/InterthreadTest.cpp b/tests/CommonLibs/InterthreadTest.cpp
new file mode 100644
index 0000000..c131c2a
--- /dev/null
+++ b/tests/CommonLibs/InterthreadTest.cpp
@@ -0,0 +1,137 @@
+/*
+* 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 "Threads.h"
+#include "Interthread.h"
+#include <iostream>
+
+using namespace std;
+
+
+InterthreadQueue<int> gQ;
+InterthreadMap<int,int> gMap;
+
+int q_last_read_val = -1;
+int q_last_write_val;
+int m_last_read_val;
+int m_last_write_val;
+
+void* qWriter(void*)
+{
+ int *p;
+ for (int i=0; i<20; i++) {
+ p = new int;
+ *p = i;
+ CERR("queue write " << *p);
+ gQ.write(p);
+ q_last_write_val = i;
+ if (random()%2) sleep(1);
+ }
+ p = new int;
+ *p = -1;
+ gQ.write(p);
+ return NULL;
+}
+
+void* qReader(void*)
+{
+ bool done = false;
+ while (!done) {
+ int *p = gQ.read();
+ CERR("queue read " << *p);
+ if (*p<0) {
+ assert(q_last_read_val == 19 && *p == -1);
+ done = true;
+ } else {
+ assert(q_last_read_val == *p - 1);
+ q_last_read_val = *p;
+ }
+ delete p;
+ }
+ return NULL;
+}
+
+
+void* mapWriter(void*)
+{
+ int *p;
+ for (int i=0; i<20; i++) {
+ p = new int;
+ *p = i;
+ CERR("map write " << *p);
+ gMap.write(i,p);
+ m_last_write_val = i;
+ if (random()%2) sleep(1);
+ }
+ return NULL;
+}
+
+void* mapReader(void*)
+{
+ for (int i=0; i<20; i++) {
+ int *p = gMap.read(i);
+ CERR("map read " << *p);
+ assert(*p == i);
+ m_last_read_val = *p;
+ // InterthreadMap will delete the pointers
+ // delete p;
+ }
+ return NULL;
+}
+
+
+
+
+
+
+int main(int argc, char *argv[])
+{
+ Thread qReaderThread;
+ qReaderThread.start(qReader,NULL);
+ Thread mapReaderThread;
+ mapReaderThread.start(mapReader,NULL);
+
+ Thread qWriterThread;
+ qWriterThread.start(qWriter,NULL);
+ Thread mapWriterThread;
+ mapWriterThread.start(mapWriter,NULL);
+
+ qReaderThread.join();
+ qWriterThread.join();
+ mapReaderThread.join();
+ mapWriterThread.join();
+
+ assert(q_last_write_val == 19);
+ assert(q_last_read_val == 19);
+ assert(m_last_write_val == 19);
+ assert(m_last_read_val == 19);
+
+ printf("Done\n");
+}
+
+
+// vim: ts=4 sw=4
diff --git a/tests/CommonLibs/InterthreadTest.ok b/tests/CommonLibs/InterthreadTest.ok
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/tests/CommonLibs/InterthreadTest.ok
@@ -0,0 +1 @@
+Done
diff --git a/tests/CommonLibs/LogTest.cpp b/tests/CommonLibs/LogTest.cpp
new file mode 100644
index 0000000..5e51ce7
--- /dev/null
+++ b/tests/CommonLibs/LogTest.cpp
@@ -0,0 +1,69 @@
+/*
+* Copyright 2009 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 <iostream>
+#include <iterator>
+
+#include "Logger.h"
+extern "C" {
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include "debug.h"
+}
+
+#define MYCAT 0
+
+int main(int argc, char *argv[])
+{
+ struct log_info_cat categories[1];
+ struct log_info linfo;
+ categories[MYCAT] = {
+ "MYCAT",
+ NULL,
+ "Whatever",
+ LOGL_NOTICE,
+ 1,
+ };
+ linfo.cat = categories;
+ linfo.num_cat = ARRAY_SIZE(categories);
+
+ void *tall_ctx = talloc_named_const(NULL, 1, "OsmoTRX context");
+ msgb_talloc_ctx_init(tall_ctx, 0);
+
+ osmo_init_logging2(tall_ctx, &linfo);
+
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_level(osmo_stderr_target, 1);
+
+ Log(MYCAT, LOGL_FATAL, __BASE_FILE__, __LINE__).get() << "testing the logger.";
+ Log(MYCAT, LOGL_ERROR, __BASE_FILE__, __LINE__).get() << "testing the logger.";
+ Log(MYCAT, LOGL_NOTICE, __BASE_FILE__, __LINE__).get() << "testing the logger.";
+ Log(MYCAT, LOGL_INFO, __BASE_FILE__, __LINE__).get() << "testing the logger.";
+ Log(MYCAT, LOGL_DEBUG, __BASE_FILE__, __LINE__).get() << "testing the logger.";
+}
diff --git a/tests/CommonLibs/LogTest.err b/tests/CommonLibs/LogTest.err
new file mode 100644
index 0000000..153e850
--- /dev/null
+++ b/tests/CommonLibs/LogTest.err
@@ -0,0 +1,3 @@
+FATAL testing the logger.
+ERROR testing the logger.
+NOTICE testing the logger.
diff --git a/tests/CommonLibs/LogTest.ok b/tests/CommonLibs/LogTest.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/CommonLibs/LogTest.ok
diff --git a/tests/CommonLibs/Makefile.am b/tests/CommonLibs/Makefile.am
new file mode 100644
index 0000000..2a9a021
--- /dev/null
+++ b/tests/CommonLibs/Makefile.am
@@ -0,0 +1,46 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = -Wall -I$(top_srcdir)/CommonLibs $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -g
+AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
+
+EXTRA_DIST = BitVectorTest.ok \
+ PRBSTest.ok \
+ InterthreadTest.ok \
+ SocketsTest.ok \
+ TimevalTest.ok \
+ VectorTest.ok \
+ LogTest.ok \
+ LogTest.err
+
+noinst_PROGRAMS = \
+ BitVectorTest \
+ PRBSTest \
+ InterthreadTest \
+ SocketsTest \
+ TimevalTest \
+ VectorTest \
+ LogTest
+
+BitVectorTest_SOURCES = BitVectorTest.cpp
+BitVectorTest_LDADD = $(COMMON_LA)
+
+PRBSTest_SOURCES = PRBSTest.cpp
+
+InterthreadTest_SOURCES = InterthreadTest.cpp
+InterthreadTest_LDADD = $(COMMON_LA)
+InterthreadTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
+
+SocketsTest_SOURCES = SocketsTest.cpp
+SocketsTest_LDADD = $(COMMON_LA)
+SocketsTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
+
+TimevalTest_SOURCES = TimevalTest.cpp
+TimevalTest_LDADD = $(COMMON_LA)
+
+VectorTest_SOURCES = VectorTest.cpp
+VectorTest_LDADD = $(COMMON_LA)
+
+LogTest_SOURCES = LogTest.cpp
+LogTest_LDADD = $(COMMON_LA)
+
+MOSTLYCLEANFILES += testSource testDestination
diff --git a/tests/CommonLibs/PRBSTest.cpp b/tests/CommonLibs/PRBSTest.cpp
new file mode 100644
index 0000000..b83e93d
--- /dev/null
+++ b/tests/CommonLibs/PRBSTest.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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
+ */
+
+#include "PRBS.h"
+#include <iostream>
+#include <cstdlib>
+#include <assert.h>
+
+void testPrbs(PRBS &prbs, uint64_t expectedPeriod)
+{
+ uint64_t period = 0;
+ do {
+ std::cout << prbs.generateBit();
+ period++;
+ } while (!prbs.isFinished());
+ std::cout << std::endl;
+ std::cout << "Period: " << period << std::endl;
+ assert(period == expectedPeriod);
+}
+
+int main(int argc, char *argv[])
+{
+ PRBS9 prbs9(0x01);
+ testPrbs(prbs9, (1<<9)-1);
+ PRBS15 prbs15(0x01);
+ testPrbs(prbs15, (1<<15)-1);
+}
diff --git a/tests/CommonLibs/PRBSTest.ok b/tests/CommonLibs/PRBSTest.ok
new file mode 100644
index 0000000..d3fe378
--- /dev/null
+++ b/tests/CommonLibs/PRBSTest.ok
@@ -0,0 +1,4 @@
+1000010001100001001110010101011000011011110100110111001000101000010101101001111110110010010010110111111001001101010011001100000001100011001010001101001011111110100010110001110101100101100111100011111011101000001101011011011101100000101101011111010101010000001010010101111001011101110000001110011101001001111010111010100010010000110011100001011110110110011010000111011110000111111111000001111011111000101110011001000001001010011101101000111100111110011011000101010010001110001101101010111000100110001000100000000
+Period: 511
+1000000000000011000000000000101000000000001111000000000010001000000000110011000000001010101000000011111111000000100000001000001100000011000010100000101000111100001111001000100010001011001100110011101010101010100111111111111101000000000000111000000000001001000000000011011000000000101101000000001110111000000010011001000000110101011000001011111101000011100000111000100100001001001101100011011010110100101101111011101110110001100110011010010101010101110111111111110011000000000010101000000000111111000000001000001000000011000011000000101000101000001111001111000010001010001000110011110011001010100010101011111100111111100000101000000100001111000001100010001000010100110011000111101010101001000111111111011001000000001101011000000010111101000000111000111000001001001001000011011011011000101101101101001110110110111010011011011001110101101101010011110110111110100011011000011100101101000100101110111001101110011001010110010101011111010111111100001111000000100010001000001100110011000010101010101000111111111111001000000000001011000000000011101000000000100111000000001101001000000010111011000000111001101000001001010111000011011111001000101100001011001110100011101010011100100111110100101101000011101110111000100110011001001101010101011010111111111101111000000000110001000000001010011000000011110101000000100011111000001100100001000010101100011000111110100101001000011101111011000100110001101001101010010111010111110111001111000011001010001000101011110011001111100010101010000100111111110001101000000010010111000000110111001000001011001011000011101011101000100111100111001101000101001010111001111011111001010001100001011110010100011100010111100100100111000101101101001001110110111011010011011001101110101101010110011110111111010100011000001111100101000010000101111000110001110001001010010010011011110110110101100011011011110100101101100011101110110100100110011011101101010101100110111111110101011000000011111101000000100000111000001100001001000010100011011000111100101101001000101110111011001110011001101010010101010111110111111111000011000000001000101000000011001111000000101010001000001111110011000010000010101000110000111111001010001000001011110011000011100010101000100100111111001101101000001010110111000011111011001000100001101011001100010111101010100111000111111101001001000000111011011000001001101101000011010110111000101111011001001110001101011010010010111101110110111000110011011001001010101101011011111110111101100000011000110100000101001011100001111011100100010001100101100110010101110101010111110011111111000010100000001000111100000011001000100000101011001100001111101010100010000111111100110001000000101010011000001111110101000010000011111000110000100001001010001100011011110010100101100010111101110100111000110011101001001010100111011011111101001101100000111010110100001001111011100011010001100100101110010101101110010111110110010111000011010111001000101111001011001110001011101010010011100111110110100101000011011101111000101100110001001110101010011010011111110101110100000011110011100000100010100100001100111101100010101000110100111111001011101000001011100111000011100101001000100101111011001101110001101010110010010111111010110111000001111011001000010001101011000110010111101001010111000111011111001001001100001011011010100011101101111100100110110000101101011010001110111101110010011000110010110101001010111011111011111001100001100001010100010100011111100111100100000101000101100001111001110100010001010011100110011110100101010100011101111111100100110000000101101010000001110111110000010011000010000110101000110001011111001010011100001011110100100011100011101100100100100110101101101101011110110110111100011011011000100101101101001101110110111010110011011001111010101101010001111110111110010000011000010110000101000111010001111001001110010001011010010110011101110111010100110011001111101010101010000111111111110001000000000010011000000000110101000000001011111000000011100001000000100100011000001101100101000010110101111000111011110001001001100010011011010100110101101111101011110110000111100011010001000100101110011001101110010101010110010111111111010111000000001111001000000010001011000000110011101000001010100111000011111101001000100000111011001100001001101010100011010111111100101111000000101110001000001110010011000010010110101000110111011111001011001100001011101010100011100111111100100101000000101101111000001110110001000010011010011000110101110101001011110011111011100010100001100100111100010101101000100111110111001101000011001010111000101011111001001111100001011010000100011101110001100100110010010101101010110111110111111011000011000001101000101000010111001111000111001010001001001011110011011011100010101101100100111110110101101000011011110111000101100011001001110100101011010011101111101110100110000110011101010001010100111110011111101000010100000111000111100001001001000100011011011001100101101101010101110110111111110011011000000010101101000000111110111000001000011001000011000101011000101001111101001111010000111010001110001001110010010011010010110110101110111011011110011001101100010101010110100111111111011101000000001100111000000010101001000000111111011000001000001101000011000010111000101000111001001111001001011010001011011101110011101100110010100110101010111101011111111000111100000001001000100000011011001100000101101010100001110111111100010011000000100110101000001101011111000010111100001000111000100011001001001100101011011010101111101101111110000110110000010001011010000110011101110001010100110010011111101010110100000111111011100001000001100100011000010101100101000111110101111001000011110001011000100010011101001100110100111010101011101001111111100111010000000101001110000001111010010000010001110110000110010011010001010110101110011111011110010100001100010111100010100111000100111101001001101000111011010111001001101111001011010110001011101111010011100110001110100101010010011101111110110100110000011011101010000101100111110001110101000010010011111000110110100001001011011100011011101100100101100110101101110101011110110011111100011010100000100101111100001101110000100010110010001100111010110010101001111010111111010001111000001110010001000010010110011000110111010101001011001111111011101010000001100111110000010101000010000111111000110001000001001010011000011011110101000101100011111001110100100001010011101100011110100110100100011101011101100100111100110101101000101011110111001111100011001010000100101011110001101111100010010110000100110111010001101011001110010111101010010111000111110111001001000011001011011000101011101101001111100110111010000101011001110001111101010010010000111110110110001000011011010011000101101110101001110110011111010011010100001110101111100010011110000100110100010001101011100110010111100101010111000101111111001001110000001011010010000011101110110000100110011010001101010101110010111111110010111000000010111001000000111001011000001001011101000011011100111000101100101001001110101111011010011110001101110100010010110011100110111010100101011001111101111101010000110000111110001010001000010011110011000110100010101001011100111111011100101000001100101111000010101110001000111110010011001000010110101011000111011111101001001100000111011010100001001101111100011010110000100101111010001101110001110010110010010010111010110110111001111011011001010001101101011110010110111100010111011000100111001101001101001010111010111011111001111001100001010001010100011110011111100100010100000101100111100001110101000100010011111001100110100001010101011100011111111100100100000000101101100000001110110100000010011011100000110101100100001011110101100011100011110100100100100011101101101100100110110110101101011011011110111101101100011000110110100101001011011101111011101100110001100110101010010101011111110111111100000011000000100000101000001100001111000010100010001000111100110011001000101010101011001111111111101010000000000111110000000001000010000000011000110000000101001010000001111011110000010001100010000110010100110001010111101010011111000111110100001001000011100011011000100100101101001101101110111010110110011001111011010101010001101111111110010110000000010111010000000111001110000001001010010000011011110110000101100011010001110100101110010011101110010110100110010111011101010111001100111111001010101000001011111111000011100000001000100100000011001101100000101010110100001111111011100010000001100100110000010101101010000111110111110001000011000010011000101000110101001111001011111010001011100001110011100100010010100101100110111101110101011000110011111101001010100000111011111100001001100000100011010100001100101111100010101110000100111110010001101000010110010111000111010111001001001111001011011010001011101101110011100110110010100101011010111101111101111000110000110001001010001010011011110011110101100010100011110100111100100011101000101100100111001110101101001010011110111011110100011001100011100101010100100101111111101101110000000110110010000001011010110000011101111010000100110001110001101010010010010111110110110111000011011011001000101101101011001110110111101010011011000111110101101001000011110111011000100011001101001100101010111010101111111001111110000001010000010000011110000110000100010001010001100110011110010101010100010111111111100111000000000101001000000001111011000000010001101000000110010111000001010111001000011111001011000100001011101001100011100111010100100101001111101101111010000110110001110001011010010010011101110110110100110011011011101010101101100111111110110101000000011011111000000101100001000001110100011000010011100101000110100101111001011101110001011100110010011100101010110100101111111011101110000001100110010000010101010110000111111111010001000000001110011000000010010101000000110111111000001011000001000011101000011000100111000101001101001001111010111011010001111001101110010001010110010110011111010111010100001111001111100010001010000100110011110001101010100010010111111100110111000000101011001000001111101011000010000111101000110001000111001010011001001011110101011011100011111101100100100000110101101100001011110110100011100011011100100100101100101101101110101110110110011110011011010100010101101111100111110110000101000011010001111000101110010001001110010110011010010111010101110111001111110011001010000010101011110000111111100010001000000100110011000001101010101000010111111111000111000000001001001000000011011011000000101101101000001110110111000010011011001000110101101011001011110111101011100011000111100100101001000101101111011001110110001101010011010010111110101110111000011110011001000100010101011001100111111101010101000000111111111000001000000001000011000000011000101000000101001111000001111010001000010001110011000110010010101001010110111111011111011000001100001101000010100010111000111100111001001000101001011011001111011101101010001100110111110010101011000010111111101000111000000111001001000001001011011000011011101101000101100110111001110101011001010011111101011110100000111100011100001000100100100011001101101100101010110110101111111011011110000001101100010000010110100110000111011101010001001100111110011010101000010101111111000111110000001001000010000011011000110000101101001010001110111011110010011001100010110101010100111011111111101001100000000111010100000001001111100000011010000100000101110001100001110010010100010010110111100110111011000101011001101001111101010111010000111111001110001000001010010011000011110110101000100011011111001100101100001010101110100011111110011100100000010100101100000111101110100001000110011100011001010100100101011111101101111100000110110000100001011010001100011101110010100100110010111101101010111000110111111001001011000001011011101000011101100111000100110101001001101011111011010111100001101111000100010110001001100111010011010101001110101111111010011110000001110100010000010011100110000110100101010001011101111110011100110000010100101010000111101111110001000110000010011001010000110101011110001011111100010011100000100110100100001101011101100010111100110100111000101011101001001111100111011010000101001101110001111010110010010001111010110110010001111011010110010001101111010110010110001111010111010010001111001110110010001010011010110011110101111010100011110001111100100010010000101100110110001110101011010010011111101110110100000110011011100001010101100100011111110101100100000011110101100000100011110100001100100011100010101100100100111110101101101000011110110111000100011011001001100101101011010101110111101111110011000110000010101001010000111111011110001000001100010011000010100110101000111101011111001000111100001011001000100011101011001100100111101010101101000111111110111001000000011001011000000101011101000001111100111000010000101001000110001111011001010010001101011110110010111100011010111000100101111001001101110001011010110010011101111010110100110001111011101010010001100111110110010101000011010111111000101111000001001110001000011010010011000101110110101001110011011111010010101100001110111110100010011000011100110101000100101011111001101111100001010110000100011111010001100100001110010101100010010111110100110111000011101011001000100111101011001101000111101010111001000111111001011001000001011101011000011100111101000100101000111001101111001001010110001011011111010011101100001110100110100010011101011100110100111100101011101000101111100111001110000101001010010001111011110110010001100011010110010100101111010111101110001111000110010010001001010110110011011111011010101100001101111110100010110000011100111010000100101001110001101111010010010110001110110111010010011011001110110101101010011011110111110101100011000011110100101000100011101111001100100110001010101101010011111110111110100000011000011100000101000100100001111001101100010001010110100110011111011101010100001100111111100010101000000100111111000001101000001000010111000011000111001000101001001011001111011011101010001101100111110010110101000010111011111000111001100001001001010100011011011111100101101100000101110110100001110011011100010010101100100110111110101101011000011110111101000100011000111001100101001001010101111011011111110001101100000010010110100000110111011100001011001100100011101010101100100111111110101101000000011110111000000100011001000001100101011000010101111101000111110000111001000010001001011000110011011101001010101100111011111110101001100000011111010100000100001111100001100010000100010100110001100111101010010101000111110111111001000011000001011000101000011101001111000100111010001001101001110011010111010010101111001110111110001010011000010011110101000110100011111001011100100001011100101100011100101110100100101110011101101110010100110110010111101011010111000111101111001001000110001011011001010011101101011110100110111100011101011000100100111101001101101000111010110111001001111011001011010001101011101110010111100110010111000101010111001001111111001011010000001011101110000011100110010000100101010110001101111111010010110000001110111010000010011001110000110101010010001011111110110011100000011010100100000101111101100001110000110100010010001011100110110011100101011010100101111101111101110000110000110010001010001010110011110011111010100010100001111100111100010000101000100110001111001101010010001010111110110011111000011010100001000101111100011001110000100101010010001101111110110010110000011010111010000101111001110001110001010010010010011110110110110100011011011011100101101101100101110110110101110011011011110010101101100010111110110100111000011011101001000101100111011001110101001101010011111010111110100001111000011100010001000100100110011001101101010101010110111111111111011000000000001101000000000010111000000000111001000000001001011000000011011101000000101100111000001110101001000010011111011000110100001101001011100010111011100100111001100101101001010101110111011111110011001100000010101010100000111111111100001000000000100011000000001100101000000010101111000000111110001000001000010011000011000110101000101001011111001111011100001010001100100011110010101100100010111110101100111000011110101001000100011111011001100100001101010101100010111111110100111000000011101001000000100111011000001101001101000010111010111000111001111001001001010001011011011110011101101100010100110110100111101011011101000111101100111001000110101001011001011111011101011100001100111100100010101000101100111111001110101000001010011111000011110100001000100011100011001100100100101010101101101111111110110110000000011011010000000101101110000001110110010000010011010110000110101111010001011110001110011100010010010100100110110111101101011011000110111101101001011000110111011101001011001100111011101010101001100111111111010101000000001111111000000010000001000000110000011000001010000101000011110001111000100010010001001100110110011010101011010101111111101111110000000110000010000001010000110000011110001010000100010011110001100110100010010101011100110111111100101011000000101111101000001110000111000010010001001000110110011011001011010101101011101111110111100110000011000101010000101001111110001111010000010010001110000110110010010001011010110110011101111011010100110001101111101010010110000111110111010001000011001110011000101010010101001111110111111010000011000001110000101000010010001111000110110010001001011010110011011101111010101100110001111110101010010000011111110110000100000011010001100000101110010100001110010111100010010111000100110111001001101011001011010111101011101111000111100110001001000101010011011001111110101101010000011110111110000100011000010001100101000110010101111001010111110001011111000010011100001000110100100011001011101100101011100110101111100101011110000101111100010001110000100110010010001101010110110010111111011010111000001101111001000010110001011000111010011101001001110100111011010011101001101110100111010110011101001111010100111010001111101001110010000111010010110001001110111010011010011001110101110101010011110011111110100010100000011100111100000100101000100001101111001100010110001010100111010011111101001110100000111010011100001001110100100011010011101100101110100110101110011101011110010100111100010111101000100111000111001101001001001010111011011011111001101101100001010110110100011111011011100100001101100101100010110101110100111011110011101001100010100111010100111101001111101000111010000111001001110001001011010010011011101110110101100110011011110101010101100011111111110100100000000011101100000000100110100000001101011100000010111100100000111000101100001001001110100011011010011100101101110100101110110011101110011010100110010101111101010111110000111111000010001000001000110011000011001010101000101011111111001111100000001010000100000011110001100000100010010100001100110111100010101011000100111111101001101000000111010111000001001111001000011010001011000101110011101001110010100111010010111101001110111000111010011001001001110101011011010011111101101110100000110110011100001011010100100011101111101100100110000110101101010001011110111110011100011000010100100101000111101101111001000110110001011001011010011101011101110100111100110011101000101010100111001111111101001010000000111011110000001001100010000011010100110000101111101010001110000111110010010001000010110110011000111011010101001001101111111011010110000001101111010000010110001110000111010010010001001110110110011010011011010101110101101111110011110110000010100011010000111100101110001000101110010011001110010110101010010111011111110111001100000011001010100000101011111100001111100000100010000100001100110001100010101010010100111111110111101000000011000111000000101001001000001111011011000010001101101000110010110111001010111011001011111001101011100001010111100100011111000101100100001001110101100011010011110100101110100011101110011100100110010100101101010111101110111111000110011000001001010101000011011111111000101100000001001110100000011010011100000101110100100001110011101100010010100110100110111101011101011000111100111101001000101000111011001111001001101010001011010111110011101111000010100110001000111101010011001000111110101011001000011111101011000100000111101001100001000111010100011001001111100101011010000101111101110001110000110010010010001010110110110011111011011010100001101101111100010110110000100111011010001101001101110010111010110010111001111010111001010001111001011110010001011100010110011100100111010100101101001111101110111010000110011001110001010101010010011111111110110100000000011011100000000101100100000001110101100000010011110100000110100011100001011100100100011100101101100100101110110101101110011011110110010101100011010111110100101111000011101110001000100110010011001101010110101010111111011111111000001100000001000010100000011000111100000101001000100001111011001100010001101010100110010111111101010111000000111111001000001000001011000011000011101000101000100111001111001101001010001010111011110011111001100010100001010100111100011111101000100100000111001101100001001010110100011011111011100101100001100101110100010101110011100111110010100101000010111101111000111000110001001001001010011011011011110101101101100011110110110100100011011011101100101101100110101110110101011110011011111100010101100000100111110100001101000011100010111000100100111001001101101001011010110111011101111011001100110001101010101010010111111111110111000000000011001000000000101011000000001111101000000010000111000000110001001000001010011011000011110101101000100011110111001100100011001010101100101011111110101111100000011110000100000100010001100001100110010100010101010111100111111111000101000000001001111000000011010001000000101110011000001110010101000010010111111000110111000001001011001000011011101011000101100111101001110101000111010011111001001110100001011010011100011101110100100100110011101101101010100110110111111101011011000000111101101000001000110111000011001011001000101011101011001111100111101010000101000111110001111001000010010001011000110110011101001011010100111011101111101001100110000111010101010001001111111110011010000000010101110000000111110010000001000010110000011000111010000101001001110001111011010010010001101110110110010110011011010111010101101111001111110110001010000011010011110000101110100010001110011100110010010100101010110111101111111011000110000001101001010000010111011110000111001100010001001010100110011011111101010101100000111111110100001000000011100011000000100100101000001101101111000010110110001000111011010011001001101110101011010110011111101111010100000110001111100001010010000100011110110001100100011010010101100101110111110101110011000011110010101000100010111111001100111000001010101001000011111111011000100000001101001100000010111010100000111001111100001001010000100011011110001100101100010010101110100110111110011101011000010100111101000111101000111001000111001001011001001011011101011011101100111101100110101000110101011111001011111100001011100000100011100100001100100101100010101101110100111110110011101000011010100111000101111101001001110000111011010010001001101110110011010110011010101111010101111110001111110000010010000010000110110000110001011010001010011101110011110100110010100011101010111100100111111000101101000001001110111000011010011001000101110101011001110011111101010010100000111110111100001000011000100011000101001100101001111010101111010001111110001110010000010010010110000110110111010001011011001110011101101010010100110111110111101011000011000111101000101001000111001111011001001010001101011011110010111101100010111000110100111001001011101001011011100111011101100101001100110101111010101011110001111111100010010000000100110110000001101011010000010111101110000111000110010001001001010110011011011111010101101100001111110110100010000011011100110000101100101010001110101111110010011110000010110100010000111011100110001001100101010011010101111110101111110000011110000010000100010000110001100110001010010101010011110111111110100011000000011100101000000100101111000001101110001000010110010011000111010110101001001111011111011010001100001101110010100010110010111100111010111000101001111001001111010001011010001110011101110010010100110010110111101010111011000111111001101001000001010111011000011111001101000100001010111001100011111001010100100001011111101100011100000110100100100001011101101100011100110110100100101011011101101111101100110110000110101011010001011111101110011100000110010100100001010111101100011111000110100100001001011101100011011100110100101100101011101110101111100110011110000101010100010001111111100110010000000101010110000001111111010000010000001110000110000010010001010000110110011110001011010100010011101111100110100110000101011101010001111100111110010000101000010110001111000111010010001001001110110011011010011010101101110101111110110011110000011010100010000101111100110001110000101010010010001111110110110010000011011010110000101101111010001110110001110010011010010010110101110110111011110011011001100010101101010100111110111111101000011000000111000101000001001001111000011011010001000101101110011001110110010101010011010111111110101111000000011110001000000100010011000001100110101000010101011111000111111100001001000000100011011000001100101101000010101110111000111110011001001000010101011011000111111101101001000000110111011000001011001101000011101010111000100111111001001101000001011010111000011101111001000100110001011001101010011101010111110100111111000011101000001000100111000011001101001000101010111011001111111001101010000001010111110000011111000010000100001000110001100011001010010100101011110111101111100011000110000100101001010001101111011110010110001100010111010010100111001110111101001010011000111011110101001001100011111011010100100001101111101100010110000110100111010001011101001110011100111010010100101001110111101111010011000110001110101001010010011111011110110100001100011011100010100101100100111101110101101000110011110111001010100011001011111100101011100000101111100100001110000101100010010001110100110110010011101011010110100111101111011101000110001100111001010010101001011110111111011100011000001100100101000010101101111000111110110001001000011010011011000101110101101001110011110111010010100011001110111100101010011000101111110101001110000011111010010000100001110110001100010011010010100110101110111101011110011000111100010101001000100111111011001101000001101010111000010111111001000111000001011001001000011101011011000100111101101001101000110111010111001011001111001011101010001011100111110011100101000010100101111000111101110001001000110010011011001010110101101011111011110111100001100011000100010100101001100111101111010101000110001111111001010010000001011110110000011100011010000100100101110001101101110010010110110010110111011010111011001101111001101010110001010111111010011111000001110100001000010011100011000110100100101001011101101111011100110110001100101011010010101111101110111110000110011000010001010101000110011111111001010100000001011111100000011100000100000100100001100001101100010100010110100111100111011101000101001100111001111010101001010001111111011110010000001100010110000010100111010000111101001110001000111010010011001001110110101011010011011111101110101100000110011110100001010100011100011111100100100100000101101101100001110110110100010011011011100110101101100101011110110101111100011011110000100101100010001101110100110010110011101010111010100111111001111101000001010000111000011110001001000100010011011001100110101101010101011110111111111100011000000000100101000000001101111000000010110001000000111010011000001001110101000011010011111000101110100001001110011100011010010100100101110111101101110011000110110010101001011010111111011101111000001100110001000010101010011000111111110101001000000011111011000000100001101000001100010111000010100111001000111101001011001000111011101011001001100111101011010101000111101111111001000110000001011001010000011101011110000100111100010001101000100110010111001101010111001010111111001011111000001011100001000011100100011000100101100101001101110101111010110011110001111010100010010001111100110110010000101011010110001111101111010010000110001110110001010010011010011110110101110100011011110011100101100010100101110100111101110011101000110010100111001010111101001011111000111011100001001001100100011011010101100101101111110101110110000011110011010000100010101110001100111110010010101000010110111111000111011000001001001101000011011010111000101101111001001110110001011010011010011101110101110100110011110011101010100010100111111100111101000000101000111000001111001001000010001011011000110011101101001010100110111011111101011001100000111101010100001000111111100011001000000100101011000001101111101000010110000111000111010001001001001110011011011010010101101101110111110110110011000011011010101000101101111111001110110000001010011010000011110101110000100011110010001100100010110010101100111010111110101001111000011111010001000100001110011001100010010101010100110111111111101011000000000111101000000001000111000000011001001000000101011011000001111101101000010000110111000110001011001001010011101011011110100111101100011101000110100100111001011101101001011100110111011100101011001100101111101010101110000111111110010001000000010110011000000111010101000001001111111000011010000001000101110000011001110010000101010010110001111110111010010000011001110110000101010011010001111110101110010000011110010110000100010111010001100111001110010101001010010111111011110111000001100011001000010100101011000111101111101001000110000111011001010001001101011110011010111100010101111000100111110001001101000010011010111000110101111001001011110001011011100010011101100100110100110101101011101011110111100111100011000101000100101001111001101111010001010110001110011111010010010100001110110111100010011011000100110101101001101011110111010111100011001111000100101010001001101111110011010110000010101111010000111110001110001000010010010011000110110110101001011011011111011101101100001100110110100010101011011100111111101100101000000110101111000001011110001000011100010011000100100110101001101101011111010110111100001111011000100010001101001100110010111010101010111001111111111001010000000001011110000000011100010000000100100110000001101101010000010110111110000111011000010001001101000110011010111001010101111001011111110001011100000010011100100000110100101100001011101110100011100110011100100101010100101101111111101110110000000110011010000001010101110000011111110010000100000010110001100000111010010100001001110111100011010011000100101110101001101110011111010110010100001111010111100010001111000100110010001001101010110011010111111010101111000001111110001000010000010011000110000110101001010001011111011110011100001100010100100010100111101100111101000110101000111001011111001001011100001011011100100011101100101100100110101110101101011110011110111100010100011000100111100101001101000101111010111001110001111001010010010001011110110110011100011011010100100101101111101101110110000110110011010001011010101110011101111110010100110000010111101010000111000111110001001001000010011011011000110101101101001011110110111011100011011001100100101101010101101110111111110110011000000011010101000000101111111000001110000001000010010000011000110110000101001011010001111011101110010001100110010110010101010111010111111111001111000000001010001000000011110011000000100010101000001100111111000010101000001000111111000011001000001000101011000011001111101000101010000111001111110001001010000010011011110000110101100010001011110100110011100011101010100100100111111101101101000000110110111000001011011001000011101101011000100110111101001101011000111010111101001001111000111011010001001001101110011011010110010101101111010111110110001111000011010010001000101110110011001110011010101010010101111111110111110000000011000010000000101000110000001111001010000010001011110000110011100010001010100100110011111101101010100000110111111100001011000000100011101000001100100111000010101101001000111110111011001000011001101011000101010111101001111111000111010000001001001110000011011010010000101101110110001110110011010010011010101110110101111110011011110000010101100010000111110100110001000011101010011000100111110101001101000011111010111000100001111001001100010001011010100110011101111101010100110000111111101010001000000111110011000001000010101000011000111111000101001000001001111011000011010001101000101110010111001110010111001010010111001011110111001011100011001011100100101011100101101111100101110110000101110011010001110010101110010010111110010110111000010111011001000111001101011001001010111101011011111000111101100001001000110100011011001011100101101011100101110111100101110011000101110010101001110010111111010010111000001110111001000010011001011000110101011101001011111100111011100000101001100100001111010101100010001111110100110010000011101010110000100111111010001101000001110010111000010010111001000110111001011001011001011101011101011100111100111100101000101000101111001111001110001010001010010011110011110110100010100011011100111100101100101000101110101111001110011110001010010100010011110111100110100011000101011100101001111100101111010000101110001110001110010010010010010110110110110111011011011011001101101101101010110110110111111011011011000001101101101000010110110111000111011011001001001101101011011010110111101101111011000110110001101001011010010111011101110111001100110011001010101010101011111111111111100000000000000
+Period: 32767
diff --git a/tests/CommonLibs/SocketsTest.cpp b/tests/CommonLibs/SocketsTest.cpp
new file mode 100644
index 0000000..e4eef54
--- /dev/null
+++ b/tests/CommonLibs/SocketsTest.cpp
@@ -0,0 +1,101 @@
+/*
+* 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 "Sockets.h"
+#include "Threads.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+
+static const int gNumToSend = 10;
+
+static void sigalarm_handler(int foo)
+{
+ printf("FAIL: test did not run successfully\n");
+ exit(EXIT_FAILURE);
+}
+
+void *testReaderIP(void *param)
+{
+ UDPSocket *readSocket = (UDPSocket *)param;
+ readSocket->nonblocking();
+ int rc = 0;
+ while (rc<gNumToSend) {
+ char buf[MAX_UDP_LENGTH+1] = { 0 };
+ int count = readSocket->read(buf, MAX_UDP_LENGTH);
+ if (count>0) {
+ buf[count] = 0;
+ CERR("read: " << buf);
+ rc++;
+ } else {
+ sleep(2);
+ }
+ }
+ return NULL;
+}
+
+int main(int argc, char * argv[] )
+{
+ int count;
+
+ if (signal(SIGALRM, sigalarm_handler) == SIG_ERR) {
+ perror("signal");
+ exit(EXIT_FAILURE);
+ }
+
+ /* If the test takes longer than 2*gNumToSend seconds, abort it */
+ alarm(2* gNumToSend);
+
+ UDPSocket readSocket("127.0.0.1", 0);
+ UDPSocket socket1("127.0.0.1", 0, "localhost", readSocket.port());
+
+ CERR("socket1: " << socket1.port() << ", readSocket: " << readSocket.port());
+
+ Thread readerThreadIP;
+ readerThreadIP.start(testReaderIP, &readSocket);
+
+ // give the readers time to open
+ sleep(1);
+
+ for (int i=0; i<gNumToSend; i++) {
+ CERR("write");
+ count = socket1.write("Hello IP land");
+ if (count < 0) {
+ COUT("FAIL: write");
+ exit(EXIT_FAILURE);
+ }
+ sleep(1);
+ }
+
+ readerThreadIP.join();
+
+ printf("Done\n");
+}
+
+// vim: ts=4 sw=4
diff --git a/tests/CommonLibs/SocketsTest.ok b/tests/CommonLibs/SocketsTest.ok
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/tests/CommonLibs/SocketsTest.ok
@@ -0,0 +1 @@
+Done
diff --git a/tests/CommonLibs/TimevalTest.cpp b/tests/CommonLibs/TimevalTest.cpp
new file mode 100644
index 0000000..61a92c9
--- /dev/null
+++ b/tests/CommonLibs/TimevalTest.cpp
@@ -0,0 +1,64 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+*
+* This software is distributed under the terms of the GNU Affero Public License.
+* See the COPYING file in the main directory for details.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#include "Timeval.h"
+#include <iostream>
+#include <assert.h>
+#include <sys/time.h>
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ long last_remaining = 10000;
+ Timeval then(last_remaining);
+ assert(then.elapsed() == -last_remaining);
+ cerr << then << " elapsed: " << then.elapsed() << endl;
+
+ /* Check that last_remaining parameter affects setting time in the future */
+ usleep(10000);
+ double increased_time_secs = Timeval().seconds();
+ assert(increased_time_secs <= then.seconds());
+
+ struct timespec invariant_time = then.timespec();
+ int loops = 0;
+
+ while (!then.passed()) {
+ struct timespec tspecnow = then.timespec();
+ cerr << "now: " << Timeval().seconds() << " then: " << then << " remaining: " << then.remaining() << endl;
+ assert(last_remaining >= then.remaining());
+ assert(tspecnow.tv_sec == invariant_time.tv_sec && tspecnow.tv_nsec == invariant_time.tv_nsec);
+ usleep(500000);
+ loops++;
+ }
+ cerr << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
+ assert(then.remaining() <= 0);
+ assert(loops >= 18);
+
+ printf("Done\n");
+}
diff --git a/tests/CommonLibs/TimevalTest.ok b/tests/CommonLibs/TimevalTest.ok
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/tests/CommonLibs/TimevalTest.ok
@@ -0,0 +1 @@
+Done
diff --git a/tests/CommonLibs/VectorTest.cpp b/tests/CommonLibs/VectorTest.cpp
new file mode 100644
index 0000000..6958889
--- /dev/null
+++ b/tests/CommonLibs/VectorTest.cpp
@@ -0,0 +1,63 @@
+/*
+* 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 "Vector.h"
+#include <iostream>
+
+using namespace std;
+
+typedef Vector<int> TestVector;
+
+int main(int argc, char *argv[])
+{
+ TestVector test1(5);
+ for (int i=0; i<5; i++) test1[i]=i;
+ TestVector test2(5);
+ for (int i=0; i<5; i++) test2[i]=10+i;
+
+ cout << test1 << endl;
+ cout << test2 << endl;
+
+ {
+ TestVector testC(test1,test2);
+ cout << testC << endl;
+ cout << testC.head(3) << endl;
+ cout << testC.tail(3) << endl;
+ testC.fill(8);
+ cout << testC << endl;
+ test1.copyToSegment(testC,3);
+ cout << testC << endl;
+
+ TestVector testD(testC.segment(4,3));
+ cout << testD << endl;
+ testD.fill(9);
+ cout << testC << endl;
+ cout << testD << endl;
+ }
+
+ return 0;
+}
diff --git a/tests/CommonLibs/VectorTest.ok b/tests/CommonLibs/VectorTest.ok
new file mode 100644
index 0000000..afe5162
--- /dev/null
+++ b/tests/CommonLibs/VectorTest.ok
@@ -0,0 +1,10 @@
+0 1 2 3 4
+10 11 12 13 14
+0 1 2 3 4 10 11 12 13 14
+0 1 2
+3 4 10 11 12 13 14
+8 8 8 8 8 8 8 8 8 8
+8 8 8 0 1 2 3 4 8 8
+1 2 3
+8 8 8 0 9 9 9 4 8 8
+9 9 9
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..d4589a4
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,41 @@
+SUBDIRS = \
+ CommonLibs \
+ Transceiver52M \
+ $(NULL)
+
+# The `:;' works around a Bash 3.2 bug when the output is not writeable.
+$(srcdir)/package.m4: $(top_srcdir)/configure.ac
+ :;{ \
+ echo '# Signature of the current package.' && \
+ echo 'm4_define([AT_PACKAGE_NAME],' && \
+ echo ' [$(PACKAGE_NAME)])' && \
+ echo 'm4_define([AT_PACKAGE_TARNAME],' && \
+ echo ' [$(PACKAGE_TARNAME)])' && \
+ echo 'm4_define([AT_PACKAGE_VERSION],' && \
+ echo ' [$(PACKAGE_VERSION)])' && \
+ echo 'm4_define([AT_PACKAGE_STRING],' && \
+ echo ' [$(PACKAGE_STRING)])' && \
+ echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
+ echo ' [$(PACKAGE_BUGREPORT)])'; \
+ echo 'm4_define([AT_PACKAGE_URL],' && \
+ echo ' [$(PACKAGE_URL)])'; \
+ } >'$(srcdir)/package.m4'
+
+EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)
+TESTSUITE = $(srcdir)/testsuite
+DISTCLEANFILES = atconfig $(NULL)
+
+check-local: atconfig $(TESTSUITE)
+ $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
+
+installcheck-local: atconfig $(TESTSUITE)
+ $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' $(TESTSUITEFLAGS)
+
+clean-local:
+ test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' --clean
+
+AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
+ $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+ mv $@.tmp $@
diff --git a/tests/Transceiver52M/Makefile.am b/tests/Transceiver52M/Makefile.am
new file mode 100644
index 0000000..06db5b0
--- /dev/null
+++ b/tests/Transceiver52M/Makefile.am
@@ -0,0 +1,18 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CFLAGS = -Wall -I$(top_srcdir)/Transceiver52M -I$(top_srcdir)/Transceiver52M/arch/common $(STD_DEFINES_AND_INCLUDES) -g
+
+EXTRA_DIST = convolve_test.ok
+
+noinst_PROGRAMS = \
+ convolve_test
+
+convolve_test_SOURCES = convolve_test.c
+convolve_test_CFLAGS = $(AM_CFLAGS)
+convolve_test_LDADD = $(COMMON_LA) $(ARCH_LA)
+if HAVE_SSE3
+convolve_test_CFLAGS += $(SIMD_FLAGS)
+endif
+if HAVE_SSE4_1
+convolve_test_CFLAGS += $(SIMD_FLAGS)
+endif
diff --git a/tests/Transceiver52M/convolve_test.c b/tests/Transceiver52M/convolve_test.c
new file mode 100644
index 0000000..54bc7a1
--- /dev/null
+++ b/tests/Transceiver52M/convolve_test.c
@@ -0,0 +1,150 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "convolve.h"
+
+#define TESTVEC_LEN 1000
+#define DO_INIT 1
+
+float x_vect[TESTVEC_LEN];
+float y_vect[TESTVEC_LEN];
+float h_vect[TESTVEC_LEN];
+
+float *x;
+float *h;
+float *y;
+
+/* Generate some random values for testing */
+void gen_floats(float *vect, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ vect[i] = (float)rand()/(float)(RAND_MAX);
+ }
+}
+
+/* Reset testvectors */
+static void reset_testvec(int seed)
+{
+ srand(seed);
+ memset(x_vect,0,sizeof(x_vect));
+ memset(y_vect,0,sizeof(y_vect));
+ memset(h_vect,0,sizeof(h_vect));
+
+ x=x_vect + TESTVEC_LEN/2;
+ y=y_vect + TESTVEC_LEN/2;
+ h=h_vect + TESTVEC_LEN/2;
+
+ gen_floats(x_vect,TESTVEC_LEN);
+ gen_floats(h_vect,TESTVEC_LEN);
+}
+
+/* Show float vector data cut and paste friendly */
+static void dump_floats(float *vect, int len, char *name)
+{
+ int i;
+
+ printf("float %s[] = {", name);
+ for(i = 0; i < len; i++) {
+
+ printf("%f",vect[i]);
+
+ if(i<len-1)
+ printf(",");
+ }
+ printf("}\n");
+}
+
+/* Test complex convolution */
+static void test_convolve_complex(int h_len)
+{
+ int x_len;
+ int y_len;
+ int start;
+ int len;
+ int step;
+ int offset;
+
+ x_len=34;
+ y_len=26;
+ start=8;
+ len=26;
+ step=1;
+ offset=1;
+ reset_testvec(0);
+ dump_floats(x,x_len,"x");
+ printf("\n");
+ dump_floats(h,h_len,"h");
+ printf("\n");
+ convolve_complex(x, x_len, h, h_len, y, y_len, start, len, step, offset);
+ dump_floats(y,y_len,"y");
+ printf("\n");
+}
+
+/* Test real convolution */
+static void test_convolve_real(int h_len)
+{
+ int x_len;
+ int y_len;
+ int start;
+ int len;
+ int step;
+ int offset;
+
+ x_len=34;
+ y_len=26;
+ start=8;
+ len=26;
+ step=1;
+ offset=1;
+ reset_testvec(0);
+ dump_floats(x,x_len,"x");
+ printf("\n");
+ dump_floats(h,h_len,"h");
+ printf("\n");
+ convolve_real(x, x_len, h, h_len, y, y_len, start, len, step, offset);
+ dump_floats(y,y_len,"y");
+ printf("\n");
+}
+
+int main(void)
+{
+#if DO_INIT == 1
+ convolve_init();
+#endif
+
+ printf("==== TEST COMPLEX BASE IMPLEMENTATION ====\n");
+ test_convolve_complex(17);
+
+ printf("==== TEST COMPLEX SSE3 IMPLEMENTATION: (h_len%%4=0) ====\n");
+ test_convolve_complex(20);
+
+ printf("==== TEST COMPLEX SSE3 IMPLEMENTATION: (h_len%%8=0) ====\n");
+ test_convolve_complex(16);
+
+ printf("\n");
+ printf("\n");
+
+ printf("==== TEST REAL BASE IMPLEMENTATION ====\n");
+ test_convolve_real(17);
+
+ printf("==== TEST REAL SSE3 IMPLEMENTATION (hlen=4) ====\n");
+ test_convolve_real(4);
+
+ printf("==== TEST REAL SSE3 IMPLEMENTATION (hlen=8) ====\n");
+ test_convolve_real(8);
+
+ printf("==== TEST REAL SSE3 IMPLEMENTATION (hlen=12) ====\n");
+ test_convolve_real(12);
+
+ printf("==== TEST REAL SSE3 IMPLEMENTATION (hlen=16) ====\n");
+ test_convolve_real(16);
+
+ printf("==== TEST REAL SSE3 IMPLEMENTATION (hlen=20) ====\n");
+ test_convolve_real(20);
+
+ printf("==== TEST REAL SSE3 IMPLEMENTATION (h_len%%4=0) ====\n");
+ test_convolve_real(24);
+
+ return 0;
+}
diff --git a/tests/Transceiver52M/convolve_test.ok b/tests/Transceiver52M/convolve_test.ok
new file mode 100644
index 0000000..5766252
--- /dev/null
+++ b/tests/Transceiver52M/convolve_test.ok
@@ -0,0 +1,72 @@
+==== TEST COMPLEX BASE IMPLEMENTATION ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771,0.590649,0.313560,0.547613,0.946811,0.793753,0.690502,0.276120,0.792995,0.446645}
+
+float y[] = {0.389293,10.824917,-0.676577,10.619646,0.283489,11.279525,0.384482,11.586230,0.711259,11.540458,-0.391531,11.281723,0.019900,12.278080,-0.070459,11.104558,0.087938,11.825965,-1.003252,11.698885,0.358887,11.911197,-0.678904,11.933812,0.245140,11.886644}
+
+==== TEST COMPLEX SSE3 IMPLEMENTATION: (h_len%4=0) ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771,0.590649,0.313560,0.547613,0.946811,0.793753,0.690502,0.276120,0.792995,0.446645,0.327805,0.785346,0.676628}
+
+float y[] = {-0.641594,12.367426,-0.970113,12.963129,-0.466783,13.747334,0.637486,13.341836,-0.168561,14.091346,0.306652,15.018833,0.233741,14.726789,-0.011241,15.034849,0.000155,13.639509,0.558827,15.495646,-0.406179,14.103148,-0.000244,15.591370,-0.492319,14.785577}
+
+==== TEST COMPLEX SSE3 IMPLEMENTATION: (h_len%8=0) ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771,0.590649,0.313560,0.547613,0.946811,0.793753,0.690502,0.276120,0.792995}
+
+float y[] = {-0.278295,10.097409,0.919633,11.502825,0.340383,10.979163,0.891132,11.679869,0.425363,11.186544,1.099703,12.121126,0.188196,11.180099,0.228905,12.436676,0.149904,11.522589,0.543155,11.703615,0.033465,12.425473,0.561782,12.373415,-0.218184,12.154579}
+
+
+
+==== TEST REAL BASE IMPLEMENTATION ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771,0.590649,0.313560,0.547613,0.946811,0.793753,0.690502,0.276120,0.792995,0.446645}
+
+float y[] = {5.354852,5.387001,4.829278,5.046340,5.849788,5.775999,5.653334,5.372714,5.999860,5.593828,5.628739,5.178002,6.010774,6.186034,6.337766,5.538046,5.616131,6.289612,5.486091,5.835261,6.277413,5.894117,5.563587,6.082063,5.828556,6.160175}
+
+==== TEST REAL SSE3 IMPLEMENTATION (hlen=4) ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604}
+
+float y[] = {1.154625,1.856899,1.754012,1.866038,1.759821,1.614741,1.946849,1.905307,2.034228,1.369325,1.929276,1.644739,1.911431,1.455565,1.751712,1.711433,1.206255,1.551974,1.351406,1.252433,1.410497,1.527218,1.666560,1.330974,1.544475,1.701906}
+
+==== TEST REAL SSE3 IMPLEMENTATION (hlen=8) ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771}
+
+float y[] = {2.966950,2.964003,3.035802,3.567513,2.983864,3.487861,3.089418,3.836586,2.979637,3.173361,3.524760,3.308944,3.511707,2.951268,3.500564,3.466951,3.174077,2.778949,3.124344,2.816606,3.196814,2.774090,3.272130,2.980138,2.646414,3.090803}
+
+==== TEST REAL SSE3 IMPLEMENTATION (hlen=12) ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771,0.590649,0.313560,0.547613,0.946811}
+
+float y[] = {3.906606,3.831477,4.613783,4.371631,4.441847,4.311853,4.446086,5.089131,4.708794,4.314635,4.866886,4.812932,4.678810,4.796319,4.687846,5.426141,4.119072,4.687284,4.516533,4.303559,4.733458,4.146965,5.133350,4.832816,4.598291,4.252030}
+
+==== TEST REAL SSE3 IMPLEMENTATION (hlen=16) ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771,0.590649,0.313560,0.547613,0.946811,0.793753,0.690502,0.276120,0.792995}
+
+float y[] = {4.845784,5.086479,6.160082,6.147918,5.549072,5.538811,6.264142,6.083664,5.942431,5.214122,6.458036,6.120992,6.385656,5.751343,6.099504,6.738166,5.942206,5.756058,6.343914,6.239408,6.090616,6.325348,6.214744,6.674619,5.691174,6.413076}
+
+==== TEST REAL SSE3 IMPLEMENTATION (hlen=20) ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771,0.590649,0.313560,0.547613,0.946811,0.793753,0.690502,0.276120,0.792995,0.446645,0.327805,0.785346,0.676628}
+
+float y[] = {6.148925,6.262301,5.792440,6.652380,6.759685,6.515733,6.943458,6.334218,6.539823,6.542612,7.766725,7.472028,7.258010,6.947061,7.347066,7.503224,7.134092,6.244353,7.690946,7.584768,7.779833,6.845586,7.351567,8.099596,7.393943,7.176465}
+
+==== TEST REAL SSE3 IMPLEMENTATION (h_len%4=0) ====
+float x[] = {0.828957,0.675654,0.904170,0.191112,0.394521,0.706067,0.868924,0.547397,0.738959,0.932485,0.233119,0.926576,0.551443,0.933420,0.494407,0.552568,0.939129,0.799646,0.814139,0.594497,0.657201,0.995300,0.935852,0.324541,0.874309,0.589157,0.637771,0.759324,0.775421,0.794910,0.262785,0.604379,0.470564,0.166955}
+
+float h[] = {0.726144,0.746635,0.470674,0.211604,0.963092,0.264553,0.265818,0.725771,0.590649,0.313560,0.547613,0.946811,0.793753,0.690502,0.276120,0.792995,0.446645,0.327805,0.785346,0.676628,0.906507,0.279178,0.015699,0.609179}
+
+float y[] = {7.032490,7.904466,6.745667,7.146502,6.958916,7.972230,7.314566,6.972099,7.773273,7.740826,7.380684,7.907260,8.446323,7.862378,8.022881,7.726059,7.748359,7.602177,8.926439,8.905205,8.569546,7.948394,8.588051,8.850824,8.592319,7.636216}
+
diff --git a/tests/testsuite.at b/tests/testsuite.at
new file mode 100644
index 0000000..f84225e
--- /dev/null
+++ b/tests/testsuite.at
@@ -0,0 +1,53 @@
+AT_INIT
+AT_BANNER([Regression tests.])
+
+AT_SETUP([BitVectorTest])
+AT_KEYWORDS([BitVectorTest])
+cat $abs_srcdir/CommonLibs/BitVectorTest.ok > expout
+AT_CHECK([$abs_top_builddir/tests/CommonLibs/BitVectorTest], [], [expout], [])
+AT_CLEANUP
+
+AT_SETUP([InterthreadTest])
+AT_KEYWORDS([InterthreadTest])
+cat $abs_srcdir/CommonLibs/InterthreadTest.ok > expout
+AT_CHECK([$abs_top_builddir/tests/CommonLibs/InterthreadTest], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([LogTest])
+AT_KEYWORDS([LogTest])
+cat $abs_srcdir/CommonLibs/LogTest.ok > expout
+cat $abs_srcdir/CommonLibs/LogTest.err > experr
+AT_CHECK([$abs_top_builddir/tests/CommonLibs/LogTest], [], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([PRBSTest])
+AT_KEYWORDS([PRBSTest])
+cat $abs_srcdir/CommonLibs/PRBSTest.ok > expout
+AT_CHECK([$abs_top_builddir/tests/CommonLibs/PRBSTest], [], [expout], [])
+AT_CLEANUP
+
+AT_SETUP([SocketsTest])
+AT_KEYWORDS([SocketsTest])
+cat $abs_srcdir/CommonLibs/SocketsTest.ok > expout
+AT_CHECK([$abs_top_builddir/tests/CommonLibs/SocketsTest], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([TimevalTest])
+AT_KEYWORDS([TimevalTest])
+cat $abs_srcdir/CommonLibs/TimevalTest.ok > expout
+AT_CHECK([$abs_top_builddir/tests/CommonLibs/TimevalTest], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([VectorTest])
+AT_KEYWORDS([VectorTest])
+cat $abs_srcdir/CommonLibs/VectorTest.ok > expout
+AT_CHECK([$abs_top_builddir/tests/CommonLibs/VectorTest], [], [expout], [])
+AT_CLEANUP
+
+AT_SETUP([convolve_test])
+AT_KEYWORDS([convolve_test])
+# Different results for i686, x86_64 and ARM. see OS#2826, OS#2828, and https://lists.osmocom.org/pipermail/openbsc/2018-January/011655.html
+AT_SKIP_IF(true)
+cat $abs_srcdir/Transceiver52M/convolve_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/Transceiver52M/convolve_test], [], [expout], [])
+AT_CLEANUP
diff --git a/utils/clockdump.sh b/utils/clockdump.sh
new file mode 100755
index 0000000..c2a6574
--- /dev/null
+++ b/utils/clockdump.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+sudo tcpdump -i lo0 -A udp port 5700
+