Compare commits

...

273 Commits

Author SHA1 Message Date
Pau Espin Pedrol
d0ac926b56 Bump version: 0.3.0-dirty → 0.4.0
Change-Id: Ifc469bce89d52012e1f876c847b4535360a602ad
2018-05-03 16:23:30 +02:00
Pau Espin Pedrol
00d5114717 git-version-gen: Take into account tags not in master
Latest tag 0.3.0 was created in a release branch instead of master. As a
result, git describe doesn't show them. Let's instead reuse git command used in
osmo-ci to get latest tag from tag list when OBS latest repository is
built.

Change-Id: I3e461d4270b5e6d4c42126df4deef5651dca1e27
2018-05-02 21:29:05 +00:00
Philipp Maier
3a496f3b8a doc: add example config for usrp B200 series
Change-Id: I1a80ddd9ca9b143e9c89618616f30fe3f3547d7b
2018-05-02 18:41:34 +02:00
Harald Welte
fad2e09840 osmo-trx: s/GSM Core Address/GSM BTS Address/
The TRX connects to the BTS, not to the "Core".  The Core network
is miles away...

Change-Id: I6de2f708fc7a7df7dea16314b7dfa4ab82f15b2c
2018-04-28 21:43:40 +02:00
Harald Welte
e09e80f5ee update .gitignore for new executable names
Change-Id: Id698024485176e5201ca92c72b1bd1d5914aff32
2018-04-28 21:43:36 +02:00
Harald Welte
2e276e7edd debian/control: Add build dependency to libusrp-dev
Change-Id: I9593c9e45d304b7ffc94474e94450da418838513
2018-04-28 14:38:16 +02:00
Harald Welte
dffc21725c debian: Ensure USRP1 firmware is part of osmo-trx-usrp1
The std_inband.rbf files are required to operate USRP1 with timestamps

Change-Id: I9b3e937ea1941953dcdcaf57cfec9575cd5a8a9c
2018-04-28 14:22:57 +02:00
Harald Welte
96f0f2cf7e debian/rules: Make sure we always require libusrp
Change-Id: Id9c9188a24617f83efa0e1fcf54f9e0dd223e365
2018-04-28 14:21:14 +02:00
Harald Welte
225b16d48e debian/control: Remove "Maintainer" from binary package section
This resolves the following warning:
dpkg-source: warning: unknown information field 'Maintainer' in input data in package's section of control info file

Change-Id: I7a8d8c54eabccc5cedf7ad0a2bccf91f32f1bfdf
2018-04-28 14:21:14 +02:00
Harald Welte
0ebbb2ed2e Revert "debian: Remove osmo-trx-usrp1 until we can build libusrp1.deb"
This reverts commit 295b938d51, as we now
have libusrp built in OBS

Change-Id: Ia1311f1e236e6aea4acc08d3f234d53e46789cf9
2018-04-28 14:21:14 +02:00
Pau Espin Pedrol
2fea950644 build: Fix OBS build for ARM
Change-Id: Ic6b408d684e2bb58af71ec04ced7e73e0d38ffcd
2018-04-28 01:44:39 +02:00
Pau Espin Pedrol
d0a97a5f73 debian: Fix OBS build
osmo-trx is a metapackage, no need for .install file.
Debug files are always put into osmo-trx-dbg, no osmo-trx-*-dbg.

Change-Id: I160dd83b265dcda52762a9f9c7eac9337928da35
2018-04-28 01:38:30 +02:00
Pau Espin Pedrol
295b938d51 debian: Remove osmo-trx-usrp1 until we can build libusrp1.deb
Change-Id: I43163755a29014fe1f098e994f8df79d853c90f8
2018-04-28 01:38:30 +02:00
Pau Espin Pedrol
e8605202ab jenkins.sh: Enable build of osmo-trx-usrp1
Change-Id: I44a978fdd7fac683520be3586d84783e5e13de5f
2018-04-28 01:38:30 +02:00
Pau Espin Pedrol
2a8183bdf0 build: More OBS build failure fixes
It seems the order in which static code and -lfoo is passed to the
linker matters.

Change-Id: I3b25be0154053ae8eb5f0a24c39fb9a229130fcf
2018-04-28 01:38:30 +02:00
Pau Espin Pedrol
478f82f47e build: Fix make distcheck
Change-Id: I1fa5e34b44331cd56408ea7ad4483dcf6443b259
2018-04-27 11:34:11 +02:00
Pau Espin Pedrol
f37b0ad652 Transceiver: log timing info of stale bursts
Change-Id: I043fd8199253bdf3912c7aec2ccf98f8cbe54251
2018-04-25 20:14:55 +02:00
Pau Espin Pedrol
3b78cbfdc1 Logger: Print correct source file and line number
Before this commit, always Logger.cpp:53 was being printed.

Change-Id: Ie5c64b4961c7c41d23484784a93eda5e08331f08
2018-04-25 16:45:34 +02:00
Pau Espin Pedrol
f3d7f443a0 Build one osmo-trx binary for each device support enabled
Same way as we do in osmo-bts. After this commit, osmo-trx no longer
exists. Instead, osmo-trx-uhd and osmo-trx-usrp1 are generated based on
configure flags enabled.

A new flag --with(out)-uhd has been added to enable/disable build of
osmo-trx with UHD backend. It is left enabled by default to keep
compatibility with older build scripts. Binary with USRP1 backend must
still be manually enabled with --with-usrp1 flag.

Change-Id: Iea8c0d7434762713a53440d29bf3ebd84accb262
2018-04-24 19:17:22 +02:00
Pau Espin Pedrol
e564f0fd84 Transceiver: Move device specific code to radioDevice class
Change-Id: Ibcf258d8bf8595e286682e0bc59391b239ea7642
2018-04-24 18:46:48 +02:00
Pau Espin Pedrol
0fc20d14b3 Move device specific code out of radioInterface
This way code of radioInterface is independent of the device and doesn't
need to be rebuild for each device.

Change-Id: Id104e1edef02f863b6465ced5b4241050dc188f9
2018-04-24 18:46:48 +02:00
Pau Espin Pedrol
a4316ee4c5 Change configure define USRP1 to DEVICE_USRP1
Similar as we do for ARCH_*, it's easier to find those are related to
device support features.

Change-Id: Iba238bff689b8f944af76120402c0fa2e29a70de
2018-04-24 18:46:48 +02:00
Pau Espin Pedrol
2128a308eb Move device specific files to device subdir
Change-Id: Ib42fef14bf4c7b779f44d99711a35c18b32a4c21
2018-04-24 18:46:48 +02:00
Pau Espin Pedrol
43fedb656b Move arch specific fiels to arch subdir
Take the chance to update some includes using files available in that
subdir to have them ina more uniform way.

Change-Id: Ibda3c54fd4dc3f6b845cc373f1a1e6b758c1ea82
2018-04-24 15:22:59 +02:00
Pau Espin Pedrol
53bdb7f82a configure.ac: Add --enable-sanitize option
Change-Id: I1b5154a47bb2f66103ac254a0f422e8a80b2d3e0
2018-04-16 15:17:48 +00:00
Pau Espin Pedrol
6462dd3963 tests: Makefile.am: Fix typo in include path
Change-Id: I036f1f587f2a5eaf93ec8fb619bf76b571c1633a
2018-04-16 15:35:57 +02:00
Pau Espin Pedrol
e1977fcd22 use osmo_init_logging2()
Change-Id: I2c28e6e6e3eb9f587680b34330e03408e32c2b94
2018-04-16 14:50:55 +02:00
Alexander Couzens
f97296e0ce jenkins.sh: change qemu-img default location to $HOME/qemu-img instead of /opt/qemu-img
Change-Id: I56f314d78c0ca968b1fef9a91ecd540a7cc8fa86
2018-04-12 09:23:28 +00:00
Martin Hauke
20259cb307 configure.ac: Fix typo
Unbreak git-version-gen when used with .tarball-version

Change-Id: Id836c6822daf1f6835a8c869624b4b40dde47f3a
2018-04-08 19:53:37 +02:00
Alexander Couzens
ffa4e5938c jenkins.sh: cleanup always the workspace
It was broken before becaues of checking emptyness against literal string
will never be zero.
However it should be cleaned always because the script is executed
recursive which would also end up, never being executed.

Change-Id: Ib228ff247a72b21b12e8dd4cbe3afe3e858c89d3
2018-03-27 13:37:39 +02:00
Vadim Yanitskiy
c0c6d70fe9 Transceiver.cpp: prevent out-of-range array access
There was no a simple range check for both (NO)HANDOVER commands,
so an out-of-range access was possible. For example, a command:

  CMD HANDOVER 0 -3

might enable EDGE at run-time, because:

  a[i] == *(a + i)

Let's fix this.

Change-Id: I24a5f70e8e8097f218d7cbdef8cb10df2c35416f
2018-03-14 21:12:32 +00:00
Vadim Yanitskiy
8c6c5d2bcd Transceiver.cpp: fix incorrect format string for SETTSC
Change-Id: If69a478121a31aa7df945548cc17271c476d6a6b
2018-03-14 21:12:32 +00:00
Vadim Yanitskiy
a62fcf786a Transceiver.cpp: use pointer arithmetics for CMD parsing
It looks like the author of control command parsing code was not
familar with simple pointer arithmetics, so excessive amount of
memory and useless memcopying was used to parse a single command.

Let's introduce two pointers, one of which will point to the
beginning of a command, another to the beginning of its arguments.
Also, let's simplify the command matching by using a separate
function called 'MATCH_CMD'.

Change-Id: I226ca0771e63228cf5e04ef9766057d4107fdd11
2018-03-14 21:12:31 +00:00
Vadim Yanitskiy
4d9b59c3ef Transceiver.cpp: properly zero-terminate received commands
Previously it was assumed that a sender should zero-terminate
each command being sent. Otherwise, this could cause to printing
garbage. Let's do this manually, using the length of received
data as a position for '\0'.

Change-Id: I69f413f33156c38a853efc5a8cdc66fbfb0ca6af
2018-03-14 21:12:30 +00:00
Vadim Yanitskiy
bd0efb0bea Transceiver.cpp: use a define for the MAX_PACKET_LENGTH
No need to keep this value on stack.

Change-Id: If9ffb03b9e7b642f45732ba5938977bca271f1c7
2018-03-14 21:12:30 +00:00
Pau Espin Pedrol
8fbbd656c7 Build Transceiver52M/common as an .la lib
Stop picking files from that directory on different places as it causes
dependency issues during make distclean/maintainer-clean.

Fixes: OS#3029

Change-Id: I81bb4251d18fce978d27849b621b20f541caab0b
2018-03-08 14:53:13 +01:00
Pau Espin Pedrol
b35cba613a Makefile.am: Avoid using subdir if arch is not required
There's no need in going a level deeper if we already know nothing's
going to be done. This way we also get cleaner make outputs.

Reference: https://www.gnu.org/software/automake/manual/html_node/Conditional-Subdirectories.html#Conditional-Subdirectories

Related: OS#3029

Change-Id: I3ff57ab14edc575904e8137929a0ef02c95e03af
2018-03-07 19:46:42 +01:00
Pau Espin Pedrol
8dffadb8da osmo-trx: Re-introduce -l cmd line parameter
Parameter -l to set the terminal logging levle was removed in
3da1f8352e, but afterwards it was decided
to keep the cmd line options for a bit more to easy migration to VTY
cfg.

The command line no longer accepts keywords ("DEBUG", "INFO", etc.) but
log level numbers, due to libosmocore APIs log_parse_level and
log_level_str being marked as deprecated and for internal use only.

Keep in mind the log level is overridden by VTY cfg if any line sets log
levels for log stderr in there.

Explicit cast to unsigned int for loglvel is issued to avoid iostream
printing it as a char.

Change-Id: I91c35ecded177b7976045d9b693855adb9e18f8a
2018-03-06 18:41:29 +01:00
Pau Espin Pedrol
408f25081e osmo-trx: Use VTY cfg structures while still allowing cmd line options
Existing cmd line options are kept for a while to give people some time
to move to use VTY cfg. All new cfg options should be set only through
VTY. VTY options take preference (override) over cmd line options.
Deprecated options are removed from help message to dissuade users from
keep using them.

Steps to drop cmd line options in the future:
- Drop comma_delimited_to_vector, print_deprecated
- Drop all options in handle_options marked with print_deprecated.
- Set "-c" param to do the same as "-C", to keep compatibility with old
param and still use same naming as all other osmocom projects.
- Remove the hack in main() to set 1 channel implicitly by default.

Change-Id: Ib8de1a5da4b3c0b6a49e00033f616e1d66656adf
2018-03-05 20:12:40 +01:00
Pau Espin Pedrol
2001550f7d doc: Add sample cfg file for LimeSDR
Change-Id: I16de70eac0fd79107b8317af37201e6da834c169
2018-03-05 19:55:02 +01:00
Pau Espin Pedrol
a3ab8c263d vty: Implement VTY cfg parsing for current parameters
At this stage, osmo-trx still uses the cmdline parameters top run the
device, but it is already able to parse all the same parameters from a
cfg file through the VTY and filling a trx_ctx structure which will be
later used to drive the device. Device config can be printed in the VTY
with "show trx".

Change-Id: Ie084c1b30b63f91c6e7640832ec1797d9e813832
2018-03-05 19:55:02 +01:00
Pau Espin Pedrol
efac20b6bb Move enums required by VTY to a separate header
This patch is a preparation for next patches, which add full VTY cfg
support.

Change-Id: I3d5b0576aa96869756f1629a40306c0043b6304b
2018-03-05 17:16:05 +01:00
Pau Espin Pedrol
0bbd8922ea osmo-trx.cpp: Move trx start and stop to helper functions
Make main() smaller, and make it easier to replace cmdline parameters in
following commits.

Change-Id: I10eaaafe38ace2b7bb095a0ad1db70d6c06ee03b
2018-03-05 17:14:32 +01:00
Alexander Couzens
28b8cc6283 jenkins.sh: fix the download url if the qemu image wasn't setup
Download the image from yesterday because linuxcontainers only contains the images of the last 3 days.

Change-Id: I75e270b7255c1cd7fca1674111b0f19fc7bba74f
2018-03-02 08:58:31 +00:00
Alexander Huemer
3f52f0e6c5 Fix USRP1 build with support for setting Rx/TxAntenna
The USRP1 build was broken by commit 77ce99ac67.

Commit 77ce99ac67 broke the USRP1 build

Change-Id: I28585947d5662cdd580a814cce54a5d9aa30eeb8
2018-02-27 22:35:37 +01:00
Pau Espin Pedrol
3da1f8352e Logger: Use libosmocore logging system
We still need an intermediate class Logger due to osmo-trx being
multi-threaded and requiring to have a lock to use libosmocore, which is
not thread safe.

Change-Id: I30baac89f53e927f8699d0586b43cccf88ecd493
2018-02-27 07:11:11 +00:00
Pau Espin Pedrol
5ea1817dc2 Add initial support for logging, vty, ctrl
Up to this point, the logging system, vty and ctrl are initialized and
can be used fine, though they don't have a lot of use yet.

Depends on libosmocore Change-Id Ib79cdb62d45d8c78445c7b064e58eb7e9faeccf9

Related: OS#2184

Change-Id: I08982c37b4f873966304b3cfb38a10ee86eb3dad
2018-02-27 07:10:50 +00:00
Pau Espin Pedrol
49d42e979e debian: Depend on libtalloc and libosmocore
Change-Id: If4bf03d164e9d19a8a21399a2c74b2984c48cc70
2018-02-26 00:56:10 +01:00
Pau Espin Pedrol
3a3b220751 osmo-trx: Set up talloc ctx
Change-Id: I67f1980fc615ab74371cbe1c4f83e987381299bc
2018-02-22 20:04:47 +01:00
Pau Espin Pedrol
ab22f4c421 osmo-trx: set up signals using libosmocore helpers
Change-Id: I86e78cd6054d0deff1b1aa061299d9f307e2a352
2018-02-22 20:04:47 +01:00
Pau Espin Pedrol
8b843e5bed Depend on libosmocore
Change-Id: If345c89293fcd7d1ad4f17214eea339951f25a5d
2018-02-22 20:04:47 +01:00
Pau Espin Pedrol
c92dad32dd configure.ac: Check for pkg-config
Change-Id: I1a851181a99f2f35ea4ff1b38c7afe27a04e5f18
2018-02-20 20:32:27 +01:00
Pau Espin Pedrol
61837c0420 Logger: Remove gLogToConsole flag
No code is using it and we always lock to console anyways.

Change-Id: I5fde99c6af5a845e635b5d27abab855682071f14
2018-02-20 20:32:27 +01:00
Pau Espin Pedrol
f83e11fefd Logger: Remove unused includes
Change-Id: I4d26c0b4f36ee3c66ed1a9e2e9fa2fa8272da16d
2018-02-20 20:32:27 +01:00
Pau Espin Pedrol
01aff88ce9 Logger: Drop support to log into file
This feature is currently not being used, so let's simplify current code
to move to libosmocore logging system in the future.

Change-Id: If2c77c776823f595130edac963be953026049423
2018-02-20 20:32:27 +01:00
Pau Espin Pedrol
11d50d950c Logger: Drop syslog support
This feature is currently not being used, so let's drop it to make it
easier to integrate into libosmocore logging system in the future.

Change-Id: I8282745ef0282d41599eaf94fe460a1d29b18e2a
2018-02-20 20:32:21 +01:00
Pau Espin Pedrol
8bd111c942 Logger: get rid of alarm APIs
It's only used internally inside the Logger module, and in case there's
an "alarm" (level more than critical) we still print on cerr, so we can
just rely on our system catching stderr instead of stdout to handle it.

Change-Id: I6d6df1578c3a4c1a37bd0d69952d443f62eed2ab
2018-02-20 18:26:45 +01:00
Pau Espin Pedrol
3808e479aa Logger: Remove unused logging macros
Change-Id: I1133e181183bec8dabe2fa77d0385f783458503f
2018-02-20 18:14:20 +01:00
Pau Espin Pedrol
bd45a979f8 Logger: Drop unused gLogEarly
Change-Id: I2c8f24fbf453e0a94d7a95c3df7cc75f0e4bd456
2018-02-20 18:14:20 +01:00
Pau Espin Pedrol
b7095c7bc5 UHDDevice: Initialize async_event_thrd in constructor
Fixes coverity CID 182757.
It's actually a false warning because "async_event_thrd" member is
protected by other member "started", so in practice it's never going to
be used before being initialized in start().

Change-Id: I5d5739bc9d08fe533e4d44c3992005a14e568a4f
2018-02-09 16:20:39 +01:00
Pau Espin Pedrol
77ce99ac67 Add support to set Rx/TxAntenna
Some devices have different Rx or Tx ports with different RF characteristics.
For instance LimeSDR has H (High), L (Low) and W (Wide) band Rx ports,
each of one being more suitable to a specific range of frequencies.

In case one wants to support several GSM bands, the best option is to
use the WideBand port and connect the antenna physically to that port in
the board. Then the firmware must be instructed ro read from that port.
Support for Rx/Tx port configuration is already in there for all the
layers (Limesuite, SoapySDR, SoapyUHD, UHD), but we are missing the
required bits in osmo-trx to make use of the available UHD API. This
commit addresses it.

Before this patch, the Rx/Tx paths configured could be changed by means
of the LimeSuiteGUI app, but after running osmo-trx, the values were
changed to the default ones.

One can now start using osmo-trx with 1 channel and specific Rx/Tx ports
by using for instance: osmo-trx -c 1 -y BAND1 -z LNAW

Default behaviour if no specific path or an empry path is passed ("") is
to do the same as preiously, ie. nothing by not calling the
set{T,R}xAntenna APIs.

One can also configure only specific channels, for instance to configure
only the first Tx channel and the second Rx channel:
osmo-trx -c 2 -y BAND1, -z ,LNAW

Change-Id: I1735e6ab05a05b0312d6d679b16ebd4a2260fa23
2018-02-07 13:43:42 +01:00
Pau Espin Pedrol
f58cd8ac83 Fix whitespace
Change-Id: Icda84caa998614ce6c15d5118f8c5c1568ba9a79
2018-02-05 13:04:41 +01:00
Max
99eb07e232 tests: null-terminate buffer
Initialize temporary buffer with 0 to make sure that it's
null-terminated.

Change-Id: Icdde701839e35d3131605ea5a11882af21c8939a
Fixes: CID149362
2018-01-31 11:34:59 +01:00
Pau Espin Pedrol
89be118a3b Remove unneeded libdl dependency
Closes: OS#1929

Change-Id: I0caea2a2a8e6bd07432fd73bae72b42b1ce022cd
2018-01-23 18:15:24 +00:00
Alexander Huemer
6fafd33b13 Unbreak ./configure --with-usrp1 build
Change-Id: I49b385594271ae64a48d4d39ee9fe26d7c95bd30
2018-01-20 23:24:20 +01:00
Pau Espin Pedrol
6e55d51747 tests: convolve: Disable due to difference in output in different archs
Let's disable this test in order to have passing jenkins jobs until we
find a better way to properly test this for different architectures.

Change-Id: I2320309bc8c1c20e2de6ef2e0f17472c68de80cb
2018-01-16 11:33:50 +01:00
Pau Espin Pedrol
6cae1d7b4b contrib/jenkins.sh: Use qemu+proot+debootstrap to run tests with ARM instruction set
The following logic doesn't require root access to run the tests, which
means we can easily run it inside jenkins.

Change-Id: Iba3f4de008662805d8ffc46e1f473e407b088fb8
2018-01-15 18:21:17 +01:00
Pau Espin Pedrol
28ce315a32 tests: TimevalTest: refactor and avoid double comparison
Before this patch, the experession assert(then_secondws==then.seconds())
was failing in x86 architecture (and passing when adding a fprintf to
debug it). Avoid comparing the double values with == as that's usually a
bad idea, since the processor can output slightly different results for
the same operation depending on how it is optimized. Use timespec()
instead to check the invariant. Take the chance to refactor some
variables around to make the test easier to read.

Change-Id: Id4324be8ece86d371b1acb46bbd97856dfed241d
2018-01-15 11:49:10 +01:00
Pau Espin Pedrol
10d76b6863 tests: SocketsTest: Fail test on write fail
Change-Id: Ib6b778a2225339ebd2eaa80b3fca6ee8d8646b23
2018-01-15 10:47:13 +01:00
Pau Espin Pedrol
708b8b44ae tests: SocketsTest: Avoid hang forever if test fails
Change-Id: Ia95e216a2ab6d397ab02c828b70f2b95d1671257
2018-01-15 10:46:42 +01:00
Pau Espin Pedrol
cb0fc9b21a tests: SocketTests: Pick OS-assigned instead of setting one manually
This fixes failures if the port is already being taken by other apps or
if this test is run several times concurrently in the same system.

Change-Id: Iea213375e489a56cf8ed3e47fe814e17c288803e
2018-01-15 10:35:11 +01:00
Pau Espin Pedrol
8639fee504 Remove UDDSocket class
This class is not used anymore in osmo-trx, so we can safely remove it.

Change-Id: I67f90aa3d6a2a5e92292436d10928e0705c8f8ff
2018-01-11 20:17:43 +01:00
Pau Espin Pedrol
ca46896cfe .gitignore: Add missing test related files
Change-Id: I7a82a2c0c97bbfa4877f148c89d121b4c3476176
2018-01-11 20:17:40 +01:00
Pau Espin Pedrol
4a25d6b8f6 arm/convert.c: Add missing convert_init implementation
osmo-trx.cpp calls convert_init, which in case of building using
--with-neon is not implemented and the compiler stops with an error.

Error was introduced in 7e07cf2346.

Related: OS#2720

Change-Id: I9840d374d13b525b97f978ea0c5ed9e8421072a0
2018-01-11 18:45:12 +01:00
Pau Espin Pedrol
79baee3a8f arm/convert.c: Fix compilation error
Commit fe9769833f aiming at cleaning stuff
introduced a compilation error.

Related: OS#2720

Change-Id: I6ce6e5ec3fdb1e3e8818e2cb674470ad54e38afb
2018-01-11 18:45:12 +01:00
Pau Espin Pedrol
c2ba427b52 tests: Migrate convtest util to autotest infrastructure
Change-Id: Ie682abf7e83de436d0f37f9f6e0664cb2f4d0c9e
2018-01-11 18:45:12 +01:00
Pau Espin Pedrol
611212676b Move ARCH_LA to Makefile.common
It will later be used by other directories too (tests/Transceiver52M).

Change-Id: I0ca9b7fc5e1377db971cb7da0b3496ba8d61c716
2018-01-10 12:32:33 +01:00
Pau Espin Pedrol
4ebb289c90 utils/convolvtest: Remove uneeded libosmocore dependency
Change-Id: I1742146c31cadec8ce8afbbdae5777f076b212d4
2018-01-10 11:54:34 +01:00
Pau Espin Pedrol
2f376a3edf tests: Sockets: adapt to have reproducible output and enable autotest
Change-Id: I5414076c6846b849973bcdeb3f2358b28dcb004c
2018-01-10 11:31:22 +01:00
Pau Espin Pedrol
2edbe4d366 Sockets.cpp: Fix initialization of UDD socket
Without this line, destination address for a UDD socket is left with
incorrect value AF_UNSPEC. Later on when calling DatagramSocket:write(),
sendto() fails with EINVAL.

This commit fixes test SocketsTest.cpp.

Change-Id: I6e1b7e743a781abdcf69aa9842b30be893633433
2018-01-10 11:29:16 +01:00
Pau Espin Pedrol
a3694bd303 tests: Log: adapt to have reproducible output and enable autotest
Change-Id: I77c40230503acadef5f64ab2624cd872f9782b98
2018-01-10 11:29:16 +01:00
Pau Espin Pedrol
2652f2bc39 tests: Timeval: adapt to have reproducible output and enable autotest
Change-Id: I1a79892ba3c934879a171789e0edb357277acae4
2018-01-10 11:29:13 +01:00
Pau Espin Pedrol
93d9b114b7 tests: InterThread: adapt to have reproducible output and enable autotest
Change-Id: I05d4067890b526bd72d2eb31cf76de43ee11e80f
2018-01-10 11:25:42 +01:00
Pau Espin Pedrol
2ac788b2c3 Set up GNU Autotest infrastructure
Test files are moved from CommonLibs/ to tests/CommonLibs/.
Some tests are disabled in autotest because they generate timedate
related output which cannot exactly match against expected output.

Change-Id: I3d6ba625968be09297642d18090c496490e9b8fc
2018-01-10 11:17:32 +01:00
Pau Espin Pedrol
d36ef2f57b cosmetic: AUTHORS: fix trailing whitespace
Change-Id: I92d9ad9a0fd69e88928a8f57920d39dcda67d59d
2018-01-10 11:14:31 +01:00
Pau Espin Pedrol
caf2abc58f Remove Configuration module and libsqlite dependency
Change-Id: I823aea91367d586507bbf352f1b6f25bdd635baa
2018-01-09 15:26:50 +01:00
Pau Espin Pedrol
de1685f6d7 Drop use of ConfigurationTable gConfig
After latest changes, it is not being used anymore.

Change-Id: I43a49aee94e3239194ad9742fb6374324acac0de
2018-01-09 15:26:50 +01:00
Pau Espin Pedrol
f3837d26f9 Logger: Stop using Log.File and Log.Level from config
This is a required step towards getting rid of ConfigurationTable class
and libsqlite dependency.

As a side effect, support for different log levels for different files
is dropped, but it's not something really being used and we will end up
dropping current logging system in favour of osmocom's one in the future
anyway.

Change-Id: I51cb12d1ab7e103e78190ac71a70fb5bb1d9ff51
2018-01-09 15:26:49 +01:00
Pau Espin Pedrol
ddf4743306 Logger: Stop using Log.Alarms.Max from config
This is a first step towards removing ConfigurationTable class and
sqlite3 dependency.

Change-Id: Idcd789afe668a5c0271352f1d20d2efda826213a
2018-01-09 15:26:49 +01:00
Pau Espin Pedrol
82f83ced73 cosmetic: Remove trailing whitespace
Change-Id: I64c8dbad3fc42bcb8dd4ac9b16bbd9c59a0cf5d5
2018-01-09 15:26:49 +01:00
Max
cff4ed9b4c Update license notes
* replace references to OpenBTS with OsmoTRX
* drop trademark notice
* drop outdated example

Change-Id: I144f96c507bfe48df350fb0350edbeba87126462
Related: OS#2600
2018-01-05 10:07:08 +00:00
Max
6ec26bb788 Update legal disclaimer
* drop trademark passage
* add link to OpenBTS website and corresponding attribution
* replace OpenBTS references with OsmoTRX

Change-Id: Ia40df831649cdb68898db9ca77868c422a8d631d
Related: OS#2600
2018-01-04 15:17:27 +01:00
Max
a1ff991402 Update installation instructions
* remove references to OpenBTS
* update URLs
* remove unnecessary requirements

Change-Id: I6ec26beaaa74dd3d98f27d110055a8f0cdd3c991
Related: OS#2600
2018-01-04 14:31:54 +01:00
Max
d09843c692 Remove unused headers
Change-Id: Idadb17aeb85b011d114ffc1d81c920544bac1989
2018-01-04 14:31:54 +01:00
Max
e5448ff972 Remove outdated references to OpenBTS
Change-Id: I2df613bf59af28e2f44a520d0ee953932bcf4d7e
2018-01-04 13:41:06 +01:00
Max
e48c1367dc Mark release target as virtual
Change-Id: Iee747faa3171663f1874a5eacddd56607de55297
2018-01-04 13:40:42 +01:00
Piotr Krysik
aa60dda99a UHDDevice.cpp: add USRP B205mini support
The B205mini is similar to the B200mini and runs OsmoTRX just
fine, so let's make OsmoTRX recogonize and support it too.

Change-Id: Iee575121248ea541f7abc49055e49ec2d30904c0
2017-12-04 00:32:33 +07:00
Harald Welte
1468a5c3dc SocketsTest: Fix printing of non-nul-terminated string
Change-Id: I33d0ddf851d84b81ab5252e3755422170cee54ee
Fixes: Coverity CID#149363
2017-11-07 20:32:09 +00:00
Neels Hofmeyr
b0e1bd8c22 jenkins: use osmo-clean-workspace.sh before and after build
See osmo-ci change I2409b2928b4d7ebbd6c005097d4ad7337307dd93 for rationale.

Depends: I2409b2928b4d7ebbd6c005097d4ad7337307dd93
Change-Id: I609f7c7c88b49f26e2e48e1f1cffed76d9e6fb5e
2017-10-31 09:24:58 +00:00
Harald Welte
78e1cd20e2 Tag/Release 0.2.0
This is the first real tagged Osmocom release of OsmoTRX.

  [ 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()

Change-Id: Ibf3be6cc25e9b20d625b1f67972114b7f613f05c
2017-10-28 17:53:25 +02:00
Harald Welte
db9c1b54cb Import git-version-gen and update AC_INIT()
In AC_INIT(), it still stated openbts.  Let's clean this up and use
the same method of version generation that we use in all other osmocom
projects, too.

Change-Id: Ie7ae0585955aebdc3950b1dd8bff0d1fff3be212
2017-10-28 17:51:54 +02:00
Max
099a44abfb Use release helper from libosmocore
See
https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
for details.

Change-Id: Ieb843923d8f534654413be695f2b5f0c87b75520
Related: OS#1861
2017-08-28 12:26:54 +02:00
Pau Espin Pedrol
8c80095017 Add -j option to bind to specific address
Before this patch, the binding of the listening sockets was hardcoded to
a local IP.

Change-Id: I9ba184a1251c823e413a9230943ed263e52142ec
2017-08-16 17:06:54 +02:00
Max
d49a6aa136 Remove redundant explicit dependency
There's no need to explicitly mention library package because
${shlibs:Depends} will take care of it automatically.

Change-Id: Ibd9cfc3673d828122edb85ba9de7ceb77f0299d0
2017-07-20 18:36:11 +00:00
Harald Welte
81486e053c Add '-t' command line option to enable SCHED_RR
SCHED_RR allows us to operate osmo-trx reliable even under exceptionally
high system load, as the realtime scheduler priority will have higher
priority than the other "regular" tasks on the system.

Change-Id: Ia2452b9763960b2be37fbeee9d832554da68a53f
Closes: OS#2344
2017-07-20 18:36:01 +00:00
ignasj
28d8081e25 LimeSDR: Fix sample value range
when "sc16" stream arg is passed to SoapyUHD sample value range is -32768 to 32767

Change-Id: I58b8b6b71648bd9cbc105ddaaa9a7cf0a31b3d47
2017-07-10 11:45:06 +02:00
ignasj
87ed77b937 LimeSDR: change tx window type to TX_WINDOW_FIXED
It seems that TX_WINDOW_USRP1 is for devices that do not support tx
sync to timestamp. LimeSDR supports it. Changing to TX_WINDOW_FIXED
greatly reduces number of "dumping stale buffer" messages

Modified to match current master by Harald Welte.

Change-Id: I8de5b165ccd72a62b0f16655618e24ca740d9637
2017-07-10 09:34:58 +00:00
ignasj
f9d996813d LimeSDR: Change device detection to work with USB and PCIe versions
Modified to match current master by Harald Welte.

Change-Id: Ie43610de0b2196d84caf09717ec8c8ca75ab926d
2017-07-10 09:34:32 +00:00
Max
aa5acc953c deb: remove unused dependency
The libdbd dependency is not used because libsqlite3 is used directly -
adjust debian/control to match.

Change-Id: Id2ab1facad703fa0c1d45084e70d41e73dbad6e7
Related: OS#1929
2017-07-06 08:28:29 +00:00
Pau Espin Pedrol
934da48618 transceiver: Avoid sending clock indications when trx is not powered on
Stop calling writeClockInterface() when receiving commands in Transceiver::driveControl,
otherwise it fools osmo-bts-trx clock skew check because it is always sending a clock
indication with the same fn when it issues any commands during the time in between
CMD POWEROFF and RSP POWERON, because fn is not increased during that period.

Also use mForceClockInterface flag to delay delivery of first IND CLOCK until we start
serving frames, otherwise the first one is sent and only after a long period of time
the next clock indications are sent, when the radio starts to process bursts. That makes
osmo-bts-trx unhappy because it expects to receive an IND CLOCK aprox at least every
400 frames. This way also we send the first IND CLOCK after the RSP POWERON 0 response.

Change-Id: I91b81a4d7627cec39c1814a39ed4be306681b874
2017-07-04 19:15:57 +02:00
Pau Espin Pedrol
7c405a0c1f cosmetic: transciever: Remove trailing whitespaces
Change-Id: Ib3fbe768048b2a34a75ace9688e306720e67019a
2017-07-04 17:23:30 +02:00
Tom Tsou
4cafb0fa15 LimeSDR: set approximate tx offset value to make GSM work
may be fine-tuned in the future

Modified to match current master by Harald Welte.

Change-Id: Ied215ca9e9d9c346c2a654f96785d1b87b075129
2017-06-29 02:26:07 +02:00
Tom Tsou
f611569018 uhd: Always specify samples-per-symbol for device lookup
Fix MCBTS device setup where the map access was failing on the wrong
assumption that all devices support 1-SPS TX-RX operation. Some devices
and/or configurations such as LIMESDR and MCBTS only support running
at 4-SPS.

Even though certain settings (e.g. number of physical channels or the
FPGA clocking rate) are not dependent on the SPS value, we still need to
specify because we use SPS as a parameter for device classification.

Fixes: OS#2341
Change-Id: I56e939285d585cc38efa6c329e30e3acebb734eb
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2017-06-27 11:36:34 -07:00
Tom Tsou
354741326c sigProcLib: Specify standard namespace for isnan()
In commit a3dce85f
"sigProcLib: Use explicit NaN check in sinc table generation"

Use of std::isnan(double) was added without namespace specifier,
which may cause build issues depending on whether the C version
isnan() call is available. Add standard namespace to force C++
call usage and potential build issues.

Change-Id: I49328c43fdd690a4e6a2b2e949411aaf5674ead1
2017-06-22 18:03:29 +00:00
Tom Tsou
d2e5c5694e sigProcLib: Replace dynamically allocated resampling buffers
Instead use object allocated STL vectors. This simplifies code,
removes the need to explicitly release buffers, and fixes a
memory leak in destructor deallocation. Also, remove simplified
init and release sub-calls.

Maintain partition filter allocation using memalign() for SIMD
alignment requirements.

Change-Id: Ie836982794c10fb1b6334e40592d44b200454846
2017-06-22 17:39:44 +00:00
Tom Tsou
a3dce85ffc sigProcLib: Use explicit NaN check in sinc table generation
Using "x < 0.01" is a crude check for detecting NaN condition, which
occurs in a sinc call when x = 0 due to divide-by-zero. Use stdlib
isnan() call for this purpose. Also, as the table is created only
once during initialization, use double floats for table value
generation.

Change-Id: I3a838fe3139fa977dfe906246020a14451185714
2017-06-19 17:04:04 +00:00
Tom Tsou
bb0c68ae61 sigProcLib: Remove trigonometric tables
Trigonometric sin/cos tables are unused after initialization.
There is no benefit to implementing lookup tables for run-once
operations. Also perform initial calculations in double width
because there is no penalty for doing so.

Change-Id: I45bba5daf8610cbba6af95b92c2142f2256491ff
2017-06-19 17:03:11 +00:00
Tom Tsou
87d158cc2d common: Declare explicit Vector move constructor
Vector class already has a semantically odd non-const copy
constructor that serves the same function as a C++11 move
constructor. Make the move constructor semantics explicit
and address Coverity at the same time.

Change-Id: I22e0099abe601b0c59beee808f7560837c6977dd
Fixes: Coverity CID 170738
2017-06-19 17:02:41 +00:00
Tom Tsou
7278a87767 sigProcLib: Remove heap based signal vector allocations
The osmo-trx internals rely heavily on dynamic alloction of
I/Q signal vectors. In a number of cases there is no reason
to to use dynamic rather than stack based allocation. Convert
these cases accordingly.

Change-Id: If53da1bf77b5944b6117765fa98ce12e1ccdeede
2017-06-16 17:11:27 +00:00
Tom Tsou
63eef9faf2 sigProcLib: Fix complex/real vector flag in Laurent modulator
The modulator vector to be shaped by Laurent C1 pulse is complex,
but was set as real. The error does not affect behaviour because
we only support complex-complex and complex-real calculations;
real-real convolution is not supported. So in this case the data
vector was already assumed to be complex despite the improper
flag setting.

Change-Id: I03afc6a93a01fde7a9a02e4eb9d201d3ee37d21a
2017-06-16 17:09:09 +00:00
Tom Tsou
d67bd603e9 transceiver: Fix POWEROFF crash on USRP2/N200/X300 devices
Upon issuing POWEROFF command to a running transceiver, UHD
interfacing thread state may become undefined if the device
is stopped with I/O threads still active. Bad behavior is
device dependent with only network based USRP devices
affected. USB based device thread behavior stops and shutdowns
as expected. Tested with N200, X300, and B210.

Tested solutions include the following:

  1. Set pthread_setcanceltype() with PTHREAD_CANCEL_ASYNCHRONOUS
  2. Add sleep delay to allow I/O threads to timeout before
     stopping the device
  3. Wait for I/O threads to join after cancellation before stopping
     the device

This patch resolves the issue by with the third approach. Number 1
is not guaranteed to always work with UHD internals as driver code
may explicitly set thread parameters. Using sleep calls to fix
order-of-operation issues is almost never a good idea.

Change-Id: Ib72ab98a27a02084b040319046c92d1c4157ae4c
2017-06-16 17:03:30 +00:00
Tom Tsou
988a464d5d uhd: Fix USRP2/N200/N210 device detection
Commit 1fb0ce67 "uhd: Use map container for for device parameter access"
inadvertently removed the string identifier for the USRP2 and derived
devices (N200/N210).

Add the missing USRP2 string identifier. Also search for partial string
matches in the UHD provided device and mboard stings. This is necessary
to guarantee that strings such as "N200r3" instead of just "N200" are
sucessfully found.

Tested with N200, X310, B200mini and B210 devices.

Change-Id: Ide4e22418e2cc469418cba018970cb0eb9906697
2017-06-16 17:02:27 +00:00
Tom Tsou
1b6ab7d7ee uhd: Fix Tx-RX timing offset setting
Integer timestamp offset was set to zero due to bad cast-operator
precedence.

Change-Id: Ib1f524cc86416699b3c143e5faddb33d61380767
2017-06-15 16:22:44 -07:00
Tom Tsou
980525c8a9 uhd: Add non-UmTRX channel swap support
Previously an UmTRX-only feature.

Change-Id: I4a0e0c1d69e89993158e948535ad33f54e568d2d
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2017-06-14 16:34:46 +00:00
Tom Tsou
70134a01eb sigProcLib: Remove unused functions from public interface
Also remove entirely completely unused calls. Most of these
calls have been around since OpenBTS conception. Nearly a
decade is long enough time for deprecation.

Change-Id: Ifc122aaff23414c363b4b00f99061eed8a6902d0
2017-06-14 16:30:28 +00:00
Tom Tsou
1fb0ce67d8 uhd: Use map container for for device parameter access
OsmoTRX is written in C++ so we might as well use built-in
container types when applicable. Map access allows removal
of significant amounts of special device handling code.

Aggregate device rates and timing offsets into a single
table with access keyed by device/tx-sps/rx-sps tuples.

Change-Id: I8660f75a2b2a13488b913c07637bdd0f5f0f4cf9
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2017-06-12 13:47:14 -07:00
Tom Tsou
8ca237b5c2 build: Require and check for gcc C++11 support
It is now 2017. We can and should be able to use C++11 features now.

Change-Id: I96477e4125390b17b43a3705bb1daf98fa01c9bb
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2017-06-09 11:19:20 -07:00
Alexander Chemeris
082bbbf8fe PRBS: a Pseudo-random binary sequence (PRBS) generator class.
Implemeted with a Galois LFSR for speed and flexibility compared to Fibonacci version.

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

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

Change-Id: Ib5331ba5d0b5819929541686fdd87905e2177b74
2017-06-08 18:33:47 +00:00
Vadim Yanitskiy
3bd763d2a1 ssedetect: call __builtin_cpu_supports() only if supported
Some compilers don't support the __builtin_cpu_supports built-in,
so let's make them able to compile the project anyway.

Change-Id: I0c90402d8e4c9f196c54b066ff30891c8de3ad2b
2017-06-08 18:32:29 +00:00
Vadim Yanitskiy
ee57357682 buildenv: check for __builtin_cpu_supports call support
The '__builtin_cpu_supports' is a GCC's built-in function which
returns a positive integer if the run-time CPU supports specified
SIMD feature and returns 0 otherwise.

This change adds a new check, whether compiler supports this call.
See /gcc/X86-Built-in-Functions.html at gcc.gnu.org for reference.

Change-Id: I797f638573e8c3aae39c28abb157ce2ac419f3f7
2017-06-08 18:32:29 +00:00
Vadim Yanitskiy
8537b90dbe buildenv: cosmetic changes
Change-Id: I9c52f2981513fa6322bdf992215e3e099ac3ddee
2017-06-08 18:32:29 +00:00
Vadim Yanitskiy
038fd7fd70 buildenv: fix build on systems without SIMD support
HAVE_SSE3 and HAVE_SSE4_1 were never defined if CPU architecture
doesn't match the (86*|x86_64*|amd64*) condition.

Change-Id: I3350b14dbc91e9b388d0b04a0ed22ba27d436313
2017-06-08 18:32:29 +00:00
Vadim Yanitskiy
0cd246c27a buildenv: actually strip unused cpuid functionality
Despite the macro message says, that cpuid functionality was stripped
it was still partially preset and wasn't used anyhow.

Change-Id: I380bc9c13d29319685781ef27973afe6744fcf3d
2017-06-08 18:32:29 +00:00
Vadim Yanitskiy
61fbf2ec95 buildenv: correct the ax_sse macro description
Change-Id: I4ce65443c8a33ae9add8f6da9d911c3178472ab2
2017-06-08 18:32:29 +00:00
Alexander Chemeris
15f9d95f5f BitVector: Remove Generator class.
It is not used in osmo-trx, because we're not doing FEC or CRC checks.

Change-Id: I1509e785c1187ebdafe5b2518bd298fbbd1cd036
2017-05-30 20:12:58 +00:00
Alexander Chemeris
73dbccda78 Configuration: Variables allocated with 'new' must be freed with 'delete'.
Thank you Valgrind.

Change-Id: I8477e4e37282947f9841cee9002565631ca0c0b6
2017-05-30 19:19:19 +00:00
Alexander Chemeris
5e65b531e0 sigProcLib: Fix number of tail bits in random Normal Bursts and zero Stealing Bits.
This bug only affects generation of normal bursts filled with random bits which
are used in test mode. It doesn't affect operation of osmo-trx during normal
operation. That's why it has stayed unnoticed for so long.

Each Normal Burst has 3 tail bits, not 4.
Also it's better to set stealing bits to 0 for maximum compatibility. We may want to
introduce a selector for each bit whether to set it to 0, to 1 or to a random number.

Change-Id: I0377029556c8b681b3ba3b635bf19572b34546ea
2017-05-29 15:15:36 +03:00
Max
b992d0a515 debian: remove obsolete dependency
This should fix package build for Ubuntu 17.04: obsolete package
hardening-wrapper was removed which cause .deb build failure.
The dependency on it is incorrect to begin with because we use
DEB_BUILD_MAINT_OPTIONS instead.

Change-Id: I3ea72b4123a280a846086d083c4f3189d611f8cf
2017-05-19 17:26:31 +00:00
Tom Tsou
d6ae8648ff radioInterface: Remove UmTRX 'diversity' option
The 'diversity' option was an experimental 2 antenna receiver
implementation for UmTRX. The implementation has not been
maintained and current working status is unknown.

In addition to code rot, Coverity is triggering errors in the
associated code sections.

Removal of code cleans up many cases of special handling that
were necessary to accommodate the implementation.

Change-Id: I46752ccf5dbcffbec806081dec03e69a0fbdcdb7
2017-05-19 17:25:44 +00:00
Philipp Maier
e51a8f029e cosmetic: Add info about SSE support
The osmo-trx binary outputs no info about its SSE support status.
This commits adds some putput that informs about the SSE of the
binary and also tells which of the SSE levels the CPU supports.

Change-Id: Iacc83fd668c31644e0efb3e18962cf2870ed1daf
2017-05-19 17:23:20 +00:00
Philipp Maier
e8ae9fcf38 buildenv: Split up SSE3 and SSE4.1 code
Currently we find SSE3 and SSE4.1 code mixed togehter along with
generic code in one file. This introduces the risk that the
compiler exidantly mixes SSE4.1 instructions into an SSE3, or
even worse into a generic code path.

This commit splits the SSE3 and SSE4.1 code into separate files
and compiles them with the matching target options.

Change-Id: I846e190e92f1258cd412d1b2d79b539e204e04b3
2017-05-19 17:21:45 +00:00
Philipp Maier
f5bf33b287 buildenv: Make build CPU invariant
Currently the build environment checks which extension the current
CPU supports and picks the compiler flags accordingly.

If the build is happening on a machine that does not support the
extensions we need (SSE3, SSE4.1), the binary will lack those
extensions, even if its intended to be used on a more powerful
machine that would support the extensions.

This commit removes the CPU tests from the build process.

Change-Id: Ic913aa13c23c348ae62e78c9dfd6ed8b0a62798c
2017-05-19 17:19:39 +00:00
Philipp Maier
fe9769833f cosmetic: remove code duplication
The ARM and the X86 implementation of the conversion functions share
the same, non cpu specific implementation in separate files.

This commit removes the code duplication by putting the generic
implementation into a convert_base.c, similar to to convolve_base.c

Change-Id: Ic8d8534a343e27cde79ddc85be4998ebd0cb6e5c
2017-05-19 17:16:37 +00:00
Philipp Maier
7e07cf2346 ssedetect: Add runtime CPU detection
The current implementation can select the SSE support level during
compiletime only.

This commit adds functionality to automatically detect and switch
the SSE support level and automatically switch the Implementation
if the CPU does not support the required SSE level.

Change-Id: Iba74f8a6e4e921ff31e4bd9f0c7c881fe547423a
2017-05-19 17:12:45 +00:00
Philipp Maier
dfe0aef184 Add test program to verify convolution implementation
Convolution is a complex process and we should be able to verify
if computing results change when the implementation is touched.

This commit adds a test program that executes some testcases.
The testcases are crafted in a way that every implmentation
(several different ones for SSE) is executed once. The output
can be compared against the included .ok file.

Change-Id: Ic702ecb356c652fbcd76bee689717fb5d3526fe9
2017-05-19 16:57:25 +00:00
Philipp Maier
131f82bfac cosmetic: Make parameter lists uniform
The non-sse implementation and the sse implementation of the convert
and convolve functions have different parameter lists. This makes it
difficult to use function pointers in order to select the right
function depending on the SSE-Level and CPU.

This commit uniformizes the parameter lists in preparation for
planned runtime cpu detection support

Change-Id: Ice063b89791537c4b591751f12f5ef5c413a2d27
2017-05-02 17:17:57 +00:00
Philipp Maier
78b5627fa1 buildenv: Turn off native architecture builds
The compiler option -march=native instructs the compiler to auto-optimize
the code for the current build architecture. This is fine for building
and using locally, but contraproductive when generating binary packages.

This commit replaces -march=native with $(SIMD_FLAGS), which contains a
collection of supported SIMD options, so we won't loose the SSE support.

Change-Id: I3df4b8db9692016115edbe2247beeec090715687
2017-05-02 10:21:25 +00:00
Tom Tsou
de116e90c0 config: Remove OpenBTS style sqlite configuration
OpenBTS relies on reading in configuration values from the OpenBTS.config
sqlite3 database. This configuration method is not maintained and not
recommended for Osmocom or OpenBTS use. Command line setup is the
recommended approach.

Note that when the osmo-trx logging mechanism is replaced, the sqlite
dependency will be removed.

Change-Id: I95d7b771fde976818bee76f89163e72c3a44ecdd
2017-04-10 06:54:40 +00:00
Tom Tsou
15da7e1f7e Configuration: Fix const and signedness compile warnings
Change-Id: I701559814b2aee6f84f10e612f128da40f6a51c1
2017-04-03 18:55:02 -07:00
Tom Tsou
6031734f44 Transceiver: Remove unsigned negative compares
Change-Id: I49f30699786c52736ef334dae61f7bbd65d878d5
Fixes: Coverity CID 149353, 149356
2017-04-03 18:06:48 +00:00
Tom Tsou
5d2a36a113 Resampler: Fix non-array delete for filter taps
Change-Id: I59cdb01809da5940c74aaae9d17f413aefbf04b2
Fixes: Coverity CID 149349
2017-04-03 18:06:48 +00:00
Tom Tsou
2af14407a8 sigProcLib: Fix negative value check on unsigned value
Convert negative value check on unsigned value to zero check
to avoid potential divide-by-zero error condition.

Change-Id: Ib0d7d1bceb5fe66e69345db93a74e3e0773a2257
Fixes: Coverity CID 165059
2017-03-31 21:43:31 +00:00
Tom Tsou
92bdfb86ac sigProcLib: Check return status on downsampling
Improper length values will cause the polyphase resampler
rotation to fail. Check return and return NULL on error.

Change-Id: I3ad22f9fd7a20754f589c04258dcca3770474a9b
Fixes: Coverity CID 165235
2017-03-31 21:41:04 +00:00
Tom Tsou
ae91f13ecb sigProcLib: Remove unreachable code and no-effect checks
Unreachable path and negative value inspection on unsigned
types.

Change-Id: If53b4b03550b0a7656c808cfe96806252153eb2f
Fixes: Coverity CID 165239, 165238, 165236
2017-03-31 21:41:03 +00:00
Tom Tsou
9d53ecf666 Resampler: Fix initialization return checking
Greater-than comparison was used on boolean type.

Change-Id: Ia3b71b3a06b34a6fd781bf197ecf9d5cc1711d13
2017-03-31 21:40:23 +00:00
Alexander Chemeris
e0c12189d4 sigProcLib: Constify demodulation functions burst argument.
demodCommon() used to scale input vector in place which changed original data.
That's a bad practice and is not really necessary, so I've changed the code to
scale burst after it's copied to a new vector during a delay operation.

Change-Id: Ic45f71b634e48808356d68925bb9f5783e0bf0d3
2017-03-28 14:24:22 +00:00
Alexander Chemeris
1470fcdb5a sigProcLib: constify signalVector arguments for detectBurst() functions.
Change-Id: Ic033371a387353eb12b1827a0eb16c00c07da88a
2017-03-28 14:24:07 +00:00
Alexander Chemeris
6e1dffd486 Move Transceiver::demodulate() to sigProcLib to make it reusable.
Change-Id: I2cad47160e53f65612bd1da8998c83a0a22bce9b
2017-03-28 14:23:56 +00:00
Alexander Chemeris
0229d22d2e sigProcLib.h: Fix whitespaces. No non-whitespace changes.
The file seem to be using "2 spaces" indent, bt some lines are using
tabs which breaks formatting.

Change-Id: I7718cca45c245c9e91250ab2877f5436d4029698
2017-03-28 14:23:44 +00:00
Alexander Chemeris
f7717acd0c sigProcLib: Add operator<< to print CorrType to a string.
Change-Id: I3d68cbdab8fb504d7f155029654a576d318a201e
2017-03-28 14:23:35 +00:00
Alexander Chemeris
b34e60c105 Move BURST_THRESH from Transceiver.cpp to sigProcLib.h to make it reusable.
Change-Id: I5a888890e26858c0fbb2ddb7ef23cb0fd66a64b4
2017-03-28 14:22:01 +00:00
Alexander Chemeris
4e6c938024 Move Transceiver::detectBurst() to sigProcLib to make it reusable.
Change-Id: I3cbe8e6e4f39dde02c945e6c9086c040e276845c
2017-03-24 14:59:24 -07:00
Alexander Chemeris
4aa548f0c2 sigProcLib: rename signalError type to SignalError.
Change-Id: I1a5ae6e87d4c69945053fdefec185d0fb1a26399
2017-03-24 19:54:04 +00:00
Alexander Chemeris
f9e78beea5 Move CorrType type from Transceiver to sigProcLib.
Required to move Transceiver::detectBurst to sigProcLib.

Change-Id: I3e0e74a98bbca4d19657f50a5fb447f078663c9b
2017-03-24 19:54:04 +00:00
Alexander Chemeris
f0189c47be vector: Introduce shrink() function to shrink vector size without loosing data.
Change-Id: I9c0ac2715aea1a90c9e6ebcd982522b80a547099
2017-03-24 01:25:06 +00:00
Alexander Chemeris
c708816be1 vector: Introduce segmentMove() method to move data inside of a vector.
Change-Id: I2f3f4267b4137a0bc031f27e0f896fba9b9f3433
2017-03-24 01:24:23 +00:00
Alexander Chemeris
e56bf3a0e5 signalVector: Implement segment().
Change-Id: I6fe3aae53fb2fa5bb7637e976de6059eabe08202
2017-03-24 01:22:40 +00:00
Alexander Chemeris
38b69871ae BitVector: Convert SoftVector from 0..1 to -1..+1 soft bits.
This makes code simpler and will allow us send -127..127 soft bits towards
osmo-bts instead of 0..255 bits.

Change-Id: I16ecc3d4c829dcf0f619ad995bc9d4a4ed8af0a4
2017-03-22 18:31:22 +00:00
Alexander Chemeris
7db522b6d9 BitVector: Remove convolutional codec - we don't use it in osmo-trx.
Now we have more fexibility in how we represent SoftVector, since we
no longer depend on the particular convolutional codec implementation.

Change-Id: I3006b6a26c5eff59dbe9c034f689961802f1d0d0
2017-03-22 18:31:17 +00:00
Alexander Chemeris
ae09b04e26 CommonLibs: Print soft bits with less confidence to console when printing a soft vector.
We use other symbols to show that these bits has less confidence:
o and . for 0 with less confidence
| and ' for 1 with less confidence

Change-Id: I747a17568ee48f1f3163e8dfab2e450af85e6435
2017-03-22 18:31:12 +00:00
Alexander Chemeris
b61c610cd9 Call vectorSlicer() right before packing bits for transmission to osmo-bts.
vectorSlicer() converts soft-bits from -1..+1 to 0..1 while we want
to keep SoftVector in -1..+1 mode until the last minute, because at some
point we'll want to transmit -1..+1 to osmo-bts instead of converting it
from 0..1 back to -1..+1 on the osmo-bts side.

Plus it removes code duplication - we call it once instead of twice.

Change-Id: Idd6ddd7ac219afb0df055a692632678b66373764
2017-03-22 18:31:07 +00:00
Alexander Chemeris
132fb247b1 sigProcLib: Slice SoftVector instead of signalVector for GMSK demod.
This makes it similar to 8-PSK demod and also saves a bit of lines ofcode and
should give us a tiny improvement in performance.

Ideally we need to remove vector slicing at all, because in osmo-bts-trx
we convert back to +-1.0 again (actually to +-127, but it doesn't mater).
So we should rather transmit +-1.0 values to avoid double conversion.

Change-Id: If9ed6f0f80fbe88c994b2f9c3cae91d0d57f4442
2017-03-22 18:31:03 +00:00
Alexander Chemeris
1c0b8b355c sigProcLib: Rename demodulateBurst() to demodGmskBurst() for clarity.
Change-Id: Ibcef8d7d4a2c06865bed7e4091ccc8dbbd494d77
2017-03-22 18:30:53 +00:00
Alexander Chemeris
1dd05cf35a sigProcLib: make energyDetect() simpler by returning actual energy.
Change-Id: I9bf97f2dc03fea9bebcf43198dfb05f6e4694e9c
2017-03-22 18:09:00 +00:00
Alexander Chemeris
14d13b67dc sigProcLib: Fix documentation, sync argument names in .cpp and .h files.
Documentation in sigProcLib.h was noticeably out of sync with the actual
implementation - e.g. not all arguments were documented and arguments
which are already removed are still in the documentation. Also argument
names were different between declaration in .h and implementation in .cpp
which was confusing.

I've fixed this for detect*Burst() functions.

Change-Id: I4dfd07125d9a1e9a42a78b79faff539f003deb16
2017-03-20 18:42:32 +00:00
Alexander Chemeris
89bca9b2de radioBuffer: Remove extra ; at the end of inline function definitions.
Change-Id: I8911adf0a0bb1ae828ac9cdf1a76c904639f6c06
2017-03-20 18:41:02 +00:00
Alexander Chemeris
9270a5aa2e sigProcLib: Typo sybols -> symbols
Change-Id: I8cbef852374d0458c4f4ad4be0df0aa998e3796a
2017-03-20 17:36:06 +00:00
Alexander Chemeris
4793f4679b CommonLibs: Remove unused files.
Change-Id: I2bfb45a1c7d01785bdb30204dba38c683a4288a9
2017-03-20 17:32:04 +00:00
Max
802b86502d Add autoconf-archive to dependencies
We use AX_EXT in ./configure for checking CPU features anyway, so it's
better to add it as explicit dependency.

Related: OS#1923
Change-Id: I7ba48e1df4ede8b477574da3faa15fd02e15c69b
2017-03-14 18:49:55 +01:00
Tom Tsou
a93f789e50 uhd: Increase MC-BTS FPGA clock rate to 51.2 MHz
Addresses following issues where UHD 3.9 and likely other UHD versions
would report a master clock (FPGA) rate error. Update MC-BTS FPGA clock
for B200 and B210 to 51.2 MHz, which is supported by all UHD versions.
Only B200/B210 is supported for MC-BTS operation.

https://osmocom.org/issues/1963
https://osmocom.org/issues/1648

ALERT UHDDevice.cpp:548:set_master_clk: Failed to set master clock rate
ALERT UHDDevice.cpp:549:set_master_clk: Requested clock rate 3.2e+06
ALERT UHDDevice.cpp:550:set_master_clk: Actual clock rate 5e+06

Change-Id: I78fb2c0959abd0e666628ba39f433162aafb067e
2017-03-07 17:54:06 -08:00
Tom Tsou
72bf762b42 uhd: Add support for UHD-3.11 logging control
The logging API changes in UHD-3.11, which causes build failure if
not properly handled.

Change-Id: I223ebb9fae3f4061e0cb37c05263c1b569e8f628
2017-03-07 14:25:05 -08:00
Max
2dee3e996e Fix building against sqlite3
* Explicitly check for sqlite3 at configure stage, remove old include
  dir, fix header inclusion.
* Use configure results for linking instead of hardcoded linker option
  for sqlite.
* Add dependency on -dev package for .deb

Change-Id: I6d7f697d67651f02ceb77fc4da4317b64fa47f9e
Fixes: OS#1928
2017-01-26 17:06:06 +01:00
Max
1f1cebb2e5 Remove embedded sqlite3
Previous patch switches to using system-wide sqlite3 so it's safe to
remove local copy now.

Change-Id: Ie8e751cc62132fe1f7748ccd78c5d48469027329
2017-01-24 15:15:30 +01:00
Ruben Undheim
d1b28bd766 Do not embed sqlite3 when building
Change-Id: If5edadc04c3ff953b451676e55ad3d00d4e43c82
2017-01-24 15:15:24 +01:00
Max
833e97e9ba Integrate Debian packaging changes
debian/control:
    * restructure to make it easier to incorporate further changes
    * update package descriptions
    * update project URL

debian/rules:
    * use proper hardening syntax

debian/copyright: update to match Debian format

Change-Id: I9a89e7311c8632ae26ac2e6c02d1e427d94b1608
Related: OS#1694
2017-01-24 15:14:32 +01:00
Max
e6d059f0c9 Add gerrit settings
Make it simple to setup and use this repo with 'git review' command.

Change-Id: I6bbe65cc09e086685995f084a07a646a7d60b93c
2017-01-24 15:03:48 +01:00
Holger Hans Peter Freyther
012a1b345b debian: Require fftw3 header files for osmo-trx
Install missing development package to get osmo-trx to build.

Change-Id: Id80937724d5e4da4ed555cbabfcd3e2457cb2a19
2017-01-24 14:47:10 +01:00
Tom Tsou
80cb08071b Revert "uhd: Set minimum supported version to 3.9.0"
This reverts commit 93ca09ea61.

Ettus Research recommends the use of 3.9 series of UHD releases,
but requiring this version has lead to issues with broken OBS and
packaged binaries by Debian, Ubuntu, and other distributions.

Change-Id: Ie6b175ac6d46d091937380c79fdd0125b16ec75f
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2017-01-19 13:48:15 -08:00
Tom Tsou
44c7f41d75 uhd: Add X300 sample timing for 4 SPS
Previously only 4/1 and 1/1 configurations Tx/Tx samples-per-symbol
were supported.

Change-Id: I9153171fe3af95e1cb0d9d35a9287dfde155d184
2017-01-11 13:30:42 -08:00
pierre.baudry
9436fbbf3c transceiver: Fix mismatched allocations and deallocations
The behaviour of a mismatched pair of allocation and deallocation is undefined
Also fixes a memory leak if malloc fails (which stops the application anyway)

Change-Id: I9c8bbade8531e8c9c02dcd43bac38cb954b3c89f
2016-10-26 08:54:13 +02:00
Tom Tsou
93ca09ea61 uhd: Set minimum supported version to 3.9.0
Versions of UHD prior to 3.9.0 are no longer supported.

Rather then backport and ifdef UHD version specific API and
behavioral changes, set minimum support to the current LTS
release, which is 3.9.0.

Change-Id: Id7d15b52cd4e45f1d856a6ef3a84832a28f2dd04
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-10-25 18:48:52 +00:00
Tom Tsou
365bc38bee transceiver: Fix command build warning
Place conditional brackets on handover table reset. Reset table
only on successful start or restart.

Change-Id: I74032b49785bd68835a0a68cb0f14cdaab4fcd26
2016-10-25 18:48:25 +00:00
Tom Tsou
43242efc85 uhd: Add missing B200 sample timing for 4 SPS receive
Only B210 timing was previously set in the timing table.

Change-Id: Idc8d1be8201ad086c57dd9b3d6d72d277306cd2b
2016-10-18 11:30:09 -07:00
Neels Hofmeyr
76b98cf236 add contrib/jenkins.sh, for gerrit build bot
Change-Id: If8887525b0062090e9445a35e32b03e3b8e7eede
2016-10-17 17:53:26 +02:00
Tom Tsou
aa15d62a8c sigproc: Match differential GMSK start/end bits to tail bits
Invert the initial and trailing diffential bits in the 4 sps
GMSK modulator to fix power-time mask irregularity at start
and end of burst. GSM tail bits are always zero, so we can
safely use fixed bit values.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-08-11 14:44:57 -07:00
Neels Hofmeyr
2e5e2c537b 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.
2016-08-08 11:55:41 -07:00
Tom Tsou
8f0ccf618d uhd: Update USRP2/N200/N210 for 4 SPS Rx
Requires changing the radioInterface API to pass in Rx side SPS
value. Update the (deprecated) diversity configuration to match
as well.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-20 16:36:27 -07:00
Tom Tsou
06676ead63 sigproc: Fix missing 8-PSK tail symbols
Agilent E4406A measurement of TSC synchronized RF envelope measurement.
showed failing power levels at the tail end of EDGE bursts. Regression
traced back to following commit.

Commit d2b070369d
"uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts"

In the patch, the EDGE burst was delayed one symbol, but erroneously
truncated at the tail end causing the failing RF envelope measurement.

The missing tail symbol did not appear to affect end-to-end EGPRS tests.
This patch corrects the truncation.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-19 12:56:24 -07:00
Tom Tsou
4609f3285c transceiver: Fix 4 SPS receive TOA value
The time-of-arrival (TOA) value out of sigProc is specified
in symbols or, equivalently, 1 sample per symbol and does
not need to be normalized.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-19 11:30:49 -07:00
Tom Tsou
7c741ec6a6 transceiver: Fix mixed GSMK / 8-PSK transmission
Input burst construction was declared static causing the first
downlink burst from upstream to determine subsequent burst size
and modulation. Consequently, fixed sequence EGPRS tests would
pass, however, switching between 8-PSK and GMSK bursts would
fail with only one modulation type being transmitted.

Internally generated test sequences '-r' option were not affected
because the bursts are not received through the socket interface.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-19 11:26:05 -07:00
Tom Tsou
2f3e60bc1f uhd: Add command line option for GPS reference
Unlike earlier versions of UHD, the current release (3.9.2)
does not automatically select on-board GPSDO as the reference
source. Modify the command line settings to allow explicit
selection of GPS in addition to the external setting.

Simultaneous GPS and external reference settingis disallowed.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-17 19:34:22 -07:00
Alexander Chemeris
cbfef6e40a UHD: Initial LimeSDR support.
Only EDGE mode is currently supported. Traditional 1 SPS Rx / 4 SPS Tx
mode requires different sampling rates which is not currently working.

RF performance is also sub-optimal and requires more tuning.
2016-07-12 16:26:51 -07:00
Tom Tsou
b577ef014f radioInterface: Fix multi-channel buffer index bug
Discrete RF multi-channel was using hard coded buffer index
on the channel iteration for transmit sample conversion. End
result was segmentation fault on dual RF channel devices with
both channels active (Ettus B210 and UmTRX).

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-12 16:11:13 -07:00
Tom Tsou
c37594f3b9 mcbts: Allow out of order channel setup
Previous checks on multi-channel TSC and ARFCN settings would fail
if channels were initialized out of order. Namely, if channel 0
was not configured first, osmo-trx would error on the control
interface leading osmo-bts to fail.

Allow global TSC setting on all channels with added logging notice.
Notify if channel frequency is unexpected - which may happen if
channels are setup out of order - but do no report as error.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-08 14:46:25 -07:00
Tom Tsou
ffee30d190 transceiver: Remove HANDOVER warnings
These warnings simply echo the socket command arguments with no
indication of any unexpected or improper operation.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-07 19:39:18 -07:00
Neels Hofmeyr
24575a6530 configure.ac: check for boost/config.hpp header 2016-07-05 15:43:19 -07:00
Neels Hofmeyr
1e9801411b add basic .gitignore 2016-07-05 15:42:35 -07:00
Tom Tsou
64464e6c34 egprs: Enable 8-PSK burst detection when EDGE is enabled
The command line EDGE option will enable 8-PSK burst
detection on any slot where a normal burst is expected.
The burst search order is 8-PSK first followed by GMSK.

EDGE will force 4 SPS sampling on Tx and Rx. Along with
twice the search correlation from 8-PSK and GMSK, EDGE
will increase CPU utilization. Whether the increase is
notable or not is dependent on the particular machine.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-01 03:50:02 -07:00
Tom Tsou
e88710881b egprs: Enable 8-PSK length vectors on the Tx interface
Allow EGPRS 8-PSK length bit vectors of length 444 (148 * 3)
to pass in through the Tx socket interface. Length is the sole
factor in determining whether to modulate a bit vector using
GMSK or 8-PSK.

Tested with 8-PSK training sequences with random payload
originating from osmo-bts. Output verified with Agilent E4406A.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-01 03:16:03 -07:00
Tom Tsou
a84e162672 sigproc: Adjust burst detection threshold criteria
Reduce the burst detection threshold to pass more bursts to upper
layers, but force stricter requirements on the computation itself.
For the latter, we now require at least 5 samples (rather than 2)
to compute a peak-to-average value.

End result is increased burst detection at low SNR conditions with
a small increase in false positive bursts when no signal is present.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-01 03:14:29 -07:00
Tom Tsou
7676427816 mcbts: Add multi-ARFCN radio support
Add new radio interface "radioInterfaceMulti" for multi-carrier
support.

Only USRP B200/B210 devices are supported because of sample
rate requirements (3.2 Msps).

Only 4 SPS operation Tx/RX is supported.

8-PSK is supported.

Other options may be added at a later time

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-01 03:14:15 -07:00
Tom Tsou
35222296fe mcbts: Add multi-ARFCN channelizing filters
Introduce polyphase channelizer (Rx) and synthesis (Tx) filterbanks,
which serve as the signal processing backend for multi-carrier GSM.

Fast Fourier Transform (FFT) is used internally. FFTW is added as
a new build dependency.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-01 03:07:27 -07:00
Tom Tsou
28670fb5da iface: Add inner ring-buffer implementation
Two buffers, inner and outer, are used in the transceiver
implementation. The outer buffer interfaces with the device receive
interface to guarantee timestamp aligned and contiguously allocated
sample buffers. The inner buffer absorbs vector size differences between
GSM bursts (156 or 157 samples) and the resampler interface (typically
fixed multiples of 65).

Reimplement the inner buffer with a ring buffer that allows fixed size
segments on the outer (resampler) portion and variable lengths (GSM
side) on the inner side. Compared to the previous stack-like version,
this implementation removes unnecessary copying of buffer contents.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-01 03:03:11 -07:00
Tom Tsou
05c6feb71d radioInterface: Convert diversity argument to general type
Rather than a simple bool type, convert the diversity switch
to the device interface specifer:

  enum InterfaceType {
    NORMAL,
    RESAMP_64M,
    RESAMP_100M,
    DIVERSITY,
  };

The more general specifier allows passing in special cases
other then selection diversity such as multi-ARFCN support.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-01 03:03:03 -07:00
Tom Tsou
2e4ed10722 transceiver: Add Rx samples-per-symbol option
Previous approach was to enable 4 SPS on the receive path only
for EDGE use, which is not a requirement for 4 SPS operation.
Make the 4 SPS configuration setting directly settable.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-07-01 03:02:56 -07:00
Tom Tsou
c8c4eac55e transceiver: Do not report error on SETTSC when radio is on
OsmoTRX does not support the use of multiple TSC settings per
internal TRX instance. There should not be an error to modifiy
the TSC value after POWERON. Setting TSC value on TRX channels
other then 0 is a NOP operation that should only error if the
requested TSC differs from that of TRX channel 0.

Reported-by: Max <msuraev@sysmocom.de>
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-06-28 17:08:34 -07:00
Alexander Chemeris
37c52c79cf transceiver: Add an option to emulate a RACH delay in random filler mode.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-06-22 15:18:13 -07:00
Alexander Chemeris
58e9591f9e transceiver: Log channel number in DEBUG output of demoded bursts.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-06-22 14:28:22 -07:00
Alexander Chemeris
19174f581b radioInterface: Initialize power scale with a meaningful default.
Right now if you forget to send "POWER" control command, osmo-trx
will transmitt zeros. This is counter-intuitive and I've spent several
hours debugging this "issue". The issue may happen easily, because
osmo-bts doesn't send "POWER" command if there is no "power" setting
in the configuration file. Given that "POWER" command actually sets
attenuation, it's percieved as optional and in absence of it should
default to "POWER 0" (no attenuation), which translates to power
scale being 1.0.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-06-22 14:28:07 -07:00
Alexander Chemeris
1ba69e7762 uhd: Fix comment.
It's osmo-trx, not OpenBTS anymore.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-06-22 14:27:38 -07:00
Alexander Chemeris
f931cf226b radioDevice: GSMRATE macro must have parentheses around its definition.
So we had the following define:
   #define GSMRATE       1625e3/6

Now, I wanted to use it in the following expression:
   3.0/GSMRATE
which turns into:
   3.0/1625e3/6
while what I really wanted is:
   3.0/(1625e3/6) = 3.0/1625e3*6

To avoid this, all macros with calculations must be enclosed in parentheses.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-06-22 14:27:24 -07:00
Alexander Chemeris
e476231deb makefile: Fix build from an external path.
When you build from an external path, compiler can't find convert.h
include, because it was specified relative to the current directory.
Change this to specify the include dit relative to the Makefile
location.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-06-22 14:27:00 -07:00
Tom Tsou
e90c24c8d5 sigproc: Expand RACH, TSC, and EDGE correlation windows
Slightly widen the search range to accommodate timing jitter
on certain classes of devices. The expanded range minimizes
the possibility of missing bursts that arrive too early or
too late due to timing error.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-06-21 16:19:54 -07:00
Tom Tsou
3b093bb13b uhd: Set minimum UHD version requirement for E3XX
Create runtime version check for minimum supported UHD driver
when using USRP E3XX devices. The minimum version, 3.9.0, matches
supported version on current E3XX release images.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-05-03 19:06:56 -07:00
Tom Tsou
3f4a13f049 uhd: Make device offset check a private method
Removes extra arguments and a static call.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-05-03 19:04:00 -07:00
Tom Tsou
0fe41a583c uhd: Set default Tx sampling to 4 sps
The majority of GSM host platforms are capable of operating with
the 4x oversampled modulator, which justifies the new default
setting. The small number exceptions (e.g. Raspberry Pi) can still
use the lower complexity 1 sps modulator with the '-s 1' command
line option if required.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-05-03 15:17:39 -07:00
Tom Tsou
a5e0f1cdba uhd: Update default E3XX settings
Tune timing values after testing on UHD 003.009.002 for E3XX.
Table value for 1 sps was off by 10 samples causing improper
operation. Table value for 4 sps was shifted by 1 sample for
more accurate timing.

Also update E3XX description string detection.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-05-03 15:14:06 -07:00
Tom Tsou
2c650a6895 common: Add mandatory length field to UDP receive calls
Current UDP receive reads up to MAX_UDP_LENGTH bytes into the
passed in buffer, which may lead to buffer overflow if the
write buffer is of insufficient size.

Add mandatory length argument to UDP socket receive calls.

Reported-by: Simone Margaritelli <simone@zimperium.com>
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-05-02 17:37:05 -07:00
Tom Tsou
d4555f267e common: Restrict UDP binding to localhost only
Reported security vulnerability where control and data UDP
packets can be injected into the transceiver externally due
to socket binding to all interfaces using INADDR_ANY.

Existing socket interface does not allow specifying local
address; only the local port and remote address/port are
arguments.

Restrict socket bind to localhost with INADDR_LOOPBACK. If
external interfaces do need to be used, the API should be
modified to allow specifying the local socket address.

Reported-by: Simone Margaritelli <simone@zimperium.com>
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-05-02 17:35:01 -07:00
Tom Tsou
047956259b EDGE: Fix demodulation slicer input
EDGE 8-PSK soft slicer was receiving input from the output of the
downsampler. Equalization and derotation were missing causing the
soft symbol output to be invalid.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-04-26 21:20:43 -07:00
Tom Tsou
d2b070369d uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts
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.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-04-26 19:31:14 -07:00
Alexander Chemeris
9664c3a6e7 transceiver: Do not pass transceiver state struct to function where it's not used. 2016-04-26 12:03:20 -07:00
Alexander Chemeris
1ab5e7f7bc osmo-trx: Output Rx SPS as a part of configuration output. 2016-04-26 12:02:51 -07:00
Alexander Chemeris
5efe05021a transceiver: Add an option to generate random Access Bursts. 2016-04-20 13:46:02 -07:00
Alexander Chemeris
78d1fc9a13 transceiver: Properly handle MAXDLY.
Previously MAXDLY value was applied to Normal Bursts, which was nice
when working with sloppy test equipment like CMD57, but useless for
real world usage. At the same time documentation and de facto usage
of MAXDLY in OsmoBTS and OpenBTS assumed that it actually applies to
Access Bursts (RACH). So this patch changes osmo-rx behavior to apply
MAXDLY to RACH bursts and introduces a new command MAXDLYNB for the
old behavior.
2016-04-20 13:45:00 -07:00
Alexander Chemeris
a8cf208616 Common: Make sure gLogEarly() log to the same facilities as the normal log. 2016-04-20 12:33:41 -07:00
Alexander Chemeris
f84232d30a Common: Get rid of a compilation warning.
debugLogEarly was replaced to an empty space and arguments of the function
became operators, grouped together by ():
Configuration.cpp: In member function 'bool ConfigurationTable::defines(const string&)':
Configuration.cpp:272:28: warning: left operand of comma operator has no effect [-Wunused-value]
   debugLogEarly(LOG_ALERT, "configuration parameter %s not found", key.c_str());
                            ^

This fix removes debugLogEarly together with its arguments.
2016-04-20 12:26:57 -07:00
Tom Tsou
9bd649ec73 EDGE: Fix USRP B210 device support
Commit 871b8782 "EDGE: Add support for UmTRX" disabled B210 support
using EDGE. Add B210 explicitly to the timing offset table to avoid
this issue.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-23 17:20:08 -07:00
Alexander Chemeris
871b87829f EDGE: Add support for UmTRX.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-22 11:01:34 -07:00
Holger Hans Peter Freyther
d17b189cbc debian: Add packaging to master taken from fairwaves/master
There doesn't seem to be a reason why this shouldn't be in master.
The fairwaves/master branch is removing --march=native as well that
looks like a good idea as well.
2016-03-18 20:22:58 +01:00
Tom Tsou
7fec3030d4 EDGE: Combine shared GMSK and 8-PSK demodulator sections
Timing recovery and single tap channel compensation are identical
in both GMSK and EDGE receivers. This is the section ahead of and
including the optional 4-1 downsampler. GMSK and EDGE specific
sections operate at 1 SPS.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:45:53 -08:00
Tom Tsou
af717b2d3c EDGE: Add random burst generator filler option
When EDGE is enabled with the '-e' option, the random burst generator
switches from GMSK normal bursts to 8-PSK EDGE bursts.

  $ ./osmo-trx -e -r 7

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:45:53 -08:00
Tom Tsou
8ee2f38a87 sigproc: Add various GSM burst generators
Setup generators for empty, random, and dummy bursts. This moves error
prone burst length handling out of the Transceiver and into the signal
processing core.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:45:53 -08:00
Tom Tsou
4dfd64aa9e sigproc: Always use 625 sample length bursts with 4 SPS
At 4 samples per symbol, we don't need to maintain the 156/157 sample
slot structure to account for the GSM 156.25 sample burst length.
Set the 4 SPS Laurent modulator to ignore the guard interval setting
and always output 625 sample sized bursts. The EDGE 8-PSK modulator
already has this behavior.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:45:53 -08:00
Tom Tsou
b0aefcbf47 EDGE: Add interfaces to enable EDGE transceiver
Create EDGE slot type in the Transceiver. When EDGE mode is enabled
for a particular slot, blind detection will be performed by
correlating against EDGE followed by normal bursts if no EDGE burst
is found.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:44:53 -08:00
Tom Tsou
d325343ecc EDGE: Add 8-PSK modulator and demodulator
Setup correlator and detection process similar to the GMSK
receiver chain. Require 4 SPS sampling on both Rx and Tx paths
as 1 SPS sampling adds too much distoration for 8-PSK recovery.
Core receiver operations still run at 1 SPS with the exception
of fractional delay filtering, which runs at the higher rate.

Perform linear equalization to handle the Gaussian pulse
induced ISI. The fixed impulse response used for equalizer tap
calculation consists of combined EDGE pulse shape filter and
effects of the downsampling filter. Note that the non-adaptive
equalizer corrects for modulation induced band limiting and
does not account for or compensate for fading channel effects.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-06 20:29:27 -08:00
Tom Tsou
5cd70dc4ec EDGE: Setup variable sampling on receive path
Allow setting the device to non single SPS sample rates - mainly
running at 4 SPS as the signal processing library does not support
other rates. Wider bandwith support is required on the receive path
to avoid 8-PSK bandlimiting distortion for EDGE.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-06 19:11:05 -08:00
Tom Tsou
465694027b sigproc: Remove normal burst DFE equalizer
DFE equalizer is unused and has been experiencing code rot for
multiple years. The effect is a significant amount of baggage being
carried in the Transceiver and interfaces.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-06 19:10:59 -08:00
Tom Tsou
2079a3c664 sigproc: Remove dynamic SPS configuration
Samples per symbol used by the transceiver is not configurable through
the socket interface once running, so stop pretending like it could be.
Initialize all tables and midambles at start.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-06 19:10:46 -08:00
Tom Tsou
99cf930f9a Transceiver52M: Fix ARM build issues
Patch f147b174 "sigproc: Make convolution and convert input buffers
immutable" changed the internal conversion interface with the addition
of the const type qualifier. This change was not reflected on ARM builds
which led to build failure. Add const qualifier to resolve build issue.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-11-09 12:06:33 -08:00
Tom Tsou
283b22dbce uhd: Remove references to USRP B205
Certain pre-release versions of the B200mini used the B205 naming, which no
longer exists. Update device naming and detection to reflect current UHD
product names.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-10-21 17:13:24 -07:00
Tom Tsou
f147b17447 sigproc: Make convolution and convert input buffers immutable
For good practice, use const specifier when applicable.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2015-08-21 19:31:24 -07:00
Tom Tsou
d4d3daa12e uhd: Use internal UHD tick conversions
UHD handles built in tick and floating point timestamp conversion
since version 003.005.004. This removes the need for separate UHD
timespec to tick conversion.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-08-21 19:29:38 -07:00
Tom Tsou
c312905f43 uhd: Add version 3.9.0 support
New functionality includes B200-mini device support and updated
timing values to match FPGA changes.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-08-21 19:20:32 -07:00
Alexander Chemeris
c4eab8795f uhd: Output Rx/Tx gain limits to log to make it more transparent.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:25:17 -07:00
Alexander Chemeris
cc6f79b1c0 Logger: Output ERR log messages to stderr as well.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:24:20 -07:00
Alexander Chemeris
5a0680655f Transceiver: Add support for OsmoBTS style handover.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:24:08 -07:00
Alexander Chemeris
3722920100 Transceiver: Fix whitespace.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:23:32 -07:00
Alexander Chemeris
f3b9af65ed uhd: Fix UmTRX tuning broken in commit 90f7a01d.
Commit 90f7a01d lost "return" statement. We also should account the fact that
offset can be negative.
We should return the tuning request immediately after

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:23:24 -07:00
Alexander Chemeris
e692ce986c transceiver: Add a debug option to dump selected timeslots to disk.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:59 -07:00
Alexander Chemeris
81c6873205 Transceiver: Do not update state->SNRestimate if equalization is disabled.
This also fix a bug of using bool type for noise instead of float.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:41 -07:00
Alexander Chemeris
c052aa1d4c uhd: Fix rounding error in timestamp conversion functions.
Rounding error introduced oscilating timing advance error by regularly
overwriting one bit and then skipping one bit.

This commit also adds an error message to show up in logs if this ever
happens again.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:28 -07:00
Alexander Chemeris
130a8007fa sigProcLib: Abstract out common part of Normal/RACH burst detection.
As a side change - get rid of passing toa and amp arguments as pointers and use
references instead.

The commit doesn't change behaviour, but makes the code cleaner.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:13 -07:00
Alexander Chemeris
72e8619632 Checking in clockdump.sh utility.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:20:22 -07:00
Alexander Chemeris
2beb1adcea Checking in a more relevant README.
This READMY is from the OpenBTS's TRXManager and actually describes the transceiver
API and behavior.
2015-07-30 14:19:51 -07:00
Alexander Chemeris
2b542100a0 Transceiver: Update noise level only when the timeslot is marked as IDLE.
We can't rely on an assumption that if we can't decode a burst - it's noise.
There are many rasons why we can't decode a burst even if it's well above the
noise level. Just one example is a RACH burst which can be overlapped with
another RACH burst up to a level both are completely unrecognizable. Another
example is when a burst is destroyed by bad multi-path.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:32 -07:00
Alexander Chemeris
2268c8558c transceiver: Remove noise/RSSI gating.
It does more harm than good. the current noise calculation is too error
prone, so we can't trust it. And we end up loosing perfectly good bursts
because of that.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:17 -07:00
Alexander Chemeris
50747dc65d osmo-trx: Add an option to swap channels on UmTRX.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:06 -07:00
Alexander Chemeris
1e9b4d57da sigProcLib: Check for bogus TOA before using it.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:18:31 -07:00
Alexander Chemeris
954b118bfa Transceiver: Fix clipping detection.
There are two primary changes in this commit:

1) Return values of detect functions changed form bool to int to actually pass
the return value from the inner function and notify higher levels about clipping.
Previously the information was lost due to conversion to bool.

2) Clipping level is not the final verdict now. We still try to demod a burst
and mark it as clipped only if the level is above the clipping level AND we can't
demod it. The reasoning for this is that in real life we want to do as much as
possible to demod the burst, because we want to get as much from our dynamic
range as possible. So a little bit of clipping is fine and is expected. We just
don't want too much of it to break our demod.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:17:59 -07:00
Alexander Chemeris
dbe26abcb9 Transceiver: Print noise level for each burst in debug mode.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:17:56 -07:00
Alexander Chemeris
e8905a03a5 osmo-trx: Add a command line option for the dBFS to dBm offset.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:14:33 -07:00
Alexander Chemeris
909ffbfd23 Common: Use a scoped lock in the Logger to avoid deadlock on thread cancel.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:14:23 -07:00
150 changed files with 8483 additions and 153711 deletions

52
.gitignore vendored Normal file
View File

@@ -0,0 +1,52 @@
# build results
*.o
*.lo
*.la
Transceiver52M/osmo-trx-uhd
Transceiver52M/osmo-trx-usrp1
# tests
tests/CommonLibs/BitVectorTest
tests/CommonLibs/F16Test
tests/CommonLibs/InterthreadTest
tests/CommonLibs/LogTest
tests/CommonLibs/RegexpTest
tests/CommonLibs/SocketsTest
tests/CommonLibs/TimevalTest
tests/CommonLibs/URLEncodeTest
tests/CommonLibs/VectorTest
tests/CommonLibs/PRBSTest
tests/Transceiver52M/convolve_test
# automake/autoconf
*.in
.deps
.libs
.dirstamp
*~
Makefile
config.log
config.status
config.h
config.guess
config.sub
config/*
configure
compile
aclocal.m4
autom4te.cache
depcomp
install-sh
libtool
ltmain.sh
missing
stamp-h1
INSTALL
tests/package.m4
tests/testsuite
tests/atconfig
tests/testsuite.dir
tests/testsuite.log
# vim
*.sw?

3
.gitreview Normal file
View File

@@ -0,0 +1,3 @@
[gerrit]
host=gerrit.osmocom.org
project=osmo-trx

83
AUTHORS
View File

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

28
COPYING
View File

@@ -673,16 +673,16 @@ on the AGPLv3 text.
=========================================================================
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX
Permissive Terms Supplementing the License
1. Remote Interaction Through IP Networks.
OpenBTS includes an implementation of the GSM network cellular air interface,
OsmoTRX is an implementation of the GSM network cellular air interface,
as well as other interfaces to IP networks. The interaction of cellular
handsets with the OpenBTS software is considered "remote network interaction"
handsets with the OsmoTRX software is considered "remote network interaction"
for the purposes of the Affero General Public License and cellular users are
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
Network Interaction; Use with the GNU General Public License").
@@ -694,17 +694,6 @@ interfaces other than the GSM air interface from the requirements of Section 13
is an additional permission granted to you.
Non-Permissive Terms Supplementing The License
1. Trademarks.
"OpenBTS" is a trademark of Range Networks, Inc., registered with
the US Patent and Trademark Office. Your use of OpenBTS software under a GPL
license does not include the right to use the OpenBTS trademark in commerce.
This additional non-permissive term is consistent with Section 7 of the AGPLv3
license.
END OF ADDITIONAL TERMS
@@ -712,13 +701,8 @@ END OF ADDITIONAL TERMS
How to comply with Section 13 of the AGPLv3 license.
The recommended method for compliance with Section 13 of the AGPLv3 license is
to deliver a text message to each handset that attaches to the OpenBTS cellular
network. At a minimum, that text message should include the string "OpenBTS
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
to deliver a text message to each handset that attaches to the cellular
network which uses OsmoTRX. At a minimum, that text message should include the string
"OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This
message need not be delivered to handsets that are denied registration with the
network, since those handsets have been denied service.
In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message"
feature. See the OpenBTS.config.example file for more information on the use of
this feature for AGPLv3 compliance.

View File

@@ -30,6 +30,7 @@
#include <iostream>
#include <stdio.h>
#include <sstream>
#include <math.h>
using namespace std;
@@ -199,49 +200,6 @@ void BitVector::LSB8MSB()
uint64_t BitVector::syndrome(Generator& gen) const
{
gen.clear();
const char *dp = mStart;
while (dp<mEnd) gen.syndromeShift(*dp++);
return gen.state();
}
uint64_t BitVector::parity(Generator& gen) const
{
gen.clear();
const char *dp = mStart;
while (dp<mEnd) gen.encoderShift(*dp++);
return gen.state();
}
void BitVector::encode(const ViterbiR2O4& coder, BitVector& target)
{
size_t sz = size();
assert(sz*coder.iRate() == target.size());
// Build a "history" array where each element contains the full history.
uint32_t history[sz];
uint32_t accum = 0;
for (size_t i=0; i<sz; i++) {
accum = (accum<<1) | bit(i);
history[i] = accum;
}
// Look up histories in the pre-generated state table.
char *op = target.begin();
for (size_t i=0; i<sz; i++) {
unsigned index = coder.cMask() & history[i];
for (unsigned g=0; g<coder.iRate(); g++) {
*op++ = coder.stateTable(g,index);
}
}
}
unsigned BitVector::sum() const
{
unsigned sum = 0;
@@ -287,148 +245,12 @@ ostream& operator<<(ostream& os, const BitVector& hv)
ViterbiR2O4::ViterbiR2O4()
{
assert(mDeferral < 32);
mCoeffs[0] = 0x019;
mCoeffs[1] = 0x01b;
computeStateTables(0);
computeStateTables(1);
computeGeneratorTable();
}
void ViterbiR2O4::initializeStates()
{
for (unsigned i=0; i<mIStates; i++) clear(mSurvivors[i]);
for (unsigned i=0; i<mNumCands; i++) clear(mCandidates[i]);
}
void ViterbiR2O4::computeStateTables(unsigned g)
{
assert(g<mIRate);
for (unsigned state=0; state<mIStates; state++) {
// 0 input
uint32_t inputVal = state<<1;
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
// 1 input
inputVal |= 1;
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
}
}
void ViterbiR2O4::computeGeneratorTable()
{
for (unsigned index=0; index<mIStates*2; index++) {
mGeneratorTable[index] = (mStateTable[0][index]<<1) | mStateTable[1][index];
}
}
void ViterbiR2O4::branchCandidates()
{
// Branch to generate new input states.
const vCand *sp = mSurvivors;
for (unsigned i=0; i<mNumCands; i+=2) {
// extend and suffix
const uint32_t iState0 = (sp->iState) << 1; // input state for 0
const uint32_t iState1 = iState0 | 0x01; // input state for 1
const uint32_t oStateShifted = (sp->oState) << mIRate; // shifted output
const float cost = sp->cost;
sp++;
// 0 input extension
mCandidates[i].cost = cost;
mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask];
mCandidates[i].iState = iState0;
// 1 input extension
mCandidates[i+1].cost = cost;
mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask];
mCandidates[i+1].iState = iState1;
}
}
void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost)
{
const float *cTab[2] = {matchCost,mismatchCost};
for (unsigned i=0; i<mNumCands; i++) {
vCand& thisCand = mCandidates[i];
// We examine input bits 2 at a time for a rate 1/2 coder.
const unsigned mismatched = inSample ^ (thisCand.oState);
thisCand.cost += cTab[mismatched&0x01][1] + cTab[(mismatched>>1)&0x01][0];
}
}
void ViterbiR2O4::pruneCandidates()
{
const vCand* c1 = mCandidates; // 0-prefix
const vCand* c2 = mCandidates + mIStates; // 1-prefix
for (unsigned i=0; i<mIStates; i++) {
if (c1[i].cost < c2[i].cost) mSurvivors[i] = c1[i];
else mSurvivors[i] = c2[i];
}
}
const ViterbiR2O4::vCand& ViterbiR2O4::minCost() const
{
int minIndex = 0;
float minCost = mSurvivors[0].cost;
for (unsigned i=1; i<mIStates; i++) {
const float thisCost = mSurvivors[i].cost;
if (thisCost>=minCost) continue;
minCost = thisCost;
minIndex=i;
}
return mSurvivors[minIndex];
}
const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs)
{
branchCandidates();
getSoftCostMetrics(inSample,probs,iprobs);
pruneCandidates();
return minCost();
}
uint64_t Parity::syndrome(const BitVector& receivedCodeword)
{
return receivedCodeword.syndrome(*this);
}
void Parity::writeParityWord(const BitVector& data, BitVector& parityTarget, bool invert)
{
uint64_t pWord = data.parity(*this);
if (invert) pWord = ~pWord;
parityTarget.fillField(0,pWord,size());
}
SoftVector::SoftVector(const BitVector& source)
{
resize(source.size());
for (size_t i=0; i<size(); i++) {
if (source.bit(i)) mStart[i]=1.0F;
else mStart[i]=0.0F;
else mStart[i]=-1.0F;
}
}
@@ -438,102 +260,20 @@ BitVector SoftVector::sliced() const
size_t sz = size();
BitVector newSig(sz);
for (size_t i=0; i<sz; i++) {
if (mStart[i]>0.5F) newSig[i]=1;
if (mStart[i]>0.0F) newSig[i]=1;
else newSig[i] = 0;
}
return newSig;
}
void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
{
const size_t sz = size();
const unsigned deferral = decoder.deferral();
const size_t ctsz = sz + deferral*decoder.iRate();
assert(sz <= decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
uint32_t history[ctsz];
{
BitVector bits = sliced();
uint32_t accum = 0;
for (size_t i=0; i<sz; i++) {
accum = (accum<<1) | bits.bit(i);
history[i] = accum;
}
// Repeat last bit at the end.
for (size_t i=sz; i<ctsz; i++) {
accum = (accum<<1) | (accum & 0x01);
history[i] = accum;
}
}
// Precompute metric tables.
float matchCostTable[ctsz];
float mismatchCostTable[ctsz];
{
const float *dp = mStart;
for (size_t i=0; i<sz; i++) {
// pVal is the probability that a bit is correct.
// ipVal is the probability that a bit is incorrect.
float pVal = dp[i];
if (pVal>0.5F) pVal = 1.0F-pVal;
float ipVal = 1.0F-pVal;
// This is a cheap approximation to an ideal cost function.
if (pVal<0.01F) pVal = 0.01;
if (ipVal<0.01F) ipVal = 0.01;
matchCostTable[i] = 0.25F/ipVal;
mismatchCostTable[i] = 0.25F/pVal;
}
// pad end of table with unknowns
for (size_t i=sz; i<ctsz; i++) {
matchCostTable[i] = 0.5F;
mismatchCostTable[i] = 0.5F;
}
}
{
decoder.initializeStates();
// Each sample of history[] carries its history.
// So we only have to process every iRate-th sample.
const unsigned step = decoder.iRate();
// input pointer
const uint32_t *ip = history + step - 1;
// output pointers
char *op = target.begin();
const char *const opt = target.end();
// table pointers
const float* match = matchCostTable;
const float* mismatch = mismatchCostTable;
size_t oCount = 0;
while (op<opt) {
// Viterbi algorithm
assert(match-matchCostTable<sizeof(matchCostTable)/sizeof(matchCostTable[0])-1);
assert(mismatch-mismatchCostTable<sizeof(mismatchCostTable)/sizeof(mismatchCostTable[0])-1);
const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch);
ip += step;
match += step;
mismatch += step;
// output
if (oCount>=deferral) *op++ = (minCost.iState >> deferral)&0x01;
oCount++;
}
}
}
// (pat) Added 6-22-2012
float SoftVector::getEnergy(float *plow) const
{
const SoftVector &vec = *this;
int len = vec.size();
float avg = 0; float low = 1;
for (int i = 0; i < len; i++) {
float bit = vec[i];
float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
float energy = fabsf(vec[i]);
if (energy < low) low = energy;
avg += energy/len;
}
@@ -545,8 +285,12 @@ float SoftVector::getEnergy(float *plow) const
ostream& operator<<(ostream& os, const SoftVector& sv)
{
for (size_t i=0; i<sv.size(); i++) {
if (sv[i]<0.25) os << "0";
else if (sv[i]>0.75) os << "1";
if (sv[i]<-0.5) os << "0";
else if (sv[i]<-0.25) os << "o";
else if (sv[i]<0.0) os << ".";
else if (sv[i]>0.5) os << "1";
else if (sv[i]>0.25) os << "|";
else if (sv[i]>0.0) os << "'";
else os << "-";
}
return os;

View File

@@ -30,201 +30,6 @@
#include <stdint.h>
class BitVector;
class SoftVector;
/** Shift-register (LFSR) generator. */
class Generator {
private:
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
uint64_t mState; ///< shift register state. LSB is most recent.
uint64_t mMask; ///< mask for reading state
unsigned mLen; ///< number of bits used in shift register
unsigned mLen_1; ///< mLen - 1
public:
Generator(uint64_t wCoeff, unsigned wLen)
:mCoeff(wCoeff),mState(0),
mMask((1ULL<<wLen)-1),
mLen(wLen),mLen_1(wLen-1)
{ assert(wLen<64); }
void clear() { mState=0; }
/**@name Accessors */
//@{
uint64_t state() const { return mState & mMask; }
unsigned size() const { return mLen; }
//@}
/**
Calculate one bit of a syndrome.
This is in the .h for inlining.
*/
void syndromeShift(unsigned inBit)
{
const unsigned fb = (mState>>(mLen_1)) & 0x01;
mState = (mState<<1) ^ (inBit & 0x01);
if (fb) mState ^= mCoeff;
}
/**
Update the generator state by one cycle.
This is in the .h for inlining.
*/
void encoderShift(unsigned inBit)
{
const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01;
mState <<= 1;
if (fb) mState ^= mCoeff;
}
};
/** Parity (CRC-type) generator and checker based on a Generator. */
class Parity : public Generator {
protected:
unsigned mCodewordSize;
public:
Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize)
:Generator(wCoefficients, wParitySize),
mCodewordSize(wCodewordSize)
{ }
/** Compute the parity word and write it into the target segment. */
void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true);
/** Compute the syndrome of a received sequence. */
uint64_t syndrome(const BitVector& receivedCodeword);
};
/**
Class to represent convolutional coders/decoders of rate 1/2, memory length 4.
This is the "workhorse" coder for most GSM channels.
*/
class ViterbiR2O4 {
private:
/**name Lots of precomputed elements so the compiler can optimize like hell. */
//@{
/**@name Core values. */
//@{
static const unsigned mIRate = 2; ///< reciprocal of rate
static const unsigned mOrder = 4; ///< memory length of generators
//@}
/**@name Derived values. */
//@{
static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors
static const uint32_t mSMask = mIStates-1; ///< survivor mask
static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask
static const uint32_t mOMask = (0x01<<mIRate)-1; ///< ouput mask, all iRate low bits set
static const unsigned mNumCands = mIStates*2; ///< number of candidates to generate during branching
static const unsigned mDeferral = 6*mOrder; ///< deferral to be used
//@}
//@}
/** Precomputed tables. */
//@{
uint32_t mCoeffs[mIRate]; ///< polynomial for each generator
uint32_t mStateTable[mIRate][2*mIStates]; ///< precomputed generator output tables
uint32_t mGeneratorTable[2*mIStates]; ///< precomputed coder output table
//@}
public:
/**
A candidate sequence in a Viterbi decoder.
The 32-bit state register can support a deferral of 6 with a 4th-order coder.
*/
typedef struct candStruct {
uint32_t iState; ///< encoder input associated with this candidate
uint32_t oState; ///< encoder output associated with this candidate
float cost; ///< cost (metric value), float to support soft inputs
} vCand;
/** Clear a structure. */
void clear(vCand& v)
{
v.iState=0;
v.oState=0;
v.cost=0;
}
private:
/**@name Survivors and candidates. */
//@{
vCand mSurvivors[mIStates]; ///< current survivor pool
vCand mCandidates[2*mIStates]; ///< current candidate pool
//@}
public:
unsigned iRate() const { return mIRate; }
uint32_t cMask() const { return mCMask; }
uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
unsigned deferral() const { return mDeferral; }
ViterbiR2O4();
/** Set all cost metrics to zero. */
void initializeStates();
/**
Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
@return reference to minimum-cost candidate.
*/
const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
private:
/** Branch survivors into new candidates. */
void branchCandidates();
/** Compute cost metrics for soft-inputs. */
void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
/** Select survivors from the candidate set. */
void pruneCandidates();
/** Find the minimum cost survivor. */
const vCand& minCost() const;
/**
Precompute the state tables.
@param g Generator index 0..((1/rate)-1)
*/
void computeStateTables(unsigned g);
/**
Precompute the generator outputs.
mCoeffs must be defined first.
*/
void computeGeneratorTable();
};
class BitVector : public Vector<char> {
@@ -282,16 +87,6 @@ class BitVector : public Vector<char> {
void zero() { fill(0); }
/**@name FEC operations. */
//@{
/** Calculate the syndrome of the vector with the given Generator. */
uint64_t syndrome(Generator& gen) const;
/** Calculate the parity word for the vector with the given Generator. */
uint64_t parity(Generator& gen) const;
/** Encode the signal with the GSM rate 1/2 convolutional encoder. */
void encode(const ViterbiR2O4& encoder, BitVector& target);
//@}
/** Invert 0<->1. */
void invert();
@@ -427,23 +222,20 @@ class SoftVector: public Vector<float> {
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
//@}
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
// (pat) How good is the SoftVector in the sense of the bits being solid?
// Result of 1 is perfect and 0 means all the bits were 0.5
// How good is the SoftVector in the sense of the bits being solid?
// Result of 1 is perfect and 0 means all the bits were 0.0
// If plow is non-NULL, also return the lowest energy bit.
float getEnergy(float *low=0) const;
/** Fill with "unknown" values. */
void unknown() { fill(0.5F); }
void unknown() { fill(0.0F); }
/** Return a hard bit value from a given index by slicing. */
bool bit(size_t index) const
{
const float *dp = mStart+index;
assert(dp<mEnd);
return (*dp)>0.5F;
return (*dp)>0.0F;
}
/** Slice the whole signal into bits. */

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BitVector.h"
#include <iostream>
#include <cstdlib>
using namespace std;
int main(int argc, char *argv[])
{
BitVector v1("0000111100111100101011110000");
cout << v1 << endl;
v1.LSB8MSB();
cout << v1 << endl;
ViterbiR2O4 vCoder;
BitVector v2(v1.size()*2);
v1.encode(vCoder,v2);
cout << v2 << endl;
SoftVector sv2(v2);
cout << sv2 << endl;
for (unsigned i=0; i<sv2.size()/4; i++) sv2[random()%sv2.size()]=0.5;
cout << sv2 << endl;
BitVector v3(v1.size());
sv2.decode(vCoder,v3);
cout << v3 << endl;
cout << v3.segment(3,4) << endl;
BitVector v4(v3.segment(0,4),v3.segment(8,4));
cout << v4 << endl;
BitVector v5("000011110000");
int r1 = v5.peekField(0,8);
int r2 = v5.peekField(4,4);
int r3 = v5.peekField(4,8);
cout << r1 << ' ' << r2 << ' ' << r3 << endl;
cout << v5 << endl;
v5.fillField(0,0xa,4);
int r4 = v5.peekField(0,8);
cout << v5 << endl;
cout << r4 << endl;
v5.reverse8();
cout << v5 << endl;
BitVector mC = "000000000000111100000000000001110000011100001101000011000000000000000111000011110000100100001010000010100000101000001010000010100000010000000000000000000000000000000000000000000000001100001111000000000000000000000000000000000000000000000000000010010000101000001010000010100000101000001010000001000000000000000000000000110000111100000000000001110000101000001100000001000000000000";
SoftVector mCS(mC);
BitVector mU(mC.size()/2);
mCS.decode(vCoder,mU);
cout << "c=" << mCS << endl;
cout << "u=" << mU << endl;
unsigned char ts[9] = "abcdefgh";
BitVector tp(70);
cout << "ts=" << ts << endl;
tp.unpack(ts);
cout << "tp=" << tp << endl;
tp.pack(ts);
cout << "ts=" << ts << endl;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,422 +0,0 @@
/*
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
* Copyright 2011, 2012 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIGURATION_H
#define CONFIGURATION_H
#include "sqlite3util.h"
#include <assert.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <regex.h>
#include <map>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <Threads.h>
#include <stdint.h>
/** A class for configuration file errors. */
class ConfigurationTableError {};
extern char gCmdName[]; // Gotta be global, gotta be char*, gotta love it.
/** An exception thrown when a given config key isn't found. */
class ConfigurationTableKeyNotFound : public ConfigurationTableError {
private:
std::string mKey;
public:
ConfigurationTableKeyNotFound(const std::string& wKey)
:mKey(wKey)
{ }
const std::string& key() const { return mKey; }
};
class ConfigurationRecord {
private:
std::string mValue;
long mNumber;
bool mDefined;
public:
ConfigurationRecord(bool wDefined=true):
mDefined(wDefined)
{ }
ConfigurationRecord(const std::string& wValue):
mValue(wValue),
mNumber(strtol(wValue.c_str(),NULL,0)),
mDefined(true)
{ }
ConfigurationRecord(const char* wValue):
mValue(std::string(wValue)),
mNumber(strtol(wValue,NULL,0)),
mDefined(true)
{ }
const std::string& value() const { return mValue; }
long number() const { return mNumber; }
bool defined() const { return mDefined; }
float floatNumber() const;
};
/** A string class that uses a hash function for comparison. */
class HashString : public std::string {
protected:
uint64_t mHash;
void computeHash();
public:
HashString(const char* src)
:std::string(src)
{
computeHash();
}
HashString(const std::string& src)
:std::string(src)
{
computeHash();
}
HashString()
{
mHash=0;
}
HashString& operator=(std::string& src)
{
std::string::operator=(src);
computeHash();
return *this;
}
HashString& operator=(const char* src)
{
std::string::operator=(src);
computeHash();
return *this;
}
bool operator==(const HashString& other)
{
return mHash==other.mHash;
}
bool operator<(const HashString& other)
{
return mHash<other.mHash;
}
bool operator>(const HashString& other)
{
return mHash<other.mHash;
}
uint64_t hash() const { return mHash; }
};
typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap;
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
class ConfigurationKey;
typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap;
/**
A class for maintaining a configuration key-value table,
based on sqlite3 and a local map-based cache.
Thread-safe, too.
*/
class ConfigurationTable {
private:
sqlite3* mDB; ///< database connection
ConfigurationMap mCache; ///< cache of recently access configuration values
mutable Mutex mLock; ///< control for multithreaded access to the cache
std::vector<std::string> (*mCrossCheck)(const std::string&); ///< cross check callback pointer
public:
ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic
ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap());
/** Generate an up-to-date example sql file for new installs. */
std::string getDefaultSQL(const std::string& program, const std::string& version);
/** Generate an up-to-date TeX snippet. */
std::string getTeX(const std::string& program, const std::string& version);
/** Return true if the key is used in the table. */
bool defines(const std::string& key);
/** Return true if the application's schema knows about this key. */
bool keyDefinedInSchema(const std::string& name);
/** Return true if the provided value validates correctly against the defined schema. */
bool isValidValue(const std::string& name, const std::string& val);
/** Return true if the provided value validates correctly against the defined schema. */
bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); }
/** Return a map of all similar keys in the defined schema. */
ConfigurationKeyMap getSimilarKeys(const std::string& snippet);
/** Return true if this key is identified as static. */
bool isStatic(const std::string& key);
/**
Get a string parameter from the table.
Throw ConfigurationTableKeyNotFound if not found.
*/
std::string getStr(const std::string& key);
/**
Get a boolean from the table.
Return false if NULL or 0, true otherwise.
*/
bool getBool(const std::string& key);
/**
Get a numeric parameter from the table.
Throw ConfigurationTableKeyNotFound if not found.
*/
long getNum(const std::string& key);
/**
Get a vector of strings from the table.
*/
std::vector<std::string> getVectorOfStrings(const std::string& key);
/**
Get a float from the table.
Throw ConfigurationTableKeyNotFound if not found.
*/
float getFloat(const std::string& key);
/**
Get a numeric vector from the table.
*/
std::vector<unsigned> getVector(const std::string& key);
/** Get length of a vector */
unsigned getVectorLength(const std::string &key)
{ return getVector(key).size(); }
/** Set or change a value in the table. */
bool set(const std::string& key, const std::string& value);
/** Set or change a value in the table. */
bool set(const std::string& key, long value);
/** Create an entry in the table, no value though. */
bool set(const std::string& key);
/**
Remove an entry from the table.
Will not alter required values.
@param key The key of the item to be removed.
@return true if anything was actually removed.
*/
bool remove(const std::string& key);
/** Search the table, dumping to a stream. */
void find(const std::string& pattern, std::ostream&) const;
/** Return all key/value pairs stored in the ConfigurationTable */
ConfigurationRecordMap getAllPairs() const;
/** Define the callback to purge the cache whenever the database changes. */
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
/** Define the callback for cross checking. */
void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&));
/** Execute the application specific value cross checking logic. */
std::vector<std::string> crossCheck(const std::string& key);
/** purege cache if it exceeds a certain age */
void checkCacheAge();
/** Delete all records from the cache. */
void purge();
private:
/**
Attempt to lookup a record, cache if needed.
Throw ConfigurationTableKeyNotFound if not found.
Caller should hold mLock because the returned reference points into the cache.
*/
const ConfigurationRecord& lookup(const std::string& key);
};
typedef std::map<HashString, std::string> HashStringMap;
class SimpleKeyValue {
protected:
HashStringMap mMap;
public:
/** Take a C string "A=B" and set map["A"]="B". */
void addItem(const char*);
/** Take a C string "A=B C=D E=F ..." and add all of the pairs to the map. */
void addItems(const char*s);
/** Return a reference to the string at map["key"]. */
const char* get(const char*) const;
};
class ConfigurationKey {
public:
enum VisibilityLevel
{
CUSTOMER,
CUSTOMERSITE,
CUSTOMERTUNE,
CUSTOMERWARN,
DEVELOPER,
FACTORY
};
enum Type
{
BOOLEAN,
CHOICE_OPT,
CHOICE,
CIDR_OPT,
CIDR,
FILEPATH_OPT,
FILEPATH,
IPADDRESS_OPT,
IPADDRESS,
IPANDPORT,
MIPADDRESS_OPT,
MIPADDRESS,
PORT_OPT,
PORT,
REGEX_OPT,
REGEX,
STRING_OPT,
STRING,
VALRANGE
};
private:
std::string mName;
std::string mDefaultValue;
std::string mUnits;
VisibilityLevel mVisibility;
Type mType;
std::string mValidValues;
bool mIsStatic;
std::string mDescription;
public:
ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription):
mName(wName),
mDefaultValue(wDefaultValue),
mUnits(wUnits),
mVisibility(wVisibility),
mType(wType),
mValidValues(wValidValues),
mIsStatic(wIsStatic),
mDescription(wDescription)
{ }
ConfigurationKey()
{ }
const std::string& getName() const { return mName; }
const std::string& getDefaultValue() const { return mDefaultValue; }
void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; }
void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); }
const std::string& getUnits() const { return mUnits; }
const VisibilityLevel& getVisibility() const { return mVisibility; }
const Type& getType() const { return mType; }
const std::string& getValidValues() const { return mValidValues; }
bool isStatic() const { return mIsStatic; }
const std::string& getDescription() const { return mDescription; }
static bool isValidIP(const std::string& ip);
static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping);
template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger);
static const std::string visibilityLevelToString(const VisibilityLevel& visibility);
static const std::string typeToString(const ConfigurationKey::Type& type);
static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os);
static void printDescription(const ConfigurationKey &key, std::ostream& os);
static const std::string getARFCNsString();
};
#endif
// vim: ts=4 sw=4

View File

@@ -1,149 +0,0 @@
/*
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Configuration.h"
#include <iostream>
#include <string>
using namespace std;
ConfigurationKeyMap getConfigurationKeys();
ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys());
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
{
//cout << "update hook" << endl;
gConfig.purge();
}
int main(int argc, char *argv[])
{
gConfig.setUpdateHook(purgeConfig);
char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
for (int i=0; i<5; i++) {
gConfig.set(keys[i],i);
}
for (int i=0; i<5; i++) {
cout << "table[" << keys[i] << "]=" << gConfig.getStr(keys[i]) << endl;
cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl;
}
for (int i=0; i<5; i++) {
cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl;
}
gConfig.set("key5","100 200 300 400 ");
std::vector<unsigned> vect = gConfig.getVector("key5");
cout << "vect length " << vect.size() << ": ";
for (unsigned i=0; i<vect.size(); i++) cout << " " << vect[i];
cout << endl;
std::vector<string> svect = gConfig.getVectorOfStrings("key5");
cout << "vect length " << svect.size() << ": ";
for (unsigned i=0; i<svect.size(); i++) cout << " " << svect[i] << ":";
cout << endl;
cout << "bool " << gConfig.getBool("booltest") << endl;
gConfig.set("booltest",1);
cout << "bool " << gConfig.getBool("booltest") << endl;
gConfig.set("booltest",0);
cout << "bool " << gConfig.getBool("booltest") << endl;
gConfig.getStr("newstring");
gConfig.getNum("numnumber");
SimpleKeyValue pairs;
pairs.addItems(" a=1 b=34 dd=143 ");
cout<< pairs.get("a") << endl;
cout<< pairs.get("b") << endl;
cout<< pairs.get("dd") << endl;
gConfig.set("fkey","123.456");
float fval = gConfig.getFloat("fkey");
cout << "fkey " << fval << endl;
cout << "search fkey:" << endl;
gConfig.find("fkey",cout);
cout << "search fkey:" << endl;
gConfig.find("fkey",cout);
gConfig.remove("fkey");
cout << "search fkey:" << endl;
gConfig.find("fkey",cout);
try {
gConfig.getNum("supposedtoabort");
} catch (ConfigurationTableKeyNotFound) {
cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl;
}
}
ConfigurationKeyMap getConfigurationKeys()
{
ConfigurationKeyMap map;
ConfigurationKey *tmp;
tmp = new ConfigurationKey("booltest","0",
"",
ConfigurationKey::DEVELOPER,
ConfigurationKey::BOOLEAN,
"",
false,
""
);
map[tmp->getName()] = *tmp;
free(tmp);
tmp = new ConfigurationKey("numnumber","42",
"",
ConfigurationKey::DEVELOPER,
ConfigurationKey::VALRANGE,
"0-100",
false,
""
);
map[tmp->getName()] = *tmp;
free(tmp);
tmp = new ConfigurationKey("newstring","new string value",
"",
ConfigurationKey::DEVELOPER,
ConfigurationKey::STRING,
"",
false,
""
);
map[tmp->getName()] = *tmp;
free(tmp);
return map;
}

View File

@@ -1,210 +0,0 @@
/*
* Copyright 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef F16_H
#define F16_H
#include <stdint.h>
#include <ostream>
/** Round a float to the appropriate F16 value. */
inline int32_t _f16_round(float f)
{
if (f>0.0F) return (int32_t)(f+0.5F);
if (f<0.0F) return (int32_t)(f-0.5F);
return 0;
}
/** A class for F15.16 fixed point arithmetic with saturation. */
class F16 {
private:
int32_t mV;
public:
F16() {}
F16(int i) { mV = i<<16; }
F16(float f) { mV = _f16_round(f*65536.0F); }
F16(double f) { mV = _f16_round((float)f*65536.0F); }
int32_t& raw() { return mV; }
const int32_t& raw() const { return mV; }
float f() const { return mV/65536.0F; }
//operator float() const { return mV/65536.0F; }
//operator int() const { return mV>>16; }
F16 operator=(float f)
{
mV = _f16_round(f*65536.0F);
return *this;
}
F16 operator=(int i)
{
mV = i<<16;
return *this;
}
F16 operator=(const F16& other)
{
mV = other.mV;
return mV;
}
F16 operator+(const F16& other) const
{
F16 retVal;
retVal.mV = mV + other.mV;
return retVal;
}
F16& operator+=(const F16& other)
{
mV += other.mV;
return *this;
}
F16 operator-(const F16& other) const
{
F16 retVal;
retVal.mV = mV - other.mV;
return retVal;
}
F16& operator-=(const F16& other)
{
mV -= other.mV;
return *this;
}
F16 operator*(const F16& other) const
{
F16 retVal;
int64_t p = (int64_t)mV * (int64_t)other.mV;
retVal.mV = p>>16;
return retVal;
}
F16& operator*=(const F16& other)
{
int64_t p = (int64_t)mV * (int64_t)other.mV;
mV = p>>16;
return *this;
}
F16 operator*(float f) const
{
F16 retVal;
retVal.mV = mV * f;
return retVal;
}
F16& operator*=(float f)
{
mV *= f;
return *this;
}
F16 operator/(const F16& other) const
{
F16 retVal;
int64_t pV = (int64_t)mV << 16;
retVal.mV = pV / other.mV;
return retVal;
}
F16& operator/=(const F16& other)
{
int64_t pV = (int64_t)mV << 16;
mV = pV / other.mV;
return *this;
}
F16 operator/(float f) const
{
F16 retVal;
retVal.mV = mV / f;
return retVal;
}
F16& operator/=(float f)
{
mV /= f;
return *this;
}
bool operator>(const F16& other) const
{
return mV>other.mV;
}
bool operator<(const F16& other) const
{
return mV<other.mV;
}
bool operator==(const F16& other) const
{
return mV==other.mV;
}
bool operator>(float f) const
{
return (mV/65536.0F) > f;
}
bool operator<(float f) const
{
return (mV/65536.0F) < f;
}
bool operator==(float f) const
{
return (mV/65536.0F) == f;
}
};
inline std::ostream& operator<<(std::ostream& os, const F16& v)
{
os << v.f();
return os;
}
#endif

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2009 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "F16.h"
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
F16 a = 2.5;
F16 b = 1.5;
F16 c = 2.5 * 1.5;
F16 d = c + a;
F16 e = 10;
cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << endl;
a *= 3;
b *= 0.3;
c *= e;
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
a /= 3;
b /= 0.3;
c = d * 0.05;
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
F16 f = a/d;
cout << f << ' ' << f+0.5 << endl;
}

View File

@@ -1,7 +1,5 @@
/*
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
* Copyright 2011, 2012 Range Networks, Inc.
* Copyright (C) 2018 sysmocom - s.f.m.c. GmbH
*
*
* This software is distributed under the terms of the GNU Affero Public License.
@@ -30,250 +28,34 @@
#include <fstream>
#include <string>
#include <stdarg.h>
#include <sys/time.h> // For gettimeofday
#include "Configuration.h"
#include "Logger.h"
#include "Threads.h" // pat added
using namespace std;
// Reference to a global config table, used all over the system.
extern ConfigurationTable gConfig;
/**@ The global alarms table. */
//@{
Mutex alarmsLock;
list<string> alarmsList;
void addAlarm(const string&);
//@}
// (pat) If Log messages are printed before the classes in this module are inited
// (which happens when static classes have constructors that do work)
// the OpenBTS just crashes.
// Prevent that by setting sLoggerInited to true when this module is inited.
static bool sLoggerInited = 0;
static struct CheckLoggerInitStatus {
CheckLoggerInitStatus() { sLoggerInited = 1; }
} sCheckloggerInitStatus;
/** Names of the logging levels. */
const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
int levelStringToInt(const string& name)
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
{
// Reverse search, since the numerically larger levels are more common.
for (int i=numLevels-1; i>=0; i--) {
if (name == levelNames[i]) return i;
}
// Common substitutions.
if (name=="INFORMATION") return 6;
if (name=="WARN") return 4;
if (name=="ERROR") return 3;
if (name=="CRITICAL") return 2;
if (name=="EMERGENCY") return 0;
// Unknown level.
return -1;
return os << ss.str();
}
/** Given a string, return the corresponding level name. */
int lookupLevel(const string& key)
{
string val = gConfig.getStr(key);
int level = levelStringToInt(val);
if (level == -1) {
string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
level = levelStringToInt(defaultLevel);
_LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
gConfig.set(key, defaultLevel);
}
return level;
}
int getLoggingLevel(const char* filename)
{
// Default level?
if (!filename) return lookupLevel("Log.Level");
// This can afford to be inefficient since it is not called that often.
const string keyName = string("Log.Level.") + string(filename);
if (gConfig.defines(keyName)) return lookupLevel(keyName);
return lookupLevel("Log.Level");
}
int gGetLoggingLevel(const char* filename)
{
// This is called a lot and needs to be efficient.
static Mutex sLogCacheLock;
static map<uint64_t,int> sLogCache;
static unsigned sCacheCount;
static const unsigned sCacheRefreshCount = 1000;
if (filename==NULL) return gGetLoggingLevel("");
HashString hs(filename);
uint64_t key = hs.hash();
sLogCacheLock.lock();
// Time for a cache flush?
if (sCacheCount>sCacheRefreshCount) {
sLogCache.clear();
sCacheCount=0;
}
// Is it cached already?
map<uint64_t,int>::const_iterator where = sLogCache.find(key);
sCacheCount++;
if (where!=sLogCache.end()) {
int retVal = where->second;
sLogCacheLock.unlock();
return retVal;
}
// Look it up in the config table and cache it.
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
// (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
sLogCacheLock.unlock();
int level = getLoggingLevel(filename);
sLogCacheLock.lock();
sLogCache.insert(pair<uint64_t,int>(key,level));
sLogCacheLock.unlock();
return level;
}
// copies the alarm list and returns it. list supposed to be small.
list<string> gGetLoggerAlarms()
{
alarmsLock.lock();
list<string> ret;
// excuse the "complexity", but to use std::copy with a list you need
// an insert_iterator - copy technically overwrites, doesn't insert.
insert_iterator< list<string> > ii(ret, ret.begin());
copy(alarmsList.begin(), alarmsList.end(), ii);
alarmsLock.unlock();
return ret;
}
/** Add an alarm to the alarm list. */
void addAlarm(const string& s)
{
alarmsLock.lock();
alarmsList.push_back(s);
unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
alarmsLock.unlock();
}
Log::~Log()
{
if (mDummyInit) return;
// Anything at or above LOG_CRIT is an "alarm".
// Save alarms in the local list and echo them to stderr.
if (mPriority <= LOG_CRIT) {
if (sLoggerInited) addAlarm(mStream.str().c_str());
cerr << mStream.str() << endl;
}
// Current logging level was already checked by the macro. So just log.
// Log to syslog
if (gLogToSyslog) {
syslog(mPriority, "%s", mStream.str().c_str());
}
// Log to file and console
if (gLogToConsole||gLogToFile) {
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
gLogToLock.lock();
if (gLogToConsole) {
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
std::cout << mStream.str();
if (neednl) std::cout<<"\n";
}
if (gLogToFile) {
fputs(mStream.str().c_str(),gLogToFile);
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
gLogToLock.unlock();
}
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
const char *fmt = neednl ? "%s\n" : "%s";
ScopedLock lock(gLogToLock);
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
}
Log::Log(const char* name, const char* level, int facility)
{
mDummyInit = true;
gLogInit(name, level, facility);
}
ostringstream& Log::get()
{
assert(mPriority<numLevels);
mStream << levelNames[mPriority] << ' ';
return mStream;
}
void gLogInit(const char* name, const char* level, int facility)
{
// Set the level if one has been specified.
if (level) {
gConfig.set("Log.Level",level);
}
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
string str = gConfig.getStr("Log.File");
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
const char *fn = str.c_str();
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
gLogToFile = fopen(fn,"w"); // New log file each time we start.
if (gLogToFile) {
time_t now;
time(&now);
fprintf(gLogToFile,"Starting at %s",ctime(&now));
fflush(gLogToFile);
std::cout << "Logging to file: " << fn << "\n";
}
}
}
// Open the log connection.
openlog(name,0,facility);
}
void gLogEarly(int level, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vsyslog(level | LOG_USER, fmt, args);
va_end(args);
}
// vim: ts=4 sw=4

View File

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

View File

@@ -22,11 +22,8 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
EXTRA_DIST = \
example.config \
README.common
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
noinst_LTLIBRARIES = libcommon.la
@@ -36,78 +33,20 @@ libcommon_la_SOURCES = \
Sockets.cpp \
Threads.cpp \
Timeval.cpp \
Reporting.cpp \
Logger.cpp \
Configuration.cpp \
sqlite3util.cpp \
URLEncode.cpp \
Utils.cpp
noinst_PROGRAMS = \
BitVectorTest \
InterthreadTest \
SocketsTest \
TimevalTest \
RegexpTest \
VectorTest \
ConfigurationTest \
LogTest \
URLEncodeTest \
F16Test
# ReportingTest
trx_vty.c \
debug.c
noinst_HEADERS = \
BitVector.h \
PRBS.h \
Interthread.h \
LinkedLists.h \
Sockets.h \
Threads.h \
Timeval.h \
Regexp.h \
Vector.h \
Configuration.h \
Reporting.h \
F16.h \
URLEncode.h \
Utils.h \
Logger.h \
sqlite3util.h
URLEncodeTest_SOURCES = URLEncodeTest.cpp
URLEncodeTest_LDADD = libcommon.la
BitVectorTest_SOURCES = BitVectorTest.cpp
BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
InterthreadTest_SOURCES = InterthreadTest.cpp
InterthreadTest_LDADD = libcommon.la
InterthreadTest_LDFLAGS = -lpthread
SocketsTest_SOURCES = SocketsTest.cpp
SocketsTest_LDADD = libcommon.la
SocketsTest_LDFLAGS = -lpthread
TimevalTest_SOURCES = TimevalTest.cpp
TimevalTest_LDADD = libcommon.la
VectorTest_SOURCES = VectorTest.cpp
VectorTest_LDADD = libcommon.la $(SQLITE_LA)
RegexpTest_SOURCES = RegexpTest.cpp
RegexpTest_LDADD = libcommon.la
ConfigurationTest_SOURCES = ConfigurationTest.cpp
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
# ReportingTest_SOURCES = ReportingTest.cpp
# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
LogTest_SOURCES = LogTest.cpp
LogTest_LDADD = libcommon.la $(SQLITE_LA)
F16Test_SOURCES = F16Test.cpp
MOSTLYCLEANFILES += testSource testDestination
trx_vty.h \
debug.h \
config_defs.h

View File

@@ -1,111 +0,0 @@
/*
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef _MEMORYLEAK_
#define _MEMORYLEAK_ 1
#include <map>
#include "ScalarTypes.h"
#include "Logger.h"
namespace Utils {
struct MemStats {
// Enumerates the classes that are checked.
// Redundancies are ok, for example, we check BitVector and also
// several descendants of BitVector.
enum MemoryNames {
mZeroIsUnused,
mVector,
mVectorData,
mBitVector,
mByteVector,
mByteVectorData,
mRLCRawBlock,
mRLCUplinkDataBlock,
mRLCMessage,
mRLCMsgPacketDownlinkDummyControlBlock, // Redundant with RLCMessage
mTBF,
mLlcEngine,
mSgsnDownlinkMsg,
mRachInfo,
mPdpPdu,
mFECDispatchInfo,
mL3Frame,
msignalVector,
mSoftVector,
mScramblingCode,
mURlcDownSdu,
mURlcPdu,
// Must be last:
mMax,
};
int mMemTotal[mMax]; // In elements, not bytes.
int mMemNow[mMax];
const char *mMemName[mMax];
MemStats();
void memChkNew(MemoryNames memIndex, const char *id);
void memChkDel(MemoryNames memIndex, const char *id);
void text(std::ostream &os);
// We would prefer to use an unordered_map, but that requires special compile switches.
// What a super great language.
typedef std::map<std::string,Int_z> MemMapType;
MemMapType mMemMap;
};
extern struct MemStats gMemStats;
extern int gMemLeakDebug;
// This is a memory leak detector.
// Use by putting RN_MEMCHKNEW and RN_MEMCHKDEL in class constructors/destructors,
// or use the DEFINE_MEMORY_LEAK_DETECTOR class and add the defined class
// as an ancestor to the class to be memory leak checked.
struct MemLabel {
std::string mccKey;
virtual ~MemLabel() {
Int_z &tmp = Utils::gMemStats.mMemMap[mccKey]; tmp = tmp - 1;
}
};
#if RN_DISABLE_MEMORY_LEAK_TEST
#define RN_MEMCHKNEW(type)
#define RN_MEMCHKDEL(type)
#define RN_MEMLOG(type,ptr)
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
struct checkerClass {};
#else
#define RN_MEMCHKNEW(type) { Utils::gMemStats.memChkNew(Utils::MemStats::m##type,#type); }
#define RN_MEMCHKDEL(type) { Utils::gMemStats.memChkDel(Utils::MemStats::m##type,#type); }
#define RN_MEMLOG(type,ptr) { \
static std::string key = format("%s_%s:%d",#type,__FILE__,__LINE__); \
(ptr)->/* MemCheck##type:: */ mccKey = key; \
Utils::gMemStats.mMemMap[key]++; \
}
// TODO: The above assumes that checkclass is MemCheck ## subClass
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
struct checkerClass : public virtual Utils::MemLabel { \
checkerClass() { RN_MEMCHKNEW(subClass); } \
virtual ~checkerClass() { \
RN_MEMCHKDEL(subClass); \
} \
};
#endif
} // namespace Utils
#endif

110
CommonLibs/PRBS.h Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PRBS_H
#define PRBS_H
#include <stdint.h>
#include <assert.h>
/** Pseudo-random binary sequence (PRBS) generator (a Galois LFSR implementation). */
class PRBS {
public:
PRBS(unsigned wLen, uint64_t wCoeff, uint64_t wState = 0x01)
: mCoeff(wCoeff), mStartState(wState), mState(wState), mLen(wLen)
{ assert(wLen<=64); }
/**@name Accessors */
//@{
uint64_t coeff() const { return mCoeff; }
uint64_t state() const { return mState; }
void state(uint64_t state) { mState = state & mask(); }
unsigned size() const { return mLen; }
//@}
/**
Calculate one bit of a PRBS
*/
unsigned generateBit()
{
const unsigned result = mState & 0x01;
processBit(result);
return result;
}
/**
Update the generator state by one bit.
If you want to synchronize your PRBS to a known state, call this function
size() times passing your PRBS to it bit by bit.
*/
void processBit(unsigned inBit)
{
mState >>= 1;
if (inBit) mState ^= mCoeff;
}
/** Return true when PRBS is wrapping through initial state */
bool isFinished() const { return mStartState == mState; }
protected:
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
uint64_t mStartState; ///< initial shift register state.
uint64_t mState; ///< shift register state.
unsigned mLen; ///< number of bits used in shift register
/** Return mask for the state register */
uint64_t mask() const { return (mLen==64)?0xFFFFFFFFFFFFFFFFUL:((1<<mLen)-1); }
};
/**
A standard 9-bit based pseudorandom binary sequence (PRBS) generator.
Polynomial: x^9 + x^5 + 1
*/
class PRBS9 : public PRBS {
public:
PRBS9(uint64_t wState = 0x01)
: PRBS(9, 0x0110, wState)
{}
};
/**
A standard 15-bit based pseudorandom binary sequence (PRBS) generator.
Polynomial: x^15 + x^14 + 1
*/
class PRBS15 : public PRBS {
public:
PRBS15(uint64_t wState = 0x01)
: PRBS(15, 0x6000, wState)
{}
};
/**
A standard 64-bit based pseudorandom binary sequence (PRBS) generator.
Polynomial: x^64 + x^63 + x^61 + x^60 + 1
*/
class PRBS64 : public PRBS {
public:
PRBS64(uint64_t wState = 0x01)
: PRBS(64, 0xD800000000000000ULL, wState)
{}
};
#endif // PRBS_H

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REGEXPW_H
#define REGEXPW_H
#include <regex.h>
#include <iostream>
#include <stdlib.h>
class Regexp {
private:
regex_t mRegex;
public:
Regexp(const char* regexp, int flags=REG_EXTENDED)
{
int result = regcomp(&mRegex, regexp, flags);
if (result) {
char msg[256];
regerror(result,&mRegex,msg,255);
std::cerr << "Regexp compilation of " << regexp << " failed: " << msg << std::endl;
abort();
}
}
~Regexp()
{ regfree(&mRegex); }
bool match(const char *text, int flags=0) const
{ return regexec(&mRegex, text, 0, NULL, flags)==0; }
};
#endif

View File

@@ -1,145 +0,0 @@
/**@file Module for performance-reporting mechanisms. */
/*
* Copyright 2012 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Reporting.h"
#include "Logger.h"
#include <stdio.h>
#include <string.h>
static const char* createReportingTable = {
"CREATE TABLE IF NOT EXISTS REPORTING ("
"NAME TEXT UNIQUE NOT NULL, "
"VALUE INTEGER DEFAULT 0, "
"CLEAREDTIME INTEGER NOT NULL, "
"UPDATETIME INTEGER DEFAULT 0 "
")"
};
ReportingTable::ReportingTable(const char* filename)
{
gLogEarly(LOG_INFO | mFacility, "opening reporting table from path %s", filename);
// Connect to the database.
int rc = sqlite3_open(filename,&mDB);
if (rc) {
gLogEarly(LOG_EMERG | mFacility, "cannot open reporting database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
sqlite3_close(mDB);
mDB = NULL;
return;
}
// Create the table, if needed.
if (!sqlite3_command(mDB,createReportingTable)) {
gLogEarly(LOG_EMERG | mFacility, "cannot create reporting table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
}
}
bool ReportingTable::create(const char* paramName)
{
char cmd[200];
sprintf(cmd,"INSERT OR IGNORE INTO REPORTING (NAME,CLEAREDTIME) VALUES (\"%s\",%ld)", paramName, time(NULL));
if (!sqlite3_command(mDB,cmd)) {
gLogEarly(LOG_CRIT|mFacility, "cannot create reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
return false;
}
return true;
}
bool ReportingTable::incr(const char* paramName)
{
char cmd[200];
sprintf(cmd,"UPDATE REPORTING SET VALUE=VALUE+1, UPDATETIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
if (!sqlite3_command(mDB,cmd)) {
gLogEarly(LOG_CRIT|mFacility, "cannot increment reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
return false;
}
return true;
}
bool ReportingTable::max(const char* paramName, unsigned newVal)
{
char cmd[200];
sprintf(cmd,"UPDATE REPORTING SET VALUE=MAX(VALUE,%u), UPDATETIME=%ld WHERE NAME=\"%s\"", newVal, time(NULL), paramName);
if (!sqlite3_command(mDB,cmd)) {
gLogEarly(LOG_CRIT|mFacility, "cannot maximize reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
return false;
}
return true;
}
bool ReportingTable::clear(const char* paramName)
{
char cmd[200];
sprintf(cmd,"UPDATE REPORTING SET VALUE=0, UPDATETIME=0, CLEAREDTIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
if (!sqlite3_command(mDB,cmd)) {
gLogEarly(LOG_CRIT|mFacility, "cannot clear reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
return false;
}
return true;
}
bool ReportingTable::create(const char* baseName, unsigned minIndex, unsigned maxIndex)
{
size_t sz = strlen(baseName);
for (unsigned i = minIndex; i<=maxIndex; i++) {
char name[sz+10];
sprintf(name,"%s.%u",baseName,i);
if (!create(name)) return false;
}
return true;
}
bool ReportingTable::incr(const char* baseName, unsigned index)
{
char name[strlen(baseName)+10];
sprintf(name,"%s.%u",baseName,index);
return incr(name);
}
bool ReportingTable::max(const char* baseName, unsigned index, unsigned newVal)
{
char name[strlen(baseName)+10];
sprintf(name,"%s.%u",baseName,index);
return max(name,newVal);
}
bool ReportingTable::clear(const char* baseName, unsigned index)
{
char name[strlen(baseName)+10];
sprintf(name,"%s.%u",baseName,index);
return clear(name);
}

View File

@@ -1,86 +0,0 @@
/**@file Module for performance-reporting mechanisms. */
/*
* Copyright 2012 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REPORTING_H
#define REPORTING_H
#include <sqlite3util.h>
#include <ostream>
/**
Collect performance statistics into a database.
Parameters are counters or max/min trackers, all integer.
*/
class ReportingTable {
private:
sqlite3* mDB; ///< database connection
int mFacility; ///< rsyslogd facility
public:
/**
Open the database connection;
create the table if it does not exist yet.
*/
ReportingTable(const char* filename);
/** Create a new parameter. */
bool create(const char* paramName);
/** Create an indexed parameter set. */
bool create(const char* baseBame, unsigned minIndex, unsigned maxIndex);
/** Increment a counter. */
bool incr(const char* paramName);
/** Increment an indexed counter. */
bool incr(const char* baseName, unsigned index);
/** Take a max of a parameter. */
bool max(const char* paramName, unsigned newVal);
/** Take a max of an indexed parameter. */
bool max(const char* paramName, unsigned index, unsigned newVal);
/** Clear a value. */
bool clear(const char* paramName);
/** Clear an indexed value. */
bool clear(const char* paramName, unsigned index);
/** Dump the database to a stream. */
void dump(std::ostream&) const;
};
#endif
// vim: ts=4 sw=4

View File

@@ -1,136 +0,0 @@
/*
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef SCALARTYPES_H
#define SCALARTYPES_H
#include <iostream> // For size_t
#include <stdint.h>
//#include "GSMCommon.h" // Was included for Z100Timer
// We dont bother to define *= /= etc.; you'll have to convert: a*=b; to: a=a*b;
#define _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
Classname() : value(Init) {} \
Classname(Basetype wvalue) { value = wvalue; } /* Can set from basetype. */ \
operator Basetype(void) const { return value; } /* Converts from basetype. */ \
Basetype operator=(Basetype wvalue) { return value = wvalue; } \
Basetype* operator&() { return &value; }
#define _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) \
Basetype operator++() { return ++value; } \
Basetype operator++(int) { return value++; } \
Basetype operator--() { return --value; } \
Basetype operator--(int) { return value--; } \
Basetype operator+=(Basetype wvalue) { return value = value + wvalue; } \
Basetype operator-=(Basetype wvalue) { return value = value - wvalue; }
#define _INITIALIZED_SCALAR_FUNCS(Classname,Basetype,Init) \
_INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
_INITIALIZED_SCALAR_ARITH_FUNCS(Basetype)
#define _DECLARE_SCALAR_TYPE(Classname_i,Classname_z,Basetype) \
template <Basetype Init> \
struct Classname_i { \
Basetype value; \
_INITIALIZED_SCALAR_FUNCS(Classname_i,Basetype,Init) \
}; \
typedef Classname_i<0> Classname_z;
// Usage:
// Where 'classname' is one of the types listed below, then:
// classname_z specifies a zero initialized type;
// classname_i<value> initializes the type to the specified value.
// We also define Float_z.
_DECLARE_SCALAR_TYPE(Int_i, Int_z, int)
_DECLARE_SCALAR_TYPE(Char_i, Char_z, signed char)
_DECLARE_SCALAR_TYPE(Int16_i, Int16_z, int16_t)
_DECLARE_SCALAR_TYPE(Int32_i, Int32_z, int32_t)
_DECLARE_SCALAR_TYPE(UInt_i, UInt_z, unsigned)
_DECLARE_SCALAR_TYPE(UChar_i, UChar_z, unsigned char)
_DECLARE_SCALAR_TYPE(UInt16_i, UInt16_z, uint16_t)
_DECLARE_SCALAR_TYPE(UInt32_i, UInt32_z, uint32_t)
_DECLARE_SCALAR_TYPE(Size_t_i, Size_t_z, size_t)
// Bool is special because it cannot accept some arithmetic funcs
//_DECLARE_SCALAR_TYPE(Bool_i, Bool_z, bool)
template <bool Init>
struct Bool_i {
bool value;
_INITIALIZED_SCALAR_BASE_FUNCS(Bool_i,bool,Init)
};
typedef Bool_i<0> Bool_z;
// float is special, because C++ does not permit the template initalization:
struct Float_z {
float value;
_INITIALIZED_SCALAR_FUNCS(Float_z,float,0)
};
struct Double_z {
double value;
_INITIALIZED_SCALAR_FUNCS(Double_z,double,0)
};
class ItemWithValueAndWidth {
public:
virtual unsigned getValue() const = 0;
virtual unsigned getWidth() const = 0;
};
// A Range Networks Field with a specified width.
// See RLCMessages.h for examples.
template <int Width=32, unsigned Init=0>
class Field_i : public ItemWithValueAndWidth
{
public:
unsigned value;
_INITIALIZED_SCALAR_FUNCS(Field_i,unsigned,Init)
unsigned getWidth() const { return Width; }
unsigned getValue() const { return value; }
};
// Synonym for Field_i, but no way to do it.
template <int Width, unsigned Init=0>
class Field_z : public ItemWithValueAndWidth
{
public:
unsigned value;
_INITIALIZED_SCALAR_FUNCS(Field_z,unsigned,Init)
unsigned getWidth() const { return Width; }
unsigned getValue() const { return value; }
};
// This is an uninitialized field.
template <int Width=32, unsigned Init=0>
class Field : public ItemWithValueAndWidth
{
public:
unsigned value;
_INITIALIZED_SCALAR_FUNCS(Field,unsigned,Init)
unsigned getWidth() const { return Width; }
unsigned getValue() const { return value; }
};
// A Z100Timer with an initial value specified.
//template <int Init>
//class Z100Timer_i : public GSM::Z100Timer {
// public:
// Z100Timer_i() : GSM::Z100Timer(Init) {}
//};
#endif

View File

@@ -187,24 +187,20 @@ int DatagramSocket::send(const struct sockaddr* dest, const char * message)
return send(dest,message,length);
}
int DatagramSocket::read(char* buffer)
int DatagramSocket::read(char* buffer, size_t length)
{
socklen_t temp_len = sizeof(mSource);
int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0,
(struct sockaddr*)&mSource,&temp_len);
if ((length==-1) && (errno!=EAGAIN)) {
socklen_t addr_len = sizeof(mSource);
int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
(struct sockaddr*) &mSource, &addr_len);
if ((rd_length==-1) && (errno!=EAGAIN)) {
perror("DatagramSocket::read() failed");
throw SocketError();
}
return length;
return rd_length;
}
int DatagramSocket::read(char* buffer, unsigned timeout)
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
{
fd_set fds;
FD_ZERO(&fds);
@@ -218,7 +214,7 @@ int DatagramSocket::read(char* buffer, unsigned timeout)
throw SocketError();
}
if (sel==0) return -1;
if (FD_ISSET(mSocketFD,&fds)) return read(buffer);
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
return -1;
}
@@ -227,18 +223,18 @@ int DatagramSocket::read(char* buffer, unsigned timeout)
UDPSocket::UDPSocket(unsigned short wSrcPort)
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
:DatagramSocket()
{
open(wSrcPort);
open(wSrcPort, wSrcIP);
}
UDPSocket::UDPSocket(unsigned short wSrcPort,
const char * wDestIP, unsigned short wDestPort )
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
const char *wDestIP, unsigned short wDestPort)
:DatagramSocket()
{
open(wSrcPort);
open(wSrcPort, wSrcIP);
destination(wDestPort, wDestIP);
}
@@ -250,7 +246,7 @@ void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
}
void UDPSocket::open(unsigned short localPort)
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
{
// create
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
@@ -269,7 +265,7 @@ void UDPSocket::open(unsigned short localPort)
size_t length = sizeof(address);
bzero(&address,length);
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_addr.s_addr = inet_addr(wlocalIP);
address.sin_port = htons(localPort);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");
@@ -288,50 +284,4 @@ unsigned short UDPSocket::port() const
return ntohs(name.sin_port);
}
UDDSocket::UDDSocket(const char* localPath, const char* remotePath)
:DatagramSocket()
{
if (localPath!=NULL) open(localPath);
if (remotePath!=NULL) destination(remotePath);
}
void UDDSocket::open(const char* localPath)
{
// create
mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0);
if (mSocketFD<0) {
perror("socket() failed");
throw SocketError();
}
// bind
struct sockaddr_un address;
size_t length = sizeof(address);
bzero(&address,length);
address.sun_family = AF_UNIX;
strcpy(address.sun_path,localPath);
unlink(localPath);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");
throw SocketError();
}
}
void UDDSocket::destination(const char* remotePath)
{
struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination;
strcpy(unAddr->sun_path,remotePath);
}
// vim:ts=4:sw=4

View File

@@ -108,7 +108,7 @@ public:
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
@return The number of bytes received or -1 on non-blocking pass.
*/
int read(char* buffer);
int read(char* buffer, size_t length);
/**
Receive a packet with a timeout.
@@ -116,7 +116,7 @@ public:
@param maximum wait time in milliseconds
@return The number of bytes received or -1 on timeout.
*/
int read(char* buffer, unsigned timeout);
int read(char* buffer, size_t length, unsigned timeout);
/** Send a packet to a given destination, other than the default. */
@@ -144,11 +144,11 @@ class UDPSocket : public DatagramSocket {
public:
/** Open a USP socket with an OS-assigned port and no default destination. */
UDPSocket( unsigned short localPort=0);
UDPSocket(const char *localIP, unsigned short localPort);
/** Given a full specification, open the socket and set the dest address. */
UDPSocket( unsigned short localPort,
const char * remoteIP, unsigned short remotePort);
UDPSocket(const char *localIP, unsigned short localPort,
const char *remoteIP, unsigned short remotePort);
/** Set the destination port. */
void destination( unsigned short wDestPort, const char * wDestIP );
@@ -157,7 +157,7 @@ public:
unsigned short port() const;
/** Open and bind the UDP socket to a local port. */
void open(unsigned short localPort=0);
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
/** Give the return address of the most recently received packet. */
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
@@ -166,26 +166,6 @@ public:
};
/** Unix Domain Datagram Socket */
class UDDSocket : public DatagramSocket {
public:
UDDSocket(const char* localPath=NULL, const char* remotePath=NULL);
void destination(const char* remotePath);
void open(const char* localPath);
/** Give the return address of the most recently received packet. */
const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; }
size_t addressSize() const { return sizeof(struct sockaddr_un); }
};
#endif

View File

@@ -1,28 +0,0 @@
/* Copyright 2011, Range Networks, Inc. */
#include <URLEncode.h>
#include <string>
#include <string.h>
#include <ctype.h>
using namespace std;
//based on javascript encodeURIComponent()
string URLEncode(const string &c)
{
static const char *digits = "01234567890ABCDEF";
string retVal="";
for (size_t i=0; i<c.length(); i++)
{
const char ch = c[i];
if (isalnum(ch) || strchr("-_.!~'()",ch)) {
retVal += ch;
} else {
retVal += '%';
retVal += digits[(ch>>4) & 0x0f];
retVal += digits[ch & 0x0f];
}
}
return retVal;
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright 2011 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
# include <string>
std::string URLEncode(const std::string&);

View File

@@ -1,17 +0,0 @@
#include "URLEncode.h"
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
string test = string("Testing: !@#$%^&*() " __DATE__ " " __TIME__);
cout << test << endl;
cout << URLEncode(test) << endl;
}

View File

@@ -1,211 +0,0 @@
/*
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <unistd.h> // For usleep
#include <sys/time.h> // For gettimeofday
#include <stdio.h> // For vsnprintf
#include <ostream> // For ostream
#include <sstream> // For ostringstream
#include <string.h> // For strcpy
//#include "GSMCommon.h"
#include "Utils.h"
#include "MemoryLeak.h"
namespace Utils {
MemStats gMemStats;
int gMemLeakDebug = 0;
MemStats::MemStats()
{
memset(mMemNow,0,sizeof(mMemNow));
memset(mMemTotal,0,sizeof(mMemTotal));
memset(mMemName,0,sizeof(mMemName));
}
void MemStats::text(std::ostream &os)
{
os << "Structs current total:\n";
for (int i = 0; i < mMax; i++) {
os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n";
}
}
void MemStats::memChkNew(MemoryNames memIndex, const char *id)
{
/*std::cout << "new " #type "\n";*/
mMemNow[memIndex]++;
mMemTotal[memIndex]++;
mMemName[memIndex] = id;
}
void MemStats::memChkDel(MemoryNames memIndex, const char *id)
{
/*std::cout << "del " #type "\n";*/
mMemNow[memIndex]--;
if (mMemNow[memIndex] < 0) {
LOG(ERR) << "Memory underflow on type "<<id;
if (gMemLeakDebug) assert(0);
mMemNow[memIndex] += 100; // Prevent another message for a while.
}
}
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
{
return os << ss.str();
}
std::ostream &osprintf(std::ostream &os, const char *fmt, ...)
{
va_list ap;
char buf[300];
va_start(ap,fmt);
int n = vsnprintf(buf,300,fmt,ap);
va_end(ap);
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
os << buf;
return os;
}
std::string format(const char *fmt, ...)
{
va_list ap;
char buf[300];
va_start(ap,fmt);
int n = vsnprintf(buf,300,fmt,ap);
va_end(ap);
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
return std::string(buf);
}
// Return time in seconds with high resolution.
// Note: In the past I found this to be a surprisingly expensive system call in linux.
double timef()
{
struct timeval tv;
gettimeofday(&tv,NULL);
return tv.tv_usec / 1000000.0 + tv.tv_sec;
}
const std::string timestr()
{
struct timeval tv;
struct tm tm;
gettimeofday(&tv,NULL);
localtime_r(&tv.tv_sec,&tm);
unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
}
// High resolution sleep for the specified time.
// Return FALSE if time is already past.
void sleepf(double howlong)
{
if (howlong <= 0.00001) return; // Less than 10 usecs, forget it.
usleep((useconds_t) (1000000.0 * howlong));
}
//bool sleepuntil(double untilwhen)
//{
//double now = timef();
//double howlong = untilwhen - now; // Fractional time in seconds.
// We are not worrying about overflow because all times should be in the near future.
//if (howlong <= 0.00001) return false; // Less than 10 usecs, forget it.
//sleepf(sleeptime);
//}
std::string Text2Str::str() const
{
std::ostringstream ss;
text(ss);
return ss.str();
}
std::ostream& operator<<(std::ostream& os, const Text2Str *val)
{
std::ostringstream ss;
if (val) {
val->text(ss);
os << ss.str();
} else {
os << "(null)";
}
return os;
}
// Greatest Common Denominator.
// This is by Doug Brown.
int gcd(int x, int y)
{
if (x > y) {
return x % y == 0 ? y : gcd(y, x % y);
} else {
return y % x == 0 ? x : gcd(x, y % x);
}
}
// Split a C string into an argc,argv array in place; the input string is modified.
// Returns argc, and places results in argv, up to maxargc elements.
// The final argv receives the rest of the input string from maxargc on,
// even if it contains additional splitchars.
// The correct idiom for use is to make a copy of your string, like this:
// char *copy = strcpy((char*)alloca(the_string.length()+1),the_string.c_str());
// char *argv[2];
// int argc = cstrSplit(copy,argv,2,NULL);
// If you want to detect the error of too many arguments, add 1 to argv, like this:
// char *argv[3];
// int argc = cstrSplit(copy,argv,3,NULL);
// if (argc == 3) { error("too many arguments"; }
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars)
{
if (splitchars == NULL) { splitchars = " \t\r\n"; } // Default is any space.
int argc = 0;
while (argc < maxargc) {
while (*in && strchr(splitchars,*in)) {in++;} // scan past any splitchars
if (! *in) return argc; // return if finished.
pargv[argc++] = in; // save ptr to start of arg.
in = strpbrk(in,splitchars); // go to end of arg.
if (!in) return argc; // return if finished.
*in++ = 0; // zero terminate this arg.
}
return argc;
}
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat) { stat.text(os); return os; }
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat) { stat.text(os); return os; }
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat) { stat.text(os); return os; }
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat) { stat.text(os); return os; }
std::string replaceAll(const std::string input, const std::string search, const std::string replace)
{
std::string output = input;
int index = 0;
while (true) {
index = output.find(search, index);
if (index == std::string::npos) {
break;
}
output.replace(index, replace.length(), replace);
index += replace.length();
}
return output;
}
};

View File

@@ -1,148 +0,0 @@
/*
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef GPRSUTILS_H
#define GPRSUTILS_H
#include <stdint.h>
#include <stdarg.h>
#include <string>
#include <string.h>
#include <math.h> // for sqrtf
#include "Logger.h"
namespace Utils {
extern double timef(); // high resolution time
extern const std::string timestr(); // A timestamp to print in messages.
extern void sleepf(double howlong); // high resolution sleep
extern int gcd(int x, int y);
// It is irritating to create a string just to interface to the brain-damaged
// C++ stream class, but this is only used for debug messages.
std::string format(const char *fmt, ...) __attribute__((format (printf,1,2)));
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars=NULL);
// For classes with a text() function, provide a function to return a String,
// and also a standard << stream function that takes a pointer to the object.
// We dont provide the function that takes a reference to the object
// because it is too highly overloaded and generally doesnt work.
class Text2Str {
public:
virtual void text(std::ostream &os) const = 0;
std::string str() const;
};
std::ostream& operator<<(std::ostream& os, const Text2Str *val);
#if 0
// Generic Activity Timer. Lots of controls to make everybody happy.
class ATimer {
double mStart;
//bool mActive;
double mLimitTime;
public:
ATimer() : mStart(0), mLimitTime(0) { }
ATimer(double wLimitTime) : mStart(0), mLimitTime(wLimitTime) { }
void start() { mStart=timef(); }
void stop() { mStart=0; }
bool active() { return !!mStart; }
double elapsed() { return timef() - mStart; }
bool expired() { return elapsed() > mLimitTime; }
};
#endif
struct BitSet {
unsigned mBits;
void setBit(unsigned whichbit) { mBits |= 1<<whichbit; }
void clearBit(unsigned whichbit) { mBits &= ~(1<<whichbit); }
unsigned getBit(unsigned whichbit) const { return mBits & (1<<whichbit); }
bool isSet(unsigned whichbit) const { return mBits & (1<<whichbit); }
unsigned bits() const { return mBits; }
operator int(void) const { return mBits; }
BitSet() { mBits = 0; }
};
// Store current, min, max and compute running average and standard deviation.
template<class Type> struct Statistic {
Type mCurrent, mMin, mMax; // min,max optional initialization so you can print before adding any values.
unsigned mCnt;
double mSum;
//double mSum2; // sum of squares.
// (Type) cast needed in case Type is an enum, stupid language.
Statistic() : mCurrent((Type)0), mMin((Type)0), mMax((Type)0), mCnt(0), mSum(0) /*,mSum2(0)*/ {}
// Set the current value and add a statisical point.
void addPoint(Type val) {
mCurrent = val;
if (mCnt == 0 || val < mMin) {mMin = val;}
if (mCnt == 0 || val > mMax) {mMax = val;}
mCnt++;
mSum += val;
//mSum2 += val * val;
}
Type getCurrent() const { // Return current value.
return mCnt ? mCurrent : 0;
}
double getAvg() const { // Return average.
return mCnt==0 ? 0 : mSum/mCnt;
};
//float getSD() const { // Return standard deviation. Use low precision square root function.
// return mCnt==0 ? 0 : sqrtf(mCnt * mSum2 - mSum*mSum) / mCnt;
//}
void text(std::ostream &os) const { // Print everything in parens.
os << "("<<mCurrent;
if (mMin != mMax) { // Not point in printing all this stuff if min == max.
os <<LOGVAR2("min",mMin)<<LOGVAR2("max",mMax)<<LOGVAR2("avg",getAvg());
if (mCnt <= 999999) {
os <<LOGVAR2("N",mCnt);
} else { // Shorten this up:
char buf[10], *ep;
sprintf(buf,"%.3g",round(mCnt));
if ((ep = strchr(buf,'e')) && ep[1] == '+') { strcpy(ep+1,ep+2); }
os << LOGVAR2("N",buf);
}
// os<<LOGVAR2("sd",getSD()) standard deviation not interesting
}
os << ")";
// " min="<<mMin <<" max="<<mMax <<format(" avg=%4g sd=%3g)",getAvg(),getSD());
}
// Not sure if this works:
//std::string statStr() const {
// return (std::string)mCurrent + " min=" + (std::string) mMin +" max="+(string)mMax+ format(" avg=%4g sd=%3g",getAvg(),getSD());
//}
};
// This I/O mechanism is so dumb:
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat);
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat);
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat);
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat);
// Yes, they botched and left this out:
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
std::ostream &osprintf(std::ostream &os, const char *fmt, ...) __attribute__((format (printf,2,3)));
std::string replaceAll(const std::string input, const std::string search, const std::string replace);
}; // namespace
using namespace Utils;
#endif

View File

@@ -92,6 +92,13 @@ template <class T> class Vector {
mEnd = mStart + newSize;
}
/** Reduce addressable size of the Vector, keeping content. */
void shrink(size_t newSize)
{
assert(newSize <= mEnd - mStart);
mEnd = mStart + newSize;
}
/** Release memory and clear pointers. */
void clear() { resize(0); }
@@ -111,8 +118,8 @@ template <class T> class Vector {
/** Build an empty Vector of a given size. */
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
/** Build a Vector by shifting the data block. */
Vector(Vector<T>& other)
/** Build a Vector by moving another. */
Vector(Vector<T>&& other)
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
{ other.mData=NULL; }
@@ -222,6 +229,21 @@ template <class T> class Vector {
memcpy(other.mStart,base,span*sizeof(T));
}
/**
Move (copy) a segment of this vector into a different position in the vector
@param from Start point from which to copy.
@param to Start point to which to copy.
@param span The number of elements to copy.
*/
void segmentMove(size_t from, size_t to, size_t span)
{
const T* baseFrom = mStart + from;
T* baseTo = mStart + to;
assert(baseFrom+span<=mEnd);
assert(baseTo+span<=mEnd);
memmove(baseTo,baseFrom,span*sizeof(T));
}
void fill(const T& val)
{
T* dp=mStart;

20
CommonLibs/config_defs.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
/*
* This file contains structures used by both VTY (C, dir CommonLibs) and
* osmo-trx (CXX, dir Transceiver52)
*/
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
FILLER_NORM_RAND,
FILLER_EDGE_RAND,
FILLER_ACCESS_RAND,
};
enum ReferenceType {
REF_INTERNAL,
REF_EXTERNAL,
REF_GPS,
};

18
CommonLibs/debug.c Normal file
View File

@@ -0,0 +1,18 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include "debug.h"
/* default categories */
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
.description = "Main generic category",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};

8
CommonLibs/debug.h Normal file
View File

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

View File

@@ -1,154 +0,0 @@
/*
* Copyright 2010 Kestrel Signal Processing, Inc.
* All rights reserved.
*/
#include "sqlite3.h"
#include "sqlite3util.h"
#include <string.h>
#include <unistd.h>
#include <stdio.h>
// Wrappers to sqlite operations.
// These will eventually get moved to commonlibs.
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query)
{
int src = SQLITE_BUSY;
while (src==SQLITE_BUSY) {
src = sqlite3_prepare_v2(DB,query,strlen(query),stmt,NULL);
if (src==SQLITE_BUSY) {
usleep(100000);
}
}
if (src) {
fprintf(stderr,"sqlite3_prepare_v2 failed for \"%s\": %s\n",query,sqlite3_errmsg(DB));
sqlite3_finalize(*stmt);
}
return src;
}
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt)
{
int src = SQLITE_BUSY;
while (src==SQLITE_BUSY) {
src = sqlite3_step(stmt);
if (src==SQLITE_BUSY) {
usleep(100000);
}
}
if ((src!=SQLITE_DONE) && (src!=SQLITE_ROW)) {
fprintf(stderr,"sqlite3_run_query failed: %s: %s\n", sqlite3_sql(stmt), sqlite3_errmsg(DB));
}
return src;
}
bool sqlite3_exists(sqlite3* DB, const char *tableName,
const char* keyName, const char* keyData)
{
size_t stringSize = 100 + strlen(tableName) + strlen(keyName) + strlen(keyData);
char query[stringSize];
sprintf(query,"SELECT * FROM %s WHERE %s == \"%s\"",tableName,keyName,keyData);
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Read the result.
int src = sqlite3_run_query(DB,stmt);
sqlite3_finalize(stmt);
// Anything there?
return (src == SQLITE_ROW);
}
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
const char* keyName, const char* keyData,
const char* valueName, unsigned &valueData)
{
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
char query[stringSize];
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Read the result.
int src = sqlite3_run_query(DB,stmt);
bool retVal = false;
if (src == SQLITE_ROW) {
valueData = (unsigned)sqlite3_column_int64(stmt,0);
retVal = true;
}
sqlite3_finalize(stmt);
return retVal;
}
// This function returns an allocated string that must be free'd by the caller.
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
const char* keyName, const char* keyData,
const char* valueName, char* &valueData)
{
valueData=NULL;
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
char query[stringSize];
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Read the result.
int src = sqlite3_run_query(DB,stmt);
bool retVal = false;
if (src == SQLITE_ROW) {
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
if (ptr) valueData = strdup(ptr);
retVal = true;
}
sqlite3_finalize(stmt);
return retVal;
}
// This function returns an allocated string that must be free'd by tha caller.
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
const char* keyName, unsigned keyData,
const char* valueName, char* &valueData)
{
valueData=NULL;
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + 20;
char query[stringSize];
sprintf(query,"SELECT %s FROM %s WHERE %s == %u",valueName,tableName,keyName,keyData);
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Read the result.
int src = sqlite3_run_query(DB,stmt);
bool retVal = false;
if (src == SQLITE_ROW) {
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
if (ptr) valueData = strdup(ptr);
retVal = true;
}
sqlite3_finalize(stmt);
return retVal;
}
bool sqlite3_command(sqlite3* DB, const char* query)
{
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Run the query.
int src = sqlite3_run_query(DB,stmt);
sqlite3_finalize(stmt);
return src==SQLITE_DONE;
}

View File

@@ -1,29 +0,0 @@
#ifndef SQLITE3UTIL_H
#define SQLITE3UTIL_H
#include <sqlite3.h>
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query);
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt);
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
const char* keyName, const char* keyData,
const char* valueName, unsigned &valueData);
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
const char* keyName, const char* keyData,
const char* valueName, char* &valueData);
// This function returns an allocated string that must be free'd by the caller.
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
const char* keyName, unsigned keyData,
const char* valueName, char* &valueData);
bool sqlite3_exists(sqlite3* DB, const char* tableName,
const char* keyName, const char* keyData);
/** Run a query, ignoring the result; return true on success. */
bool sqlite3_command(sqlite3* DB, const char* query);
#endif

574
CommonLibs/trx_vty.c Normal file
View File

@@ -0,0 +1,574 @@
/*
* Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/misc.h>
#include "trx_vty.h"
#include "../config.h"
static struct trx_ctx* g_trx_ctx;
static const struct value_string clock_ref_names[] = {
{ REF_INTERNAL, "internal" },
{ REF_EXTERNAL, "external" },
{ REF_GPS, "gspdo" },
{ 0, NULL }
};
static const struct value_string filler_names[] = {
{ FILLER_DUMMY, "Dummy bursts" },
{ FILLER_ZERO, "Disabled" },
{ FILLER_NORM_RAND, "Normal bursts with random payload" },
{ FILLER_EDGE_RAND, "EDGE bursts with random payload" },
{ FILLER_ACCESS_RAND, "Access bursts with random payload" },
{ 0, NULL }
};
struct trx_ctx *trx_from_vty(struct vty *v)
{
/* It can't hurt to force callers to continue to pass the vty instance
* to this function, in case we'd like to retrieve the global
* trx instance from the vty at some point in the future. But
* until then, just return the global pointer, which should have been
* initialized by trx_vty_init().
*/
OSMO_ASSERT(g_trx_ctx);
return g_trx_ctx;
}
enum trx_vty_node {
TRX_NODE = _LAST_OSMOVTY_NODE + 1,
CHAN_NODE,
};
static struct cmd_node trx_node = {
TRX_NODE,
"%s(config-trx)# ",
1,
};
static struct cmd_node chan_node = {
CHAN_NODE,
"%s(config-trx-chan)# ",
1,
};
DEFUN(cfg_trx, cfg_trx_cmd,
"trx",
"Configure the TRX\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (!trx)
return CMD_WARNING;
vty->node = TRX_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
"bind-ip A.B.C.D",
"Set the IP address for the local bind\n"
"IPv4 Address\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
"remote-ip A.B.C.D",
"Set the IP address for the remote BTS\n"
"IPv4 Address\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_base_port, cfg_base_port_cmd,
"base-port <1-65535>",
"Set the TRX Base Port\n"
"TRX Base Port\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.base_port = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_dev_args, cfg_dev_args_cmd,
"dev-args DESC",
"Set the device-specific arguments to pass to the device\n"
"Device-specific arguments\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
"tx-sps (1|4)",
"Set the Tx Samples-per-Symbol\n"
"Tx Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.tx_sps = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
"rx-sps (1|4)",
"Set the Rx Samples-per-Symbol\n"
"Rx Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rx_sps = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd,
"test rtsc <0-7>",
"Set the Random Normal Burst test mode with TSC\n"
"TSC\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (trx->cfg.rach_delay_set) {
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
VTY_NEWLINE);
return CMD_WARNING;
}
trx->cfg.rtsc_set = true;
trx->cfg.rtsc = atoi(argv[0]);
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
trx->cfg.filler = FILLER_NORM_RAND;
return CMD_SUCCESS;
}
DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd,
"test rach-delay <0-68>",
"Set the Random Access Burst test mode with delay\n"
"RACH delay\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (trx->cfg.rtsc_set) {
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
VTY_NEWLINE);
return CMD_WARNING;
}
if (trx->cfg.egprs) {
vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s",
VTY_NEWLINE);
return CMD_WARNING;
}
trx->cfg.rach_delay_set = true;
trx->cfg.rach_delay = atoi(argv[0]);
trx->cfg.filler = FILLER_ACCESS_RAND;
return CMD_SUCCESS;
}
DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
"clock-ref (internal|external|gpsdo)",
"Set the Reference Clock\n"
"Enable internal referece (default)\n"
"Enable external 10 MHz reference\n"
"Enable GPSDO reference\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
"multi-arfcn (disable|enable)",
"Enable multi-ARFCN transceiver (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0) {
trx->cfg.multi_arfcn = false;
} else if (strcmp("enable", argv[0]) == 0) {
trx->cfg.multi_arfcn = true;
} else {
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_offset, cfg_offset_cmd,
"offset FLOAT",
"Set the baseband frequency offset (default=0, auto)\n"
"Baseband Frequency Offset\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.offset = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd,
"rssi-offset FLOAT",
"Set the RSSI to dBm offset in dB (default=0)\n"
"RSSI to dBm offset in dB\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rssi_offset = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
"swap-channels (disable|enable)",
"Swap channels (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0) {
trx->cfg.swap_channels = false;
} else if (strcmp("enable", argv[0]) == 0) {
trx->cfg.swap_channels = true;
} else {
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_egprs, cfg_egprs_cmd,
"egprs (disable|enable)",
"Enable EDGE receiver (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0) {
trx->cfg.egprs = false;
} else if (strcmp("enable", argv[0]) == 0) {
trx->cfg.egprs = true;
trx->cfg.filler = FILLER_EDGE_RAND;
} else {
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
"rt-prio <1-32>",
"Set the SCHED_RR real-time priority\n"
"Real time priority\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.sched_rr = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_filler, cfg_filler_cmd,
"filler dummy",
"Enable C0 filler table\n"
"Dummy method\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.filler = FILLER_DUMMY;
return CMD_SUCCESS;
}
DEFUN(cfg_chan, cfg_chan_cmd,
"chan <0-100>",
"Select a channel to configure\n"
"Channel index\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
int idx = atoi(argv[0]);
if (idx >= TRX_CHAN_MAX) {
vty_out(vty, "Chan list full.%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */
vty_out(vty, "Non-existent or non-consecutive chan %d.%s",
idx, VTY_NEWLINE);
return CMD_WARNING;
} else if (trx->cfg.num_chans == idx) { /* creating it */
trx->cfg.num_chans++;
trx->cfg.chans[idx].trx = trx;
trx->cfg.chans[idx].idx = idx;
}
vty->node = CHAN_NODE;
vty->index = &trx->cfg.chans[idx];
return CMD_SUCCESS;
}
DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd,
"rx-path NAME",
"Set the Rx Path\n"
"Rx Path name\n")
{
struct trx_chan *chan = vty->index;
osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd,
"tx-path NAME",
"Set the Tx Path\n"
"Tx Path name\n")
{
struct trx_chan *chan = vty->index;
osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]);
return CMD_SUCCESS;
}
static int dummy_config_write(struct vty *v)
{
return CMD_SUCCESS;
}
static int config_write_trx(struct vty *vty)
{
struct trx_chan *chan;
int i;
struct trx_ctx *trx = trx_from_vty(vty);
vty_out(vty, "trx%s", VTY_NEWLINE);
if (trx->cfg.bind_addr)
vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
if (trx->cfg.remote_addr)
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
if (trx->cfg.base_port != DEFAULT_TRX_PORT)
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
if (trx->cfg.dev_args)
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
if (trx->cfg.rx_sps != DEFAULT_RX_SPS)
vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
if (trx->cfg.rtsc_set)
vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
if (trx->cfg.rach_delay_set)
vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
if (trx->cfg.clock_ref != REF_INTERNAL)
vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE);
if (trx->cfg.offset != 0)
vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE);
if (trx->cfg.rssi_offset != 0)
vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE);
vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE);
if (trx->cfg.sched_rr != 0)
vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE);
for (i = 0; i < trx->cfg.num_chans; i++) {
chan = &trx->cfg.chans[i];
vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE);
if (chan->rx_path)
vty_out(vty, " rx-path %s%s", chan->rx_path, VTY_NEWLINE);
if (chan->tx_path)
vty_out(vty, " tx-path %s%s", chan->tx_path, VTY_NEWLINE);
}
return CMD_SUCCESS;
}
static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
{
struct trx_chan *chan;
int i;
vty_out(vty, "TRX Config:%s", VTY_NEWLINE);
vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE);
vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE);
vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc,
trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay,
trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE);
vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr,
trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE);
for (i = 0; i < trx->cfg.num_chans; i++) {
chan = &trx->cfg.chans[i];
vty_out(vty, " Channel %u:%s", chan->idx, VTY_NEWLINE);
if (chan->rx_path)
vty_out(vty, " Rx Path: %s%s", chan->rx_path, VTY_NEWLINE);
if (chan->tx_path)
vty_out(vty, " Tx Path: %s%s", chan->tx_path, VTY_NEWLINE);
}
}
DEFUN(show_trx, show_trx_cmd,
"show trx",
SHOW_STR "Display information on the TRX\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx_dump_vty(vty, trx);
return CMD_SUCCESS;
}
static int trx_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
case TRX_NODE:
case CHAN_NODE:
return 1;
default:
return 0;
}
}
static int trx_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case TRX_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
case CHAN_NODE:
vty->node = TRX_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
default:
OSMO_ASSERT(0);
}
return vty->node;
}
static const char trx_copyright[] =
"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n"
"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n"
"Copyright (C) 2015 Ettus Research LLC\r\n"
"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
struct vty_app_info g_vty_info = {
.name = "OsmoTRX",
.version = PACKAGE_VERSION,
.copyright = trx_copyright,
.go_parent_cb = trx_vty_go_parent,
.is_config_node = trx_vty_is_config_node,
};
struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
{
struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx);
trx->cfg.bind_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
trx->cfg.base_port = DEFAULT_TRX_PORT;
trx->cfg.tx_sps = DEFAULT_TX_SPS;
trx->cfg.rx_sps = DEFAULT_RX_SPS;
trx->cfg.filler = FILLER_ZERO;
return trx;
}
int trx_vty_init(struct trx_ctx* trx)
{
g_trx_ctx = trx;
install_element_ve(&show_trx_cmd);
install_element(CONFIG_NODE, &cfg_trx_cmd);
install_node(&trx_node, config_write_trx);
install_element(TRX_NODE, &cfg_bind_ip_cmd);
install_element(TRX_NODE, &cfg_remote_ip_cmd);
install_element(TRX_NODE, &cfg_base_port_cmd);
install_element(TRX_NODE, &cfg_dev_args_cmd);
install_element(TRX_NODE, &cfg_tx_sps_cmd);
install_element(TRX_NODE, &cfg_rx_sps_cmd);
install_element(TRX_NODE, &cfg_test_rtsc_cmd);
install_element(TRX_NODE, &cfg_test_rach_delay_cmd);
install_element(TRX_NODE, &cfg_clock_ref_cmd);
install_element(TRX_NODE, &cfg_multi_arfcn_cmd);
install_element(TRX_NODE, &cfg_offset_cmd);
install_element(TRX_NODE, &cfg_rssi_offset_cmd);
install_element(TRX_NODE, &cfg_swap_channels_cmd);
install_element(TRX_NODE, &cfg_egprs_cmd);
install_element(TRX_NODE, &cfg_rt_prio_cmd);
install_element(TRX_NODE, &cfg_filler_cmd);
install_element(TRX_NODE, &cfg_chan_cmd);
install_node(&chan_node, dummy_config_write);
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
return 0;
}

68
CommonLibs/trx_vty.h Normal file
View File

@@ -0,0 +1,68 @@
#pragma once
#include <osmocom/vty/command.h>
#include "config_defs.h"
extern struct vty_app_info g_vty_info;
#define TRX_CHAN_MAX 8
/* Samples-per-symbol for downlink path
* 4 - Uses precision modulator (more computation, less distortion)
* 1 - Uses minimized modulator (less computation, more distortion)
*
* Other values are invalid. Receive path (uplink) is always
* downsampled to 1 sps. Default to 4 sps for all cases.
*/
#define DEFAULT_TX_SPS 4
/*
* Samples-per-symbol for uplink (receiver) path
* Do not modify this value. EDGE configures 4 sps automatically on
* B200/B210 devices only. Use of 4 sps on the receive path for other
* configurations is not supported.
*/
#define DEFAULT_RX_SPS 1
/* Default configuration parameters */
#define DEFAULT_TRX_PORT 5700
#define DEFAULT_TRX_IP "127.0.0.1"
#define DEFAULT_CHANS 1
struct trx_ctx;
struct trx_chan {
struct trx_ctx *trx; /* backpointer */
unsigned int idx; /* channel index */
char *rx_path;
char *tx_path;
};
struct trx_ctx {
struct {
char *bind_addr;
char *remote_addr;
char *dev_args;
unsigned int base_port;
unsigned int tx_sps;
unsigned int rx_sps;
unsigned int rtsc;
bool rtsc_set;
unsigned int rach_delay;
bool rach_delay_set;
enum ReferenceType clock_ref;
enum FillerType filler;
bool multi_arfcn;
double offset;
double rssi_offset;
bool swap_channels;
bool egprs;
unsigned int sched_rr;
unsigned int num_chans;
struct trx_chan chans[TRX_CHAN_MAX];
} cfg;
};
int trx_vty_init(struct trx_ctx* trx);
struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx);

View File

@@ -41,10 +41,24 @@ const BitVector GSM::gTrainingSequence[] = {
BitVector("11101111000100101110111100"),
};
const BitVector GSM::gEdgeTrainingSequence[] = {
BitVector("111111001111111001111001001001111111111111001111111111001111111001111001001001"),
BitVector("111111001111001001111001001001111001001001001111111111001111001001111001001001"),
BitVector("111001111111111111001001001111001001001111001111111001111111111111001001001111"),
BitVector("111001111111111001001001001111001001111001111111111001111111111001001001001111"),
BitVector("111111111001001111001111001001001111111001111111111111111001001111001111001001"),
BitVector("111001111111001001001111001111001001111111111111111001111111001001001111001111"),
BitVector("001111001111111001001001001001111001001111111111001111001111111001001001001001"),
BitVector("001001001111001001001001111111111001111111001111001001001111001001001001111111"),
};
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
// |-head-||---------midamble----------------------||--------------data----------------||t|
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
{

View File

@@ -46,12 +46,15 @@ namespace GSM {
/** GSM Training sequences from GSM 05.02 5.2.3. */
extern const BitVector gTrainingSequence[];
extern const BitVector gEdgeTrainingSequence[];
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
extern const BitVector gDummyBurst;
/** Random access burst synch. sequence */
extern const BitVector gRACHSynchSequence;
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
extern const BitVector gRACHBurst;
/**@name Modulus operations for frame numbers. */

View File

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

20
LEGAL
View File

@@ -1,5 +1,8 @@
OpenBTS
The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details.
The related copyrights:
Most parts copyright 2008-2011 Free Software Foundation.
Some parts copyright 2010 Kestrel Signal Processing, Inc.
Some parts copyright 2011 Range Networks, Inc.
@@ -12,17 +15,9 @@ patented technologies. The user of this software is required to take whatever
actions are necessary to avoid patent infringement.
Trademark
"OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a
California corporation. Range reserves the right to control the use of this
trademark. Do not use this trademark in commerce without permission and do not
rebrand OpenBTS under a different trademark.
Telecom and Radio Spectrum Laws
The primary function of OpenBTS is the provision of telecommunications service
The primary function of OsmoTRX is the provision of telecommunications service
over a radio link. This activity is heavily regulated nearly everywhere in
the world. Users of this software are expected to comply with local and national
regulations in the jurisdictions where this sortware is used with radio equipment.
@@ -39,7 +34,7 @@ The legal restrictions listed here are not necessarily exhaustive.
Note to US Government Users
The OpenBTS software applications and associated documentation are "Commercial
The OsmoTRX software applications and associated documentation are "Commercial
Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of
"Commercial Computer Software" and "Commercial Computer Software Documentation,"
as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
@@ -54,13 +49,12 @@ and AGPLv3.
Note to US Government Contractors
GPL is not compatible with "government purpose rights" (GPR). If you receive
OpenBTS software under a GPL and deliver it under GPR, you will be in violation
OsmoTRX software under a GPL and deliver it under GPR, you will be in violation
of GPL and possibly subject to enforcement actions by the original authors and
copyright holders, including the Free Software Foundation, Inc.
Software Licensing and Distribution
A subset of OpenBTS is distributed publicly under AGPLv3. Range reserves the right to
distribute most of this source code other licenses as well. See the COPYING file
The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file
for more information on the license for this distribution.

View File

@@ -22,16 +22,16 @@ include $(top_srcdir)/Makefile.common
ACLOCAL_AMFLAGS = -I config
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
AM_CXXFLAGS = -Wall -pthread -ldl
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
AM_CXXFLAGS = -Wall -pthread
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
# Order must be preserved
SUBDIRS = \
sqlite3 \
CommonLibs \
GSM \
Transceiver52M
Transceiver52M \
tests
EXTRA_DIST = \
autogen.sh \
@@ -40,6 +40,9 @@ EXTRA_DIST = \
COPYING \
README
.PHONY: release
@RELMAKE@
dox: FORCE
doxygen doxconfig

View File

@@ -18,21 +18,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
top_srcdir = $(abs_top_srcdir)
top_builddir = $(abs_top_builddir)
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
GSM_INCLUDEDIR = $(top_srcdir)/GSM
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
STD_DEFINES_AND_INCLUDES = \
$(SVNDEV) \
-I$(COMMON_INCLUDEDIR) \
-I$(GSM_INCLUDEDIR) \
-I$(SQLITE_INCLUDEDIR)
-I$(GSM_INCLUDEDIR)
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
GSM_LA = $(top_builddir)/GSM/libGSM.la
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl
if ARCH_ARM
ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la
else
ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la
endif
MOSTLYCLEANFILES = *~

260
README
View File

@@ -1,168 +1,116 @@
Welcome to the OpenBTS source code.
This is the interface to the transcevier.
For free support, please subscribe to openbts-discuss@lists.sourceforge.net.
See http://sourceforge.net/mailarchive/forum.php?forum_name=openbts-discuss
and https://lists.sourceforge.net/lists/listinfo/openbts-discuss for details.
For additional information, refer to http://openbts.org.
These are the directories:
AsteriskConfig Asterisk configuration files for use with OpenBTS.
CommonLib Common-use libraries, mostly C++ wrappers for basic facilities.
Control Control-layer functions for the protocols of GSM 04.08 and SIP.
GSM The GSM stack.
SIP Components of the SIP state machines ued by the control layer.
SMS The SMS stack.
SR The subscriber registry.
TRXManager The interface between the GSM stack and the radio.
Transceiver The software transceiver and specific installation tests.
apps OpenBTS application binaries.
doc Project documentation.
tests Test fixtures for subsets of OpenBTS components.
smqueue RFC-3428 store-and-forward server for SMS
Each TRX Manager UDP socket interface represents a single ARFCN.
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
Give a base port B (5700), the master clock interface is at port P=B.
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
The corresponding core-side interface for every socket is at P+100.
For any given build, the number of ARFCN interfaces can be fixed.
By default, OpenBTS assumes the following UDP port assignments:
Indications on the Master Clock Interface
5060 -- Asterisk SIP interface
5061 -- local SIP softphone
5062 -- OpenBTS SIP interface
5063 -- smqueue SIP interface
5064 -- subscriber registry SIP interface
5700-range -- OpenBTS-transceiver interface
The master clock interface is output only (from the radio).
Messages are "indications".
These can be controlled in the CONFIG table in /etc/OpenBTS.db.
CLOCK gives the current value of the transceiver clock to be used by the core.
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
IND CLOCK <totalFrames>
Standrd paths:
/OpenBTS -- Binary installation.
/etc/OpenBTS -- Configuration databases.
/var/run/OpenBTS -- Real-time reporting databases.
The script apps/setUpFiles.sh will create these directories and install the
correct files in them.
Commands on the Per-ARFCN Control Interface
The per-ARFCN control interface uses a command-reponse protocol.
Commands are NULL-terminated ASCII strings, one per UDP socket.
Each command has a corresponding response.
Every command is of the form:
CMD <cmdtype> [params]
The <cmdtype> is the actual command.
Parameters are optional depending on the commands type.
Every response is of the form:
RSP <cmdtype> <status> [result]
The <status> is 0 for success and a non-zero error code for failure.
Successful responses may include results, depending on the command type.
Power Control
POWEROFF shuts off transmitter power and stops the demodulator.
CMD POWEROFF
RSP POWEROFF <status>
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
This command fails if the transmitter and receiver are not yet tuned.
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
If the transceiver is already on, it response with success to this command.
CMD POWERON
RSP POWERON <status>
SETPOWER sets output power in dB wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD SETPOWER <dB>
RSP SETPOWER <status> <dB>
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD ADJPOWER <dBStep>
RSP ADJPOWER <status> <dBLevel>
Tuning Control
RXTUNE tunes the receiver to a given frequency in kHz.
This command fails if the receiver is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD RXTUNE <kHz>
RSP RXTUNE <status> <kHz>
TXTUNE tunes the transmitter to a given frequency in kHz.
This command fails if the transmitter is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD TXTUNE <kHz>
RSP TXTUNE <status> <kHz>
Timeslot Control
SETSLOT sets the format of the uplink timeslots in the ARFCN.
The <timeslot> indicates the timeslot of interest.
The <chantype> indicates the type of channel that occupies the timeslot.
A chantype of zero indicates the timeslot is off.
CMD SETSLOT <timeslot> <chantype>
RSP SETSLOT <status> <timeslot> <chantype>
Messages on the per-ARFCN Data Interface
Messages on the data interface carry one radio burst per UDP message.
Received Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte RSSI in -dBm
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
Transmit Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte transmit level wrt ARFCN max, -dB (attenuation)
148 bytes output symbol values, 0 & 1
Release history:
Release Name SVN Reposiory SVN Rev Comments
1.0 (none) SF.net ?? completed L1, L2
1.1 Arnaudville GNU Radio r10019 (trunk)
1.2 Breaux Bridge GNU Radio r10088 (trunk) GNU Build, very early assignment
1.3 Carencro KSP r1 (trunk) first post-injunction release
1.4 Donaldsonville KSP r23 (trunk) fixed Ubuntu build error
1.5 Eunice KSP r39 (trunk) fixed L2 bugs related to segmentation
removed incomplete SMS directory
moved "abort" calls into L3 subclasses
1.6 New Iberia KSP r130 (trunk) import of all 2.2 improvements to non-SMS release
2.0 St. Francisville KSP r54 (smswork) SMS support
file-based configuration
2.1 Grand Coteau KSP r70 (smswork) DTMF support
fixed more Linux-related build errors
-lpthread
TLMessage constructor
expanded stack to prevent overflows in Linux
moved gSIPInterface to main app
fixed iterator bug in Pager
2.2 Houma KSP r122 (smswork) added LEGAL notice
removed Assert classes
stop paging on page response
fixed Pager-spin bug
fixed Transceiver spin bugs
fixed 2^32 microsecond rollover bug
reduced stack footprints in Transceiver
fixed SMS timestamps
check LAI before using TMSI in LUR
reduced memory requirement by 75%
removed PagerTest
fixed stale-transaction bug in paging handler
fixed USRP clock rollover bug
faster call connection
new USRPDevice design
2.3 Jean Lafitte KSP r190? (trunk) check for out-of-date RACH bursts
better TRX-GSM clock sync
formal logging system
command line interface
emergency call setup
2.4 Kinder KSP r208? (trunk) fixed BCCH neighbor list bug
support for neighbor lists
fixed support for non-local Asterisk servers
cleaner configuration management
more realtime control of BCCH parameters
proper rejection of Hold messages
fixed L3 hanging bug in MTDCheckBYE
2.4.1 Kinder KSP r462 fixed lots of valgrind errors
2.4.2 Kinder KSP r482 zero-length calling party number bug
g++ 4.4 #includes
2.5 Lacassine KSP r551 imported Joshua Lackey patches
SIP fixes from Anne Kwong
SIP fixes from testing with SMS server
L3 TI handling fixes
SMS server support
GNU Radio 3.2 compatibility
configurable max range and LU-reject cause
"page" & "testcall" CLI features
2.5.1 Lacassine KSP r595 fixed some build bugs for some Linux distros
2.5.2 Lacassine KSP r630 fixed channel assignment bug for Nokia DCT4+ handsets
2.5.3 Lacassine KSP r756 merged fix for transceiver startup crash
due to use of uninitialized variables (r646)
merged fix for fusb bug from trunk (r582)
2.5.4 Lacassine KSP r812 merged fixes to build under latest Fedora and
to build with git GnuRadio (r814)
2.6 Mamou KSP r886 fixed infamous fusb bug (r582)
fixed idle-filling table size bug
smoother uplink power control
load-limiting downlink power control
new "config" features (optional, static)
IMEI interrogation
fixed MOD "missing FIFO" bug
configurable short code features
fixed transceiver startup crash (r646)
readline support is back
fixed timing advance bug (r844)
added CLI "chans" command
track time-of-use in TMSI table (r844)
added CLI "noise" command (r844)
added CLI "rxpower" command (r844)
added CLI "unconfig" command
2.7 Natchitoches Range rxxx (never released publicly)
converted TMSITable to sqlite3 (r902)
sqlite3-based configuration (r???)
converted Logger to syslogd (r903)
added support for rest octets (r1022)
external database for transaction reporting (r1184)
external database for channel status reporting (r1203)
in-call delivery and submission of text messages (r1231)
RFC-2833 DMTF (r1249)
2.8 Opelousas Range rxxx move databases to /etc and /var
RRLP aiding support

View File

@@ -0,0 +1,107 @@
/*
* Polyphase channelizer
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include "Channelizer.h"
extern "C" {
#include "fft.h"
#include "convolve.h"
}
static void deinterleave(const float *in, size_t ilen,
float **out, size_t olen, size_t m)
{
size_t i, n;
for (i = 0; i < olen; i++) {
for (n = 0; n < m; n++) {
out[m - 1 - n][2 * i + 0] = in[2 * (i * m + n) + 0];
out[m - 1 - n][2 * i + 1] = in[2 * (i * m + n) + 1];
}
}
}
size_t Channelizer::inputLen() const
{
return blockLen * m;
}
size_t Channelizer::outputLen() const
{
return blockLen;
}
float *Channelizer::outputBuffer(size_t chan) const
{
if (chan >= m)
return NULL;
return hInputs[chan];
}
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool Channelizer::rotate(const float *in, size_t len)
{
size_t hSize = 2 * hLen * sizeof(float);
if (!checkLen(blockLen, len))
return false;
deinterleave(in, len, hInputs, blockLen, m);
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
convolve_real(hInputs[i], blockLen,
subFilters[i], hLen,
hOutputs[i], blockLen,
0, blockLen, 1, 0);
}
cxvec_fft(fftHandle);
return true;
}
/* Setup channelizer paramaters */
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
: ChannelizerBase(m, blockLen, hLen)
{
}
Channelizer::~Channelizer()
{
}

View File

@@ -0,0 +1,34 @@
#ifndef _CHANNELIZER_RX_H_
#define _CHANNELIZER_RX_H_
#include "ChannelizerBase.h"
class Channelizer : public ChannelizerBase {
public:
/** Constructor for channelizing filter bank
@param m number of physical channels
@param blockLen number of samples per output of each iteration
@param hLen number of taps in each constituent filter path
*/
Channelizer(size_t m, size_t blockLen, size_t hLen = 16);
~Channelizer();
/* Return required input and output buffer lengths */
size_t inputLen() const;
size_t outputLen() const;
/** Rotate "input commutator" and drive samples through filterbank
@param in complex input vector
@param iLen number of samples in buffer (must match block length)
@return false on error and true otherwise
*/
bool rotate(const float *in, size_t iLen);
/** Get buffer for an output path
@param chan channel number of filterbank
@return NULL on error and pointer to buffer otherwise
*/
float *outputBuffer(size_t chan) const;
};
#endif /* _CHANNELIZER_RX_H_ */

View File

@@ -0,0 +1,251 @@
/*
* Polyphase channelizer
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <malloc.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include "Logger.h"
#include "ChannelizerBase.h"
extern "C" {
#include "fft.h"
}
static float sinc(float x)
{
if (x == 0.0f)
return 0.999999999999f;
return sin(M_PI * x) / (M_PI * x);
}
/*
* There are more efficient reversal algorithms, but we only reverse at
* initialization so we don't care.
*/
static void reverse(float *buf, size_t len)
{
float tmp[2 * len];
memcpy(tmp, buf, 2 * len * sizeof(float));
for (size_t i = 0; i < len; i++) {
buf[2 * i + 0] = tmp[2 * (len - 1 - i) + 0];
buf[2 * i + 1] = tmp[2 * (len - 1 - i) + 1];
}
}
/*
* Create polyphase filterbank
*
* Implementation based material found in,
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool ChannelizerBase::initFilters()
{
size_t protoLen = m * hLen;
float *proto;
float sum = 0.0f, scale = 0.0f;
float midpt = (float) (protoLen - 1.0) / 2.0;
/*
* Allocate 'M' partition filters and the temporary prototype
* filter. Coefficients are real only and must be 16-byte memory
* aligned for SSE usage.
*/
proto = new float[protoLen];
if (!proto)
return false;
subFilters = (float **) malloc(sizeof(float *) * m);
if (!subFilters) {
delete[] proto;
return false;
}
for (size_t i = 0; i < m; i++) {
subFilters[i] = (float *)
memalign(16, hLen * 2 * sizeof(float));
}
/*
* Generate the prototype filter with a Blackman-harris window.
* Scale coefficients with DC filter gain set to unity divided
* by the number of channels.
*/
float a0 = 0.35875;
float a1 = 0.48829;
float a2 = 0.14128;
float a3 = 0.01168;
for (size_t i = 0; i < protoLen; i++) {
proto[i] = sinc(((float) i - midpt) / (float) m);
proto[i] *= a0 -
a1 * cos(2 * M_PI * i / (protoLen - 1)) +
a2 * cos(4 * M_PI * i / (protoLen - 1)) -
a3 * cos(6 * M_PI * i / (protoLen - 1));
sum += proto[i];
}
scale = (float) m / sum;
/*
* Populate partition filters and reverse the coefficients per
* convolution requirements.
*/
for (size_t i = 0; i < hLen; i++) {
for (size_t n = 0; n < m; n++) {
subFilters[n][2 * i + 0] = proto[i * m + n] * scale;
subFilters[n][2 * i + 1] = 0.0f;
}
}
for (size_t i = 0; i < m; i++)
reverse(subFilters[i], hLen);
delete[] proto;
return true;
}
bool ChannelizerBase::initFFT()
{
size_t size;
if (fftInput || fftOutput || fftHandle)
return false;
size = blockLen * m * 2 * sizeof(float);
fftInput = (float *) fft_malloc(size);
memset(fftInput, 0, size);
size = (blockLen + hLen) * m * 2 * sizeof(float);
fftOutput = (float *) fft_malloc(size);
memset(fftOutput, 0, size);
if (!fftInput | !fftOutput) {
LOG(ALERT) << "Memory allocation error";
return false;
}
fftHandle = init_fft(0, m, blockLen, blockLen + hLen,
fftInput, fftOutput, hLen);
return true;
}
bool ChannelizerBase::mapBuffers()
{
if (!fftHandle) {
LOG(ALERT) << "FFT buffers not initialized";
return false;
}
hInputs = (float **) malloc(sizeof(float *) * m);
hOutputs = (float **) malloc(sizeof(float *) * m);
if (!hInputs | !hOutputs)
return false;
for (size_t i = 0; i < m; i++) {
hInputs[i] = &fftOutput[2 * (i * (blockLen + hLen) + hLen)];
hOutputs[i] = &fftInput[2 * (i * blockLen)];
}
return true;
}
/*
* Setup filterbank internals
*/
bool ChannelizerBase::init()
{
/*
* Filterbank coefficients, fft plan, history, and output sample
* rate conversion blocks
*/
if (!initFilters()) {
LOG(ALERT) << "Failed to initialize channelizing filter";
return false;
}
hist = (float **) malloc(sizeof(float *) * m);
for (size_t i = 0; i < m; i++) {
hist[i] = new float[2 * hLen];
memset(hist[i], 0, 2 * hLen * sizeof(float));
}
if (!initFFT()) {
LOG(ALERT) << "Failed to initialize FFT";
return false;
}
mapBuffers();
return true;
}
/* Check vector length validity */
bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
{
if (outerLen != innerLen * m) {
LOG(ALERT) << "Invalid outer length " << innerLen
<< " is not multiple of " << blockLen;
return false;
}
if (innerLen != blockLen) {
LOG(ALERT) << "Invalid inner length " << outerLen
<< " does not equal " << blockLen;
return false;
}
return true;
}
/*
* Setup channelizer paramaters
*/
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
: fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
{
this->m = m;
this->hLen = hLen;
this->blockLen = blockLen;
}
ChannelizerBase::~ChannelizerBase()
{
free_fft(fftHandle);
for (size_t i = 0; i < m; i++) {
free(subFilters[i]);
delete hist[i];
}
fft_free(fftInput);
fft_free(fftOutput);
free(hInputs);
free(hOutputs);
free(hist);
}

View File

@@ -0,0 +1,39 @@
#ifndef _CHANNELIZER_BASE_H_
#define _CHANNELIZER_BASE_H_
class ChannelizerBase {
protected:
ChannelizerBase(size_t m, size_t blockLen, size_t hLen);
~ChannelizerBase();
/* Channelizer parameters */
size_t m;
size_t hLen;
size_t blockLen;
/* Channelizer filterbank sub-filters */
float **subFilters;
/* Input/Output buffers */
float **hInputs, **hOutputs, **hist;
float *fftInput, *fftOutput;
/* Pointer to opaque FFT instance */
struct fft_hdl *fftHandle;
/* Initializer internals */
bool initFilters();
bool initFFT();
void releaseFilters();
/* Map overlapped FFT and filter I/O buffers */
bool mapBuffers();
/* Buffer length validity checking */
bool checkLen(size_t innerLen, size_t outerLen);
public:
/* Initilize channelizer/synthesis filter internals */
bool init();
};
#endif /* _CHANNELIZER_BASE_H_ */

View File

@@ -21,22 +21,10 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I./common
AM_CXXFLAGS = -ldl -lpthread
SUBDIRS = arch device
SUBDIRS = arm x86
if ARCH_ARM
ARCH_LA = arm/libarch.la
else
ARCH_LA = x86/libarch.la
endif
if USRP1
AM_CPPFLAGS += $(USRP_CFLAGS)
else
AM_CPPFLAGS += $(UHD_CFLAGS)
endif
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
rev2dir = $(datadir)/usrp/rev2
rev4dir = $(datadir)/usrp/rev4
@@ -44,55 +32,70 @@ rev4dir = $(datadir)/usrp/rev4
dist_rev2_DATA = std_inband.rbf
dist_rev4_DATA = std_inband.rbf
EXTRA_DIST = \
README \
README.Talgorithm
EXTRA_DIST = README
noinst_LTLIBRARIES = libtransceiver.la
noinst_LTLIBRARIES = libtransceiver_common.la
COMMON_SOURCES = \
radioInterface.cpp \
radioVector.cpp \
radioClock.cpp \
radioBuffer.cpp \
sigProcLib.cpp \
signalVector.cpp \
Transceiver.cpp
Transceiver.cpp \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp
libtransceiver_la_SOURCES = \
libtransceiver_common_la_SOURCES = \
$(COMMON_SOURCES) \
Resampler.cpp \
radioInterfaceResamp.cpp \
radioInterfaceDiversity.cpp
bin_PROGRAMS = osmo-trx
radioInterfaceMulti.cpp
noinst_HEADERS = \
Complex.h \
radioInterface.h \
radioVector.h \
radioClock.h \
radioDevice.h \
radioBuffer.h \
sigProcLib.h \
signalVector.h \
Transceiver.h \
USRPDevice.h \
Resampler.h \
common/convolve.h \
common/convert.h \
common/scale.h \
common/mult.h
ChannelizerBase.h \
Channelizer.h \
Synthesis.h
osmo_trx_SOURCES = osmo-trx.cpp
osmo_trx_LDADD = \
libtransceiver.la \
COMMON_LDADD = \
libtransceiver_common.la \
$(ARCH_LA) \
$(GSM_LA) \
$(COMMON_LA) $(SQLITE_LA)
$(COMMON_LA) \
$(FFTWF_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS)
if USRP1
libtransceiver_la_SOURCES += USRPDevice.cpp
osmo_trx_LDADD += $(USRP_LIBS)
else
libtransceiver_la_SOURCES += UHDDevice.cpp
osmo_trx_LDADD += $(UHD_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

View File

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

View File

@@ -20,6 +20,9 @@
#ifndef _RESAMPLER_H_
#define _RESAMPLER_H_
#include <vector>
#include <complex>
class Resampler {
public:
/* Constructor for rational sample rate conversion
@@ -52,7 +55,7 @@ public:
* Input and output vector lengths must of be equal multiples of the
* rational conversion rate denominator and numerator respectively.
*/
int rotate(float *in, size_t in_len, float *out, size_t out_len);
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
/* Get filter length
* @return number of taps in each filter partition
@@ -63,15 +66,11 @@ private:
size_t p;
size_t q;
size_t filt_len;
size_t *in_index;
size_t *out_path;
std::vector<size_t> in_index;
std::vector<size_t> out_path;
std::vector<std::complex<float> *> partitions;
float **partitions;
float *history;
bool initFilters(float bw);
void releaseFilters();
void computePath();
void initFilters(float bw);
};
#endif /* _RESAMPLER_H_ */

View File

@@ -0,0 +1,121 @@
/*
* Polyphase synthesis filter
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include <iostream>
#include "Synthesis.h"
extern "C" {
#include "fft.h"
#include "convolve.h"
}
static void interleave(float **in, size_t ilen,
float *out, size_t m)
{
size_t i, n;
for (i = 0; i < ilen; i++) {
for (n = 0; n < m; n++) {
out[2 * (i * m + n) + 0] = in[n][2 * i + 0];
out[2 * (i * m + n) + 1] = in[n][2 * i + 1];
}
}
}
size_t Synthesis::inputLen() const
{
return blockLen;
}
size_t Synthesis::outputLen() const
{
return blockLen * m;
}
float *Synthesis::inputBuffer(size_t chan) const
{
if (chan >= m)
return NULL;
return hOutputs[chan];
}
bool Synthesis::resetBuffer(size_t chan)
{
if (chan >= m)
return false;
memset(hOutputs[chan], 0, blockLen * 2 * sizeof(float));
return true;
}
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool Synthesis::rotate(float *out, size_t len)
{
size_t hSize = 2 * hLen * sizeof(float);
if (!checkLen(blockLen, len)) {
std::cout << "Length fail" << std::endl;
exit(1);
return false;
}
cxvec_fft(fftHandle);
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
convolve_real(hInputs[i], blockLen,
subFilters[i], hLen,
hOutputs[i], blockLen,
0, blockLen, 1, 0);
}
/* Interleave into output vector */
interleave(hOutputs, blockLen, out, m);
return true;
}
Synthesis::Synthesis(size_t m, size_t blockLen, size_t hLen)
: ChannelizerBase(m, blockLen, hLen)
{
}
Synthesis::~Synthesis()
{
}

View File

@@ -0,0 +1,35 @@
#ifndef _SYNTHESIS_H_
#define _SYNTHESIS_H_
#include "ChannelizerBase.h"
class Synthesis : public ChannelizerBase {
public:
/** Constructor for synthesis filterbank
@param m number of physical channels
@param blockLen number of samples per output of each iteration
@param hLen number of taps in each constituent filter path
*/
Synthesis(size_t m, size_t blockLen, size_t hLen = 16);
~Synthesis();
/* Return required input and output buffer lengths */
size_t inputLen() const;
size_t outputLen() const;
/** Rotate "output commutator" and drive samples through filterbank
@param out complex output vector
@param oLen number of samples in buffer (must match block length * m)
@return false on error and true otherwise
*/
bool rotate(float *out, size_t oLen);
/** Get buffer for an input path
@param chan channel number of filterbank
@return NULL on error and pointer to buffer otherwise
*/
float *inputBuffer(size_t chan) const;
bool resetBuffer(size_t chan);
};
#endif /* _SYNTHESIS_H_ */

View File

@@ -22,6 +22,8 @@
*/
#include <stdio.h>
#include <iomanip> // std::setprecision
#include <fstream>
#include "Transceiver.h"
#include <Logger.h>
@@ -33,12 +35,6 @@ using namespace GSM;
#define USB_LATENCY_INTRVL 10,0
#if USE_UHD
# define USB_LATENCY_MIN 6,7
#else
# define USB_LATENCY_MIN 1,1
#endif
/* Number of running values use in noise average */
#define NOISE_CNT 20
@@ -69,89 +65,68 @@ TransceiverState::~TransceiverState()
}
}
static BitVector *genRandNormalBurst(size_t tsc)
bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
{
if (tsc > 7)
return NULL;
BitVector *bits = new BitVector(148);
size_t i = 0;
/* Tail bits */
for (; i < 4; i++)
(*bits)[i] = 0;
/* Random bits */
for (; i < 61; i++)
(*bits)[i] = rand() % 2;
/* Training sequence */
for (int j = 0; i < 87; i++, j++)
(*bits)[i] = GSM::gTrainingSequence[tsc][j];
/* Random bits */
for (; i < 144; i++)
(*bits)[i] = rand() % 2;
/* Tail bits */
for (; i < 148; i++)
(*bits)[i] = 0;
return bits;
}
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
{
BitVector *bits;
signalVector *burst;
if ((sps != 1) && (sps != 4))
return false;
for (size_t n = 0; n < 8; n++) {
size_t guard = 8 + !(n % 4);
size_t len = sps == 4 ? 625 : 148 + guard;
for (size_t i = 0; i < 102; i++) {
switch (filler) {
case Transceiver::FILLER_DUMMY:
burst = modulateBurst(gDummyBurst, guard, sps);
case FILLER_DUMMY:
burst = generateDummyBurst(sps, n);
break;
case Transceiver::FILLER_RAND:
bits = genRandNormalBurst(rtsc);
burst = modulateBurst(*bits, guard, sps);
delete bits;
case FILLER_NORM_RAND:
burst = genRandNormalBurst(rtsc, sps, n);
break;
case Transceiver::FILLER_ZERO:
case FILLER_EDGE_RAND:
burst = generateEdgeBurst(rtsc);
break;
case FILLER_ACCESS_RAND:
burst = genRandAccessBurst(rach_delay, sps, n);
break;
case FILLER_ZERO:
default:
burst = new signalVector(len);
burst = generateEmptyBurst(sps, n);
}
scaleVector(*burst, scale);
fillerTable[i][n] = burst;
}
if (filler == Transceiver::FILLER_RAND)
chanType[n] = Transceiver::TSC;
if ((filler == FILLER_NORM_RAND) ||
(filler == FILLER_EDGE_RAND)) {
chanType[n] = TSC;
}
}
return false;
}
Transceiver::Transceiver(int wBasePort,
const char *wTRXAddress,
size_t wSPS, size_t wChans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface)
: mBasePort(wBasePort), mAddr(wTRXAddress),
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
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),
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0)
rssiOffset(wRssiOffset),
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()
@@ -180,7 +155,7 @@ Transceiver::~Transceiver()
* are still expected to report clock indications through control channel
* activity.
*/
bool Transceiver::init(int filler, size_t rtsc)
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge)
{
int d_srcport, d_dstport, c_srcport, c_dstport;
@@ -189,11 +164,13 @@ bool Transceiver::init(int filler, size_t rtsc)
return false;
}
if (!sigProcLibSetup(mSPSTx)) {
if (!sigProcLibSetup()) {
LOG(ALERT) << "Failed to initialize signal processing library";
return false;
}
mEdge = edge;
mDataSockets.resize(mChans);
mCtrlSockets.resize(mChans);
mControlServiceLoopThreads.resize(mChans);
@@ -215,8 +192,8 @@ bool Transceiver::init(int filler, size_t rtsc)
d_srcport = mBasePort + 2 * i + 2;
d_dstport = mBasePort + 2 * i + 102;
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
}
/* Randomize the central clock */
@@ -236,7 +213,7 @@ bool Transceiver::init(int filler, size_t rtsc)
if (i && filler == FILLER_DUMMY)
filler = FILLER_ZERO;
mStates[i].init(filler, mSPSTx, txFullScale, rtsc);
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
}
return true;
@@ -291,7 +268,7 @@ bool Transceiver::start()
TxUpperLoopAdapter, (void*) chan);
}
writeClockInterface();
mForceClockInterface = true;
mOn = true;
return true;
}
@@ -315,6 +292,10 @@ void Transceiver::stop()
LOG(NOTICE) << "Stopping the transceiver";
mTxLowerLoopThread->cancel();
mRxLowerLoopThread->cancel();
mTxLowerLoopThread->join();
mRxLowerLoopThread->join();
delete mTxLowerLoopThread;
delete mRxLowerLoopThread;
for (size_t i = 0; i < mChans; i++) {
mRxServiceLoopThreads[i]->cancel();
@@ -333,11 +314,6 @@ void Transceiver::stop()
mTxPriorityQueues[i].clear();
}
mTxLowerLoopThread->join();
mRxLowerLoopThread->join();
delete mTxLowerLoopThread;
delete mRxLowerLoopThread;
mOn = false;
LOG(NOTICE) << "Transceiver stopped";
}
@@ -358,7 +334,12 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
return;
}
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
/* 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);
@@ -392,7 +373,8 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
state = &mStates[i];
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->USRP interface ("
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
if (state->mRetrans)
updateFillerTable(i, burst);
delete burst;
@@ -443,7 +425,7 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
case V:
state->fillerModulus[timeslot] = 51;
break;
//case V:
//case V:
case VII:
state->fillerModulus[timeslot] = 102;
break;
@@ -456,12 +438,18 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
}
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
size_t chan)
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:
@@ -471,16 +459,25 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
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;
/*if (burstFN % 26 == 25)
return IDLE;
else
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:
@@ -495,6 +492,8 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
return RACH;
else if ((mod51 == 45) || (mod51 == 46))
return RACH;
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
return RACH;
else
return TSC;
break;
@@ -502,6 +501,8 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
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;
@@ -527,104 +528,32 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
}
}
/*
* Detect RACH synchronization sequence within a burst. No equalization
* is used or available on the RACH channel.
*/
bool Transceiver::detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa)
void writeToFile(radioVector *radio_burst, size_t chan)
{
float threshold = 6.0;
return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
}
/*
* Detect normal burst training sequence midamble. Update equalization
* state information and channel estimate if necessary. Equalization
* is currently disabled.
*/
bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, GSM::Time &time)
{
int tn = time.TN();
float chanOffset, threshold = 5.0;
bool noise, needDFE = false, estimateChan = false;
double elapsed = time - state->chanEstimateTime[tn];
signalVector *chanResp;
/* Check equalization update state */
if (needDFE && ((elapsed > 50) || (!state->chanResponse[tn]))) {
delete state->DFEForward[tn];
delete state->DFEFeedback[tn];
state->DFEForward[tn] = NULL;
state->DFEFeedback[tn] = NULL;
estimateChan = true;
}
/* Detect normal burst midambles */
if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
&toa, mMaxExpectedDelay, estimateChan,
&chanResp, &chanOffset)) {
return false;
}
noise = state->mNoiseLev;
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
/* Set equalizer if unabled */
if (needDFE && estimateChan) {
state->chanResponse[tn] = chanResp;
state->chanRespOffset[tn] = chanOffset;
state->chanRespAmplitude[tn] = amp;
scaleVector(*chanResp, complex(1.0, 0.0) / amp);
designDFE(*chanResp, state->SNRestimate[tn],
7, &state->DFEForward[tn], &state->DFEFeedback[tn]);
state->chanEstimateTime[tn] = time;
}
return true;;
}
/*
* Demodulate GMSK burst using equalization if requested. Otherwise
* demodulate by direct rotation and soft slicing.
*/
SoftVector *Transceiver::demodulate(TransceiverState *state,
signalVector &burst, complex amp,
float toa, size_t tn, bool equalize)
{
if (equalize) {
scaleVector(burst, complex(1.0, 0.0) / amp);
return equalizeBurst(burst,
toa - state->chanRespOffset[tn],
mSPSRx,
*state->DFEForward[tn],
*state->DFEFeedback[tn]);
}
return demodulateBurst(burst, mSPSRx, amp, toa);
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.
* and burst correlation type. Equalzation is currently disabled.
*/
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
int &timingOffset, size_t chan)
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan)
{
bool success, equalize = false;
int rc;
complex amp;
float toa, pow, max = -1.0, avg = 0.0;
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();
@@ -635,14 +564,26 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
GSM::Time time = radio_burst->getTime();
CorrType type = expectedCorrType(time, chan);
if ((type == OFF) || (type == IDLE)) {
/* 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++) {
energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow);
float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
if (pow > max) {
max = pow;
max_i = i;
@@ -659,41 +600,48 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
/* Average noise on diversity paths and update global levels */
burst = radio_burst->getVector(max_i);
avg = sqrt(avg / radio_burst->chans());
state->mNoiseLev = state->mNoises.avg();
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 */
if (type == TSC)
success = detectTSC(state, *burst, amp, toa, time);
else
success = detectRACH(state, *burst, amp, toa);
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa,
(type==RACH)?mMaxExpectedDelayAB:mMaxExpectedDelayNB);
/* Update noise average if no bust detected or alert on error */
if (success <= 0) {
if (!success) {
state->mNoises.insert(avg);
} else if (success == -SIGERR_CLIP) {
LOG(ALERT) << "Clipping detected on RACH input";
} else if (success < 0) {
LOG(ALERT) << "Unhandled RACH error";
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;
}
/* Demodulate and set output info */
if (equalize && (type != TSC))
equalize = false;
timingOffset = toa;
if (avg - state->mNoiseLev > 0.0)
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
wTime = time;
RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
timingOffset = (int) round(toa * 256.0 / mSPSRx);
bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
delete radio_burst;
return bits;
}
@@ -703,62 +651,113 @@ void Transceiver::reset()
mTxPriorityQueues[i].clear();
}
void Transceiver::driveControl(size_t chan)
#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)
{
int MAX_PACKET_LENGTH = 100;
size_t cmd_len = strlen(cmd);
// check control socket
char buffer[MAX_PACKET_LENGTH];
int msgLen = -1;
buffer[0] = '\0';
/* Check a command itself */
if (strncmp(buf, cmd, cmd_len))
return false;
msgLen = mCtrlSockets[chan]->read(buffer);
/* A command has arguments */
if (params != NULL) {
/* Make sure there is a space */
if (buf[cmd_len] != ' ')
return false;
if (msgLen < 1) {
return;
/* Update external pointer */
*params = buf + cmd_len + 1;
}
char cmdcheck[4];
char command[MAX_PACKET_LENGTH];
char response[MAX_PACKET_LENGTH];
return true;
}
sscanf(buffer,"%3s %s",cmdcheck,command);
void Transceiver::driveControl(size_t chan)
{
char buffer[MAX_PACKET_LENGTH + 1];
char response[MAX_PACKET_LENGTH + 1];
char *command, *params;
int msgLen;
if (!chan)
writeClockInterface();
/* Attempt to read from control socket */
msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
if (msgLen < 1)
return;
if (strcmp(cmdcheck,"CMD")!=0) {
/* Zero-terminate received string */
buffer[msgLen] = '\0';
/* Verify a command signature */
if (strncmp(buffer, "CMD ", 4)) {
LOG(WARNING) << "bogus message on control interface";
return;
}
LOG(INFO) << "command is " << buffer;
if (strcmp(command,"POWEROFF")==0) {
/* 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 (strcmp(command,"POWERON")==0) {
if (!start())
} else if (match_cmd(command, "POWERON", NULL)) {
if (!start()) {
sprintf(response,"RSP POWERON 1");
else
} else {
sprintf(response,"RSP POWERON 0");
}
else if (strcmp(command,"SETMAXDLY")==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(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
sscanf(params, "%d", &maxDelay);
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
}
else if (strcmp(command,"SETRXGAIN")==0) {
} 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(buffer,"%3s %s %d",cmdcheck,command,&newGain);
sscanf(params, "%d", &newGain);
newGain = mRadioInterface->setRxGain(newGain, chan);
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
}
else if (strcmp(command,"NOISELEV")==0) {
} else if (match_cmd(command, "NOISELEV", NULL)) {
if (mOn) {
float lev = mStates[chan].mNoiseLev;
sprintf(response,"RSP NOISELEV 0 %d",
@@ -767,26 +766,23 @@ void Transceiver::driveControl(size_t chan)
else {
sprintf(response,"RSP NOISELEV 1 0");
}
}
else if (!strcmp(command, "SETPOWER")) {
} else if (match_cmd(command, "SETPOWER", &params)) {
int power;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
sscanf(params, "%d", &power);
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP SETPOWER 0 %d", power);
}
else if (!strcmp(command,"ADJPOWER")) {
} else if (match_cmd(command, "ADJPOWER", &params)) {
int power, step;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
sscanf(params, "%d", &step);
power = mStates[chan].mPower + step;
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP ADJPOWER 0 %d", power);
}
else if (strcmp(command,"RXTUNE")==0) {
} else if (match_cmd(command, "RXTUNE", &params)) {
// tune receiver
int freqKhz;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
sscanf(params, "%d", &freqKhz);
mRxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
LOG(ALERT) << "RX failed to tune";
@@ -794,11 +790,10 @@ void Transceiver::driveControl(size_t chan)
}
else
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
}
else if (strcmp(command,"TXTUNE")==0) {
} else if (match_cmd(command, "TXTUNE", &params)) {
// tune txmtr
int freqKhz;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
sscanf(params, "%d", &freqKhz);
mTxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
LOG(ALERT) << "TX failed to tune";
@@ -806,37 +801,38 @@ void Transceiver::driveControl(size_t chan)
}
else
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
}
else if (!strcmp(command,"SETTSC")) {
} else if (match_cmd(command, "SETTSC", &params)) {
// set TSC
unsigned TSC;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
if (mOn || (TSC < 0) || (TSC > 7))
sscanf(params, "%u", &TSC);
if (TSC > 7) {
sprintf(response, "RSP SETTSC 1 %d", TSC);
else if (chan && (TSC != mTSC))
sprintf(response, "RSP SETTSC 1 %d", TSC);
else {
} else {
LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
mTSC = TSC;
generateMidamble(mSPSRx, TSC);
sprintf(response,"RSP SETTSC 0 %d", TSC);
}
}
else if (strcmp(command,"SETSLOT")==0) {
} else if (match_cmd(command, "SETSLOT", &params)) {
// set slot type
int corrCode;
int timeslot;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
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 {
} 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");
}
@@ -846,12 +842,20 @@ void Transceiver::driveControl(size_t chan)
bool Transceiver::driveTxPriorityQueue(size_t chan)
{
char buffer[gSlotLen+50];
int burstLen;
char buffer[EDGE_BURST_NBITS + 50];
// check data socket
size_t msgLen = mDataSockets[chan]->read(buffer);
size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
if (msgLen!=gSlotLen+1+4+1) {
if (msgLen == gSlotLen + 1 + 4 + 1) {
burstLen = gSlotLen;
} else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
if (mSPSTx != 4)
return false;
burstLen = EDGE_BURST_NBITS;
} else {
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
return false;
}
@@ -862,14 +866,14 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
int RSSI = (int) buffer[5];
static BitVector newBurst(gSlotLen);
BitVector newBurst(burstLen);
BitVector::iterator itr = newBurst.begin();
char *bufferItr = buffer+6;
while (itr < newBurst.end())
while (itr < newBurst.end())
*itr++ = *bufferItr++;
GSM::Time currTime = GSM::Time(frameNum,timeSlot);
addRadioVector(chan, newBurst, RSSI, currTime);
@@ -883,53 +887,79 @@ void Transceiver::driveReceiveRadio()
{
if (!mRadioInterface->driveReceiveRadio()) {
usleep(100000);
} else {
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
writeClockInterface();
} 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;
int RSSI;
int TOA; // in 1/256 of a symbol
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, TOA, chan);
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
if (!rxBurst)
return;
if (rxBurst) {
// Convert -1..+1 soft bits to 0..1 soft bits
vectorSlicer(rxBurst);
LOG(DEBUG) << "burst parameters: "
<< " time: " << burstTime
<< " RSSI: " << RSSI
<< " TOA: " << TOA
<< " bits: " << *rxBurst;
char burstString[gSlotLen+10];
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = RSSI;
burstString[6] = (TOA >> 8) & 0x0ff;
burstString[7] = TOA & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
/*
* EDGE demodulator returns 444 (148 * 3) bits
*/
if (rxBurst->size() == gSlotLen * 3)
nbits = gSlotLen * 3;
for (unsigned int i = 0; i < gSlotLen; i++) {
burstString[8+i] =(char) round((*burstItr++)*255.0);
}
burstString[gSlotLen+9] = '\0';
delete rxBurst;
dBm = RSSI + rssiOffset;
logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
mDataSockets[chan]->write(burstString,gSlotLen+10);
}
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
Features a carefully controlled latency mechanism, to
assure that transmit packets arrive at the radio/USRP
before they need to be transmitted.
@@ -940,7 +970,7 @@ void Transceiver::driveTxFIFO()
RadioClock *radioClock = (mRadioInterface->getClock());
if (mOn) {
//radioClock->wait(); // wait until clock updates
LOG(DEBUG) << "radio clock " << radioClock->get();
@@ -959,7 +989,7 @@ void Transceiver::driveTxFIFO()
else {
// if underrun hasn't occurred in the last sec (216 frames) drop
// transmit latency by a timeslot
if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
if (mTransmitLatency > mRadioInterface->minLatency()) {
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
mTransmitLatency.decTN();
LOG(INFO) << "reduced latency: " << mTransmitLatency;

View File

@@ -30,6 +30,10 @@
#include <sys/types.h>
#include <sys/socket.h>
extern "C" {
#include "config_defs.h"
}
class Transceiver;
/** Channel descriptor for transceiver object and channel number pair */
@@ -54,7 +58,7 @@ struct TransceiverState {
~TransceiverState();
/* Initialize a multiframe slot in the filler table */
bool init(int filler, size_t sps, float scale, size_t rtsc);
bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
int chanType[8];
@@ -89,24 +93,27 @@ struct TransceiverState {
/** The Transceiver class, responsible for physical layer of basestation */
class Transceiver {
public:
/** Transceiver constructor
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX manager, as a string
@param TRXAddress IP address of the TRX, as a string
@param GSMcoreAddress IP address of the GSM core, as a string
@param wSPS number of samples per GSM symbol
@param wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
Transceiver(int wBasePort,
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
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(int filler, size_t rtsc);
bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -141,23 +148,10 @@ public:
LOOPBACK ///< similar go VII, used in loopback testing
} ChannelCombination;
/** Codes for burst types of received bursts*/
typedef enum {
OFF, ///< timeslot is off
TSC, ///< timeslot should contain a normal burst
RACH, ///< timeslot should contain an access burst
IDLE ///< timeslot is an idle (or dummy) burst
} CorrType;
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
FILLER_RAND,
};
private:
int mBasePort;
std::string mAddr;
std::string mLocalAddr;
std::string mRemoteAddr;
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
@@ -174,13 +168,15 @@ private:
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
RadioInterface *mRadioInterface; ///< associated radioInterface object
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
double rssiOffset; ///< RSSI to dBm conversion offset
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
@@ -192,8 +188,9 @@ private:
void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI,
int &timingOffset, size_t chan = 0);
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);
@@ -204,30 +201,20 @@ private:
/** send messages over the clock socket */
void writeClockInterface(void);
/** Detect RACH bursts */
bool detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa);
/** Detect normal bursts */
bool detectTSC(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa, GSM::Time &time);
/** Demodulat burst and output soft bits */
SoftVector *demodulate(TransceiverState *state,
signalVector &burst, complex amp,
float toa, size_t tn, bool equalize);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
bool mOn; ///< flag to indicate that transceiver is powered on
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 mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
unsigned mMaxExpectedDelayAB; ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH)
unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
std::vector<TransceiverState> mStates;
@@ -273,6 +260,8 @@ protected:
/** 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 *);
@@ -286,4 +275,3 @@ void *ControlServiceLoopAdapter(TransceiverChannel *);
/** transmit queueing thread loop */
void *TxUpperLoopAdapter(TransceiverChannel *);

View File

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

View File

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

View File

@@ -25,25 +25,15 @@
#include "config.h"
#endif
void neon_convert_ps_si16_4n(short *, float *, float *, int);
void neon_convert_si16_ps_4n(float *, short *, int);
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
void neon_convert_si16_ps_4n(float *, const short *, int);
#ifndef HAVE_NEON
static void convert_si16_ps(float *out, short *in, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i];
void convert_init(void) {
}
static void convert_ps_si16(short *out, float *in, float scale, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i] * scale;
}
#else
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_si16_ps(float *restrict out,
short *restrict in,
static void neon_convert_si16_ps(float *out,
const short *in,
int len)
{
int start = len / 4 * 4;
@@ -55,9 +45,9 @@ static void neon_convert_si16_ps(float *restrict out,
}
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_ps_si16(short *restrict out,
float *restrict in,
float *restrict scale,
static void neon_convert_ps_si16(short *out,
const float *in,
const float *scale,
int len)
{
int start = len / 4 * 4;
@@ -67,9 +57,8 @@ static void neon_convert_ps_si16(short *restrict out,
for (int i = 0; i < len % 4; i++)
out[start + i] = (short) (in[start + i] * (*scale));
}
#endif
void convert_float_short(short *out, float *in, float scale, int len)
void convert_float_short(short *out, const float *in, float scale, int len)
{
#ifdef HAVE_NEON
float q[4] = { scale, scale, scale, scale };
@@ -79,11 +68,11 @@ void convert_float_short(short *out, float *in, float scale, int len)
else
neon_convert_ps_si16_4n(out, in, q, len >> 2);
#else
convert_ps_si16(out, in, scale, len);
base_convert_float_short(out, in, scale, len);
#endif
}
void convert_short_float(float *out, short *in, int len)
void convert_short_float(float *out, const short *in, int len)
{
#ifdef HAVE_NEON
if (len % 4)
@@ -91,6 +80,6 @@ void convert_short_float(float *out, short *in, int len)
else
neon_convert_si16_ps_4n(out, in, len >> 2);
#else
convert_si16_ps(out, in, len);
base_convert_short_float(out, in, len);
#endif
}

View File

@@ -58,6 +58,13 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
}
#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,

View File

@@ -0,0 +1,15 @@
AM_CFLAGS = -Wall -std=gnu99
noinst_LTLIBRARIES = libarch_common.la
noinst_HEADERS = \
convolve.h \
convert.h \
scale.h \
mult.h \
fft.h
libarch_common_la_SOURCES = \
convolve_base.c \
convert_base.c \
fft.c

View File

@@ -0,0 +1,15 @@
#ifndef _CONVERT_H_
#define _CONVERT_H_
void convert_float_short(short *out, const float *in, float scale, int len);
void convert_short_float(float *out, const short *in, int len);
void base_convert_float_short(short *out, const float *in,
float scale, int len);
void base_convert_short_float(float *out, const short *in, int len);
void convert_init(void);
#endif /* _CONVERT_H_ */

View File

@@ -0,0 +1,34 @@
/*
* Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "convert.h"
void base_convert_float_short(short *out, const float *in,
float scale, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i] * scale;
}
void base_convert_short_float(float *out, const short *in, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i];
}

View File

@@ -3,28 +3,30 @@
void *convolve_h_alloc(int num);
int convolve_real(float *x, int x_len,
float *h, int h_len,
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(float *x, int x_len,
float *h, int h_len,
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(float *x, int x_len,
float *h, int h_len,
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(float *x, int x_len,
float *h, int h_len,
int base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
void convolve_init(void);
#endif /* _CONVOLVE_H_ */

View File

@@ -26,21 +26,21 @@
#endif
/* Base multiply and accumulate complex-real */
static void mac_real(float *x, float *h, float *y)
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(float *x, float *h, float *y)
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(float *x, float *h, float *y,
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)
@@ -48,7 +48,7 @@ static void mac_real_vec_n(float *x, float *h, float *y,
}
/* Base vector complex-complex multiply and accumulate */
static void mac_cmplx_vec_n(float *x, float *h, float *y,
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)
@@ -56,8 +56,8 @@ static void mac_cmplx_vec_n(float *x, float *h, float *y,
}
/* Base complex-real convolution */
int _base_convolve_real(float *x, int x_len,
float *h, int h_len,
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)
@@ -73,8 +73,8 @@ int _base_convolve_real(float *x, int x_len,
}
/* Base complex-complex convolution */
int _base_convolve_complex(float *x, int x_len,
float *h, int h_len,
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
@@ -110,8 +110,8 @@ int bounds_check(int x_len, int h_len, int y_len,
}
/* API: Non-aligned (no SSE) complex-real */
int base_convolve_real(float *x, int x_len,
float *h, int h_len,
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)
@@ -128,8 +128,8 @@ int base_convolve_real(float *x, int x_len,
}
/* API: Non-aligned (no SSE) complex-complex */
int base_convolve_complex(float *x, int x_len,
float *h, int h_len,
int base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)

View File

@@ -0,0 +1,112 @@
/*
* Fast Fourier transform
*
* Copyright (C) 2012 Tom Tsou <tom@tsou.cc>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fftw3.h>
#include "fft.h"
struct fft_hdl {
float *fft_in;
float *fft_out;
int len;
fftwf_plan fft_plan;
};
/*! \brief Initialize FFT backend
* \param[in] reverse FFT direction
* \param[in] m FFT length
* \param[in] istride input stride count
* \param[in] ostride output stride count
* \param[in] in input buffer (FFTW aligned)
* \param[in] out output buffer (FFTW aligned)
* \param[in] ooffset initial offset into output buffer
*
* If the reverse is non-NULL, then an inverse FFT will be used. This is a
* wrapper for advanced non-contiguous FFTW usage. See FFTW documentation for
* further details.
*
* http://www.fftw.org/doc/Advanced-Complex-DFTs.html
*
* It is currently unknown how the offset of the output buffer affects FFTW
* memory alignment.
*/
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
float *in, float *out, int ooffset)
{
int rank = 1;
int n[] = { m };
int howmany = istride;
int idist = 1;
int odist = 1;
int *inembed = n;
int *onembed = n;
fftwf_complex *obuffer, *ibuffer;
struct fft_hdl *hdl = (struct fft_hdl *) malloc(sizeof(struct fft_hdl));
if (!hdl)
return NULL;
int direction = FFTW_FORWARD;
if (reverse)
direction = FFTW_BACKWARD;
ibuffer = (fftwf_complex *) in;
obuffer = (fftwf_complex *) out + ooffset;
hdl->fft_in = in;
hdl->fft_out = out;
hdl->fft_plan = fftwf_plan_many_dft(rank, n, howmany,
ibuffer, inembed, istride, idist,
obuffer, onembed, ostride, odist,
direction, FFTW_MEASURE);
return hdl;
}
void *fft_malloc(size_t size)
{
return fftwf_malloc(size);
}
void fft_free(void *ptr)
{
free(ptr);
}
/*! \brief Free FFT backend resources
*/
void free_fft(struct fft_hdl *hdl)
{
fftwf_destroy_plan(hdl->fft_plan);
free(hdl);
}
/*! \brief Run multiple DFT operations with the initialized plan
* \param[in] hdl handle to an intitialized fft struct
*
* Input and output buffers are configured with init_fft().
*/
int cxvec_fft(struct fft_hdl *hdl)
{
fftwf_execute(hdl->fft_plan);
return 0;
}

View File

@@ -0,0 +1,13 @@
#ifndef _FFT_H_
#define _FFT_H_
struct fft_hdl;
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
float *in, float *out, int ooffset);
void *fft_malloc(size_t size);
void fft_free(void *ptr);
void free_fft(struct fft_hdl *hdl);
int cxvec_fft(struct fft_hdl *hdl);
#endif /* _FFT_H_ */

View File

@@ -0,0 +1,33 @@
AM_CFLAGS = -Wall -std=gnu99 -I${srcdir}/../common
noinst_LTLIBRARIES = libarch.la
noinst_LTLIBRARIES += libarch_sse_3.la
noinst_LTLIBRARIES += libarch_sse_4_1.la
noinst_HEADERS = \
convert_sse_3.h \
convert_sse_4_1.h \
convolve_sse_3.h
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
# SSE 3 specific code
if HAVE_SSE3
libarch_sse_3_la_SOURCES = \
convert_sse_3.c \
convolve_sse_3.c
libarch_sse_3_la_CFLAGS = $(AM_CFLAGS) -msse3
libarch_la_LIBADD += libarch_sse_3.la
endif
# SSE 4.1 specific code
if HAVE_SSE4_1
libarch_sse_4_1_la_SOURCES = \
convert_sse_4_1.c
libarch_sse_4_1_la_CFLAGS = $(AM_CFLAGS) -msse4.1
libarch_la_LIBADD += libarch_sse_4_1.la
endif
libarch_la_SOURCES = \
convert.c \
convolve.c

View File

@@ -0,0 +1,83 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include "convert.h"
#include "convert_sse_3.h"
#include "convert_sse_4_1.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Architecture dependant function pointers */
struct convert_cpu_context {
void (*convert_si16_ps_16n) (float *, const short *, int);
void (*convert_si16_ps) (float *, const short *, int);
void (*convert_scale_ps_si16_16n)(short *, const float *, float, int);
void (*convert_scale_ps_si16_8n)(short *, const float *, float, int);
void (*convert_scale_ps_si16)(short *, const float *, float, int);
};
static struct convert_cpu_context c;
void convert_init(void)
{
c.convert_scale_ps_si16_16n = base_convert_float_short;
c.convert_scale_ps_si16_8n = base_convert_float_short;
c.convert_scale_ps_si16 = base_convert_float_short;
c.convert_si16_ps_16n = base_convert_short_float;
c.convert_si16_ps = base_convert_short_float;
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
#ifdef HAVE_SSE4_1
if (__builtin_cpu_supports("sse4.1")) {
c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n;
c.convert_si16_ps = &_sse_convert_si16_ps;
}
#endif
#ifdef HAVE_SSE3
if (__builtin_cpu_supports("sse3")) {
c.convert_scale_ps_si16_16n = _sse_convert_scale_ps_si16_16n;
c.convert_scale_ps_si16_8n = _sse_convert_scale_ps_si16_8n;
c.convert_scale_ps_si16 = _sse_convert_scale_ps_si16;
}
#endif
#endif
}
void convert_float_short(short *out, const float *in, float scale, int len)
{
if (!(len % 16))
c.convert_scale_ps_si16_16n(out, in, scale, len);
else if (!(len % 8))
c.convert_scale_ps_si16_8n(out, in, scale, len);
else
c.convert_scale_ps_si16(out, in, scale, len);
}
void convert_short_float(float *out, const short *in, int len)
{
if (!(len % 16))
c.convert_si16_ps_16n(out, in, len);
else
c.convert_si16_ps(out, in, len);
}

View File

@@ -0,0 +1,107 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include "convert_sse_3.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SSE3
#include <xmmintrin.h>
#include <emmintrin.h>
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
void _sse_convert_scale_ps_si16_8n(short *restrict out,
const float *restrict in,
float scale, int len)
{
__m128 m0, m1, m2;
__m128i m4, m5;
for (int i = 0; i < len / 8; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_ps(&in[8 * i + 0]);
m1 = _mm_loadu_ps(&in[8 * i + 4]);
m2 = _mm_load1_ps(&scale);
/* Scale */
m0 = _mm_mul_ps(m0, m2);
m1 = _mm_mul_ps(m1, m2);
/* Convert */
m4 = _mm_cvtps_epi32(m0);
m5 = _mm_cvtps_epi32(m1);
/* Pack and store */
m5 = _mm_packs_epi32(m4, m5);
_mm_storeu_si128((__m128i *) & out[8 * i], m5);
}
}
/* 8*N single precision floats scaled and converted with remainder */
void _sse_convert_scale_ps_si16(short *restrict out,
const float *restrict in, float scale, int len)
{
int start = len / 8 * 8;
_sse_convert_scale_ps_si16_8n(out, in, scale, len);
for (int i = 0; i < len % 8; i++)
out[start + i] = in[start + i] * scale;
}
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
void _sse_convert_scale_ps_si16_16n(short *restrict out,
const float *restrict in,
float scale, int len)
{
__m128 m0, m1, m2, m3, m4;
__m128i m5, m6, m7, m8;
for (int i = 0; i < len / 16; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_ps(&in[16 * i + 0]);
m1 = _mm_loadu_ps(&in[16 * i + 4]);
m2 = _mm_loadu_ps(&in[16 * i + 8]);
m3 = _mm_loadu_ps(&in[16 * i + 12]);
m4 = _mm_load1_ps(&scale);
/* Scale */
m0 = _mm_mul_ps(m0, m4);
m1 = _mm_mul_ps(m1, m4);
m2 = _mm_mul_ps(m2, m4);
m3 = _mm_mul_ps(m3, m4);
/* Convert */
m5 = _mm_cvtps_epi32(m0);
m6 = _mm_cvtps_epi32(m1);
m7 = _mm_cvtps_epi32(m2);
m8 = _mm_cvtps_epi32(m3);
/* Pack and store */
m5 = _mm_packs_epi32(m5, m6);
m7 = _mm_packs_epi32(m7, m8);
_mm_storeu_si128((__m128i *) & out[16 * i + 0], m5);
_mm_storeu_si128((__m128i *) & out[16 * i + 8], m7);
}
}
#endif

View File

@@ -0,0 +1,34 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
void _sse_convert_scale_ps_si16_8n(short *restrict out,
const float *restrict in,
float scale, int len);
/* 8*N single precision floats scaled and converted with remainder */
void _sse_convert_scale_ps_si16(short *restrict out,
const float *restrict in, float scale, int len);
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
void _sse_convert_scale_ps_si16_16n(short *restrict out,
const float *restrict in,
float scale, int len);

View File

@@ -0,0 +1,77 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include "convert_sse_4_1.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SSE4_1
#include <smmintrin.h>
/* 16*N 16-bit signed integer converted to single precision floats */
void _sse_convert_si16_ps_16n(float *restrict out,
const short *restrict in, int len)
{
__m128i m0, m1, m2, m3, m4, m5;
__m128 m6, m7, m8, m9;
for (int i = 0; i < len / 16; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_si128((__m128i *) & in[16 * i + 0]);
m1 = _mm_loadu_si128((__m128i *) & in[16 * i + 8]);
/* Unpack */
m2 = _mm_cvtepi16_epi32(m0);
m4 = _mm_cvtepi16_epi32(m1);
m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2));
m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2));
m3 = _mm_cvtepi16_epi32(m0);
m5 = _mm_cvtepi16_epi32(m1);
/* Convert */
m6 = _mm_cvtepi32_ps(m2);
m7 = _mm_cvtepi32_ps(m3);
m8 = _mm_cvtepi32_ps(m4);
m9 = _mm_cvtepi32_ps(m5);
/* Store */
_mm_storeu_ps(&out[16 * i + 0], m6);
_mm_storeu_ps(&out[16 * i + 4], m7);
_mm_storeu_ps(&out[16 * i + 8], m8);
_mm_storeu_ps(&out[16 * i + 12], m9);
}
}
/* 16*N 16-bit signed integer conversion with remainder */
void _sse_convert_si16_ps(float *restrict out,
const short *restrict in, int len)
{
int start = len / 16 * 16;
_sse_convert_si16_ps_16n(out, in, len);
for (int i = 0; i < len % 16; i++)
out[start + i] = in[start + i];
}
#endif

View File

@@ -0,0 +1,28 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
/* 16*N 16-bit signed integer converted to single precision floats */
void _sse_convert_si16_ps_16n(float *restrict out,
const short *restrict in, int len);
/* 16*N 16-bit signed integer conversion with remainder */
void _sse_convert_si16_ps(float *restrict out,
const short *restrict in, int len);

View File

@@ -0,0 +1,172 @@
/*
* SSE Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include "convolve.h"
#include "convolve_sse_3.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Architecture dependant function pointers */
struct convolve_cpu_context {
void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_cmplx_8n) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_cmplx) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_real4) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_real8) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_real12) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_real16) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_real20) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_real4n) (const float *, int, const float *, int, float *,
int, int, int, int, int);
void (*conv_real) (const float *, int, const float *, int, float *, int,
int, int, int, int);
};
static struct convolve_cpu_context c;
/* Forward declarations from base implementation */
int _base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int bounds_check(int x_len, int h_len, int y_len,
int start, int len, int step);
/* API: Initalize convolve module */
void convolve_init(void)
{
c.conv_cmplx_4n = (void *)_base_convolve_complex;
c.conv_cmplx_8n = (void *)_base_convolve_complex;
c.conv_cmplx = (void *)_base_convolve_complex;
c.conv_real4 = (void *)_base_convolve_real;
c.conv_real8 = (void *)_base_convolve_real;
c.conv_real12 = (void *)_base_convolve_real;
c.conv_real16 = (void *)_base_convolve_real;
c.conv_real20 = (void *)_base_convolve_real;
c.conv_real4n = (void *)_base_convolve_real;
c.conv_real = (void *)_base_convolve_real;
#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
if (__builtin_cpu_supports("sse3")) {
c.conv_cmplx_4n = sse_conv_cmplx_4n;
c.conv_cmplx_8n = sse_conv_cmplx_8n;
c.conv_real4 = sse_conv_real4;
c.conv_real8 = sse_conv_real8;
c.conv_real12 = sse_conv_real12;
c.conv_real16 = sse_conv_real16;
c.conv_real20 = sse_conv_real20;
c.conv_real4n = sse_conv_real4n;
}
#endif
}
/* API: Aligned complex-real */
int convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len, int start, int len, int step, int offset)
{
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
if (step <= 4) {
switch (h_len) {
case 4:
c.conv_real4(x, x_len, h, h_len, y, y_len, start, len,
step, offset);
break;
case 8:
c.conv_real8(x, x_len, h, h_len, y, y_len, start, len,
step, offset);
break;
case 12:
c.conv_real12(x, x_len, h, h_len, y, y_len, start, len,
step, offset);
break;
case 16:
c.conv_real16(x, x_len, h, h_len, y, y_len, start, len,
step, offset);
break;
case 20:
c.conv_real20(x, x_len, h, h_len, y, y_len, start, len,
step, offset);
break;
default:
if (!(h_len % 4))
c.conv_real4n(x, x_len, h, h_len, y, y_len,
start, len, step, offset);
else
c.conv_real(x, x_len, h, h_len, y, y_len, start,
len, step, offset);
}
} else
c.conv_real(x, x_len, h, h_len, y, y_len, start, len, step,
offset);
return len;
}
/* API: Aligned complex-complex */
int convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset)
{
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
if (step <= 4) {
if (!(h_len % 8))
c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start,
len, step, offset);
else if (!(h_len % 4))
c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start,
len, step, offset);
else
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len,
step, offset);
} else
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len, step,
offset);
return len;
}

View File

@@ -20,40 +20,31 @@
#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
/* 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_SSE3
#include <xmmintrin.h>
#include <pmmintrin.h>
/* 4-tap SSE complex-real convolution */
static void sse_conv_real4(float *restrict x,
float *restrict h,
float *restrict y,
int len)
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]);
@@ -61,8 +52,8 @@ static void sse_conv_real4(float *restrict x,
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]);
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));
@@ -81,13 +72,17 @@ static void sse_conv_real4(float *restrict x,
}
/* 8-tap SSE complex-real convolution */
static void sse_conv_real8(float *restrict x,
float *restrict h,
float *restrict y,
int len)
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]);
@@ -99,10 +94,10 @@ static void sse_conv_real8(float *restrict x,
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]);
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));
@@ -128,14 +123,18 @@ static void sse_conv_real8(float *restrict x,
}
/* 12-tap SSE complex-real convolution */
static void sse_conv_real12(float *restrict x,
float *restrict h,
float *restrict y,
int len)
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]);
@@ -150,18 +149,18 @@ static void sse_conv_real12(float *restrict x,
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]);
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]);
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));
@@ -175,8 +174,8 @@ static void sse_conv_real12(float *restrict x,
m5 = _mm_mul_ps(m9, m14);
/* Sum and store */
m8 = _mm_add_ps(m0, m2);
m9 = _mm_add_ps(m1, m3);
m8 = _mm_add_ps(m0, m2);
m9 = _mm_add_ps(m1, m3);
m10 = _mm_add_ps(m8, m4);
m11 = _mm_add_ps(m9, m5);
@@ -190,14 +189,18 @@ static void sse_conv_real12(float *restrict x,
}
/* 16-tap SSE complex-real convolution */
static void sse_conv_real16(float *restrict x,
float *restrict h,
float *restrict y,
int len)
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]);
@@ -216,23 +219,23 @@ static void sse_conv_real16(float *restrict x,
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]);
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]);
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));
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));
@@ -248,8 +251,8 @@ static void sse_conv_real16(float *restrict x,
m7 = _mm_mul_ps(m11, m15);
/* Sum and store */
m8 = _mm_add_ps(m0, m2);
m9 = _mm_add_ps(m1, m3);
m8 = _mm_add_ps(m0, m2);
m9 = _mm_add_ps(m1, m3);
m10 = _mm_add_ps(m4, m6);
m11 = _mm_add_ps(m5, m7);
@@ -265,14 +268,18 @@ static void sse_conv_real16(float *restrict x,
}
/* 20-tap SSE complex-real convolution */
static void sse_conv_real20(float *restrict x,
float *restrict h,
float *restrict y,
int len)
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]);
@@ -293,19 +300,19 @@ static void sse_conv_real20(float *restrict x,
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]);
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));
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);
@@ -314,16 +321,16 @@ static void sse_conv_real20(float *restrict x,
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);
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]);
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));
@@ -335,8 +342,8 @@ static void sse_conv_real20(float *restrict x,
m2 = _mm_mul_ps(m6, m15);
m3 = _mm_mul_ps(m7, m15);
m4 = _mm_add_ps(m0, m2);
m5 = _mm_add_ps(m1, m3);
m4 = _mm_add_ps(m0, m2);
m5 = _mm_add_ps(m1, m3);
/* Final sum and store */
m0 = _mm_add_ps(m8, m4);
@@ -351,10 +358,17 @@ static void sse_conv_real20(float *restrict x,
}
/* 4*N-tap SSE complex-real convolution */
static void sse_conv_real4n(float *x, float *h, float *y, int h_len, int len)
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();
@@ -367,8 +381,8 @@ static void sse_conv_real4n(float *x, float *h, float *y, int h_len, int len)
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]);
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));
@@ -391,10 +405,20 @@ static void sse_conv_real4n(float *x, float *h, float *y, int h_len, int len)
}
/* 4*N-tap SSE complex-complex convolution */
static void sse_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
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();
@@ -408,8 +432,8 @@ static void sse_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
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]);
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));
@@ -439,11 +463,18 @@ static void sse_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
}
/* 8*N-tap SSE complex-complex convolution */
static void sse_conv_cmplx_8n(float *x, float *h, float *y, int h_len, int len)
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();
@@ -464,13 +495,13 @@ static void sse_conv_cmplx_8n(float *x, float *h, float *y, int h_len, int len)
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]);
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));
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));
@@ -509,93 +540,3 @@ static void sse_conv_cmplx_8n(float *x, float *h, float *y, int h_len, int len)
}
}
#endif
/* 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;
void (*conv_func_n)(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_SSE3
if (step <= 4) {
switch (h_len) {
case 4:
conv_func = sse_conv_real4;
break;
case 8:
conv_func = sse_conv_real8;
break;
case 12:
conv_func = sse_conv_real12;
break;
case 16:
conv_func = sse_conv_real16;
break;
case 20:
conv_func = sse_conv_real20;
break;
default:
if (!(h_len % 4))
conv_func_n = sse_conv_real4n;
}
}
#endif
if (conv_func) {
conv_func(&x[2 * (-(h_len - 1) + start)],
h, y, len);
} else if (conv_func_n) {
conv_func_n(&x[2 * (-(h_len - 1) + start)],
h, y, h_len, len);
} else {
_base_convolve_real(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
}
return len;
}
/* API: Aligned complex-complex */
int convolve_complex(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_SSE3
if (step <= 4) {
if (!(h_len % 8))
conv_func = sse_conv_cmplx_8n;
else if (!(h_len % 4))
conv_func = sse_conv_cmplx_4n;
}
#endif
if (conv_func) {
conv_func(&x[2 * (-(h_len - 1) + start)],
h, y, h_len, len);
} else {
_base_convolve_complex(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
}
return len;
}

View File

@@ -0,0 +1,68 @@
/*
* SSE Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
/* 4-tap SSE complex-real convolution */
void sse_conv_real4(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
/* 8-tap SSE complex-real convolution */
void sse_conv_real8(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
/* 12-tap SSE complex-real convolution */
void sse_conv_real12(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
/* 16-tap SSE complex-real convolution */
void sse_conv_real16(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
/* 20-tap SSE complex-real convolution */
void sse_conv_real20(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
/* 4*N-tap SSE complex-real convolution */
void sse_conv_real4n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
/* 4*N-tap SSE complex-complex convolution */
void sse_conv_cmplx_4n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
/* 8*N-tap SSE complex-complex convolution */
void sse_conv_cmplx_8n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);

View File

@@ -1,7 +0,0 @@
#ifndef _CONVERT_H_
#define _CONVERT_H_
void convert_float_short(short *out, float *in, float scale, int len);
void convert_short_float(float *out, short *in, int len);
#endif /* _CONVERT_H_ */

View File

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

View File

@@ -18,11 +18,18 @@
#include <string>
#include <vector>
#include "GSMCommon.h"
extern "C" {
#include "config_defs.h"
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define GSMRATE 1625e3/6
#define GSMRATE (1625e3/6)
#define MCBTS_SPACING 800000.0
/** a 64-bit virtual timestamp for radio data */
typedef unsigned long long TIMESTAMP;
@@ -35,13 +42,20 @@ class RadioDevice {
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
/* Radio interface types */
enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, DIVERSITY };
enum InterfaceType {
NORMAL,
RESAMP_64M,
RESAMP_100M,
MULTI_ARFCN,
};
static RadioDevice *make(size_t sps, size_t chans = 1,
bool diversity = false, double offset = 0.0);
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 = "", bool extref = false)=0;
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
virtual ~RadioDevice() { }
@@ -124,6 +138,24 @@ class RadioDevice {
/** return minimum Tx Gain **/
virtual double minTxGain(void) = 0;
/** sets the RX path to use, returns true if successful and false otherwise */
virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0;
/** return the used RX path */
virtual std::string getRxAntenna(size_t chan = 0) = 0;
/** sets the RX path to use, returns true if successful and false otherwise */
virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0;
/** return the used RX path */
virtual std::string getTxAntenna(size_t chan = 0) = 0;
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual bool requiresRadioAlign() = 0;
/** Minimum latency that the device can achieve */
virtual GSM::Time minLatency() = 0;
/** Return internal status values */
virtual double getTxFreq(size_t chan = 0) = 0;
virtual double getRxFreq(size_t chan = 0) = 0;

View File

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

View File

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

View File

@@ -27,17 +27,16 @@
Compilation Flags
SWLOOPBACK compile for software loopback testing
*/
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "Logger.h"
#include "Threads.h"
#include "USRPDevice.h"
#include <Logger.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -59,7 +58,7 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t sps, size_t, bool)
USRPDevice::USRPDevice(size_t sps)
{
LOG(INFO) << "creating USRP device...";
@@ -81,7 +80,7 @@ USRPDevice::USRPDevice(size_t sps, size_t, bool)
else
pingOffset = 0;
#ifdef SWLOOPBACK
#ifdef SWLOOPBACK
samplePeriod = 1.0e6/actualSampleRate;
loopbackBufferSize = 0;
gettimeofday(&lastReadTime,NULL);
@@ -89,14 +88,14 @@ USRPDevice::USRPDevice(size_t sps, size_t, bool)
#endif
}
int USRPDevice::open(const std::string &, bool)
int USRPDevice::open(const std::string &, int, bool)
{
writeLock.unlock();
LOG(INFO) << "opening USRP device..";
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
string rbf = "std_inband.rbf";
//string rbf = "inband_1rxhb_1tx.rbf";
//string rbf = "inband_1rxhb_1tx.rbf";
m_uRx.reset();
if (!skipRx) {
try {
@@ -145,7 +144,7 @@ int USRPDevice::open(const std::string &, bool)
if (!skipRx) m_uRx->stop();
m_uTx->stop();
#endif
switch (dboardConfig) {
@@ -176,19 +175,19 @@ int USRPDevice::open(const std::string &, bool)
samplesRead = 0;
samplesWritten = 0;
started = false;
return NORMAL;
}
bool USRPDevice::start()
bool USRPDevice::start()
{
LOG(INFO) << "starting USRP...";
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uRx && !skipRx) return false;
if (!m_uTx) return false;
if (!skipRx) m_uRx->stop();
m_uTx->stop();
@@ -218,8 +217,8 @@ bool USRPDevice::start()
hi32Timestamp = 0;
isAligned = false;
if (!skipRx)
if (!skipRx)
started = (m_uRx->start() && m_uTx->start());
else
started = m_uTx->start();
@@ -230,14 +229,14 @@ bool USRPDevice::start()
#endif
}
bool USRPDevice::stop()
bool USRPDevice::stop()
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uRx) return false;
if (!m_uTx) return false;
delete[] currData;
started = !(m_uRx->stop() && m_uTx->stop());
return !started;
#else
@@ -258,7 +257,7 @@ double USRPDevice::minTxGain()
double USRPDevice::maxRxGain()
{
return m_dbRx->gain_max();
}
}
double USRPDevice::minRxGain()
{
@@ -314,28 +313,76 @@ double USRPDevice::setRxGain(double dB, size_t chan)
return dB;
}
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
{
if (chan >= rx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
return false;
}
LOG(ALERT) << "Not implemented";
return true;
}
std::string USRPDevice::getRxAntenna(size_t chan)
{
if (chan >= rx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
return "";
}
LOG(ALERT) << "Not implemented";
return "";
}
bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
{
if (chan >= tx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
return false;
}
LOG(ALERT) << "Not implemented";
return true;
}
std::string USRPDevice::getTxAntenna(size_t chan)
{
if (chan >= tx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
return "";
}
LOG(ALERT) << "Not implemented";
return "";
}
bool USRPDevice::requiresRadioAlign()
{
return true;
}
GSM::Time USRPDevice::minLatency() {
return GSM::Time(1,1);
}
// NOTE: Assumes sequential reads
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uRx)
return 0;
short *buf = bufs[0];
timestamp += timestampOffset;
if (timestamp + len < timeStart) {
memset(buf,0,len*2*sizeof(short));
return len;
}
if (underrun) *underrun = false;
uint32_t readBuf[2000];
while (1) {
//guestimate USB read size
int readLen=0;
@@ -345,7 +392,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
if (readLen > 8000) readLen= (8000/512)*512;
}
// read USRP packets, parse and save A/D data as needed
readLen = m_uRx->read((void *)readBuf,readLen,overrun);
for(int pktNum = 0; pktNum < (readLen/512); pktNum++) {
@@ -382,13 +429,13 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
continue;
}
if ((word0 >> 28) & 0x04) {
if (underrun) *underrun = true;
if (underrun) *underrun = true;
LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
}
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
if (!isAligned) continue;
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
while (cursorStart*2 > currDataSize) {
cursorStart -= currDataSize/2;
@@ -401,17 +448,17 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
else {
memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
}
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
}
}
}
}
// copy desired data to buf
unsigned bufStart = dataStart+(timestamp-timeStart);
if (bufStart + len < currDataSize/2) {
if (bufStart + len < currDataSize/2) {
LOG(DEBUG) << "bufStart: " << bufStart;
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
memset(data+bufStart*2,0,len*2*sizeof(short));
@@ -429,21 +476,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
timeStart = timestamp + len;
return len;
#else
if (loopbackBufferSize < 2) return 0;
int numSamples = 0;
struct timeval currTime;
gettimeofday(&currTime,NULL);
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
(currTime.tv_usec - lastReadTime.tv_usec);
if (timeElapsed < samplePeriod) {return 0;}
int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
if (numSamplesToRead < len) return 0;
if (numSamplesToRead > len) numSamplesToRead = len;
if (numSamplesToRead > loopbackBufferSize/2) {
firstRead =false;
firstRead =false;
numSamplesToRead = loopbackBufferSize/2;
}
memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
@@ -461,7 +508,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
firstRead = true;
}
samplesRead += numSamples;
return numSamples;
#endif
}
@@ -472,7 +519,7 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
{
writeLock.lock();
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uTx)
return 0;
@@ -519,14 +566,14 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
samplesWritten += retVal;
loopbackBufferSize += retVal*2;
return retVal;
#endif
}
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
short data[] = {0x00,0x02,0x00,0x00};
uint32_t *wordPtr = (uint32_t *) data;
*wordPtr = host_to_usrp_u32(*wordPtr);
@@ -543,7 +590,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
#endif
}
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
{
usrp_tune_result result;
@@ -600,7 +647,10 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity, double)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
{
return new USRPDevice(sps, chans, diversity);
return new USRPDevice(tx_sps);
}

View File

@@ -83,10 +83,10 @@ private:
double rxGain;
#ifdef SWLOOPBACK
#ifdef SWLOOPBACK
short loopbackBuffer[1000000];
int loopbackBufferSize;
double samplePeriod;
double samplePeriod;
struct timeval startTime;
struct timeval lastReadTime;
@@ -96,10 +96,10 @@ private:
public:
/** Object constructor */
USRPDevice(size_t sps, size_t chans = 1, bool diversity = false);
USRPDevice(size_t sps);
/** Instantiate the USRP */
int open(const std::string &, bool);
int open(const std::string &, int, bool);
/** Start the USRP */
bool start();
@@ -179,6 +179,24 @@ private:
/** return minimum Rx Gain **/
double minTxGain(void);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setRxAntenna(const std::string &ant, size_t chan = 0);
/* return the used RX path */
std::string getRxAntenna(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setTxAntenna(const std::string &ant, size_t chan = 0);
/* return the used RX path */
std::string getTxAntenna(size_t chan = 0);
/** return whether user drives synchronization of Tx/Rx of USRP */
bool requiresRadioAlign();
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency();
/** Return internal status values */
inline double getTxFreq(size_t chan = 0) { return 0; }
inline double getRxFreq(size_t chan = 0) { return 0; }
@@ -186,7 +204,7 @@ private:
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten; }
std::vector<std::string> tx_paths, rx_paths;
};
#endif // _USRP_DEVICE_H_

View File

@@ -27,168 +27,49 @@
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <GSMCommon.h>
#include <Logger.h>
#include <Configuration.h>
/* Samples-per-symbol for downlink path
* 4 - Uses precision modulator (more computation, less distortion)
* 1 - Uses minimized modulator (less computation, more distortion)
*
* Other values are invalid. Receive path (uplink) is always
* downsampled to 1 sps. Default to 4 sps for all cases except for
* ARM and non-SIMD enabled architectures.
*/
#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
#define DEFAULT_SPS 1
#else
#define DEFAULT_SPS 4
#endif
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/stats.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/misc.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/ctrl/ports.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/vty/stats.h>
#include "convolve.h"
#include "convert.h"
#include "trx_vty.h"
#include "debug.h"
}
/* Default configuration parameters
* Note that these values are only used if the particular key does not
* exist in the configuration database. IP port and address values will
* typically be overwritten by the OpenBTS.db values. Other values will
* not be in the database by default.
*/
#define DEFAULT_TRX_PORT 5700
#define DEFAULT_TRX_IP "127.0.0.1"
#define DEFAULT_EXTREF false
#define DEFAULT_DIVERSITY false
#define DEFAULT_CHANS 1
#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
struct trx_config {
std::string log_level;
std::string addr;
std::string dev_args;
unsigned port;
unsigned sps;
unsigned chans;
unsigned rtsc;
bool extref;
Transceiver::FillerType filler;
bool diversity;
double offset;
};
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
ConfigurationTable gConfig;
static char* config_file = (char*)DEFAULT_CONFIG_FILE;
volatile bool gshutdown = false;
/* Run sanity check on configuration table
* The global table constructor cannot provide notification in the
* event of failure. Make sure that we can access the database,
* write to it, and that it contains the bare minimum required keys.
*/
bool testConfig()
{
int val = 9999;
std::string test = "asldfkjsaldkf";
const char *key = "Log.Level";
static void *tall_trx_ctx;
static struct trx_ctx *g_trx_ctx;
static struct ctrl_handle *g_ctrlh;
/* Attempt to query */
try {
gConfig.getStr(key);
} catch (...) {
std::cerr << std::endl;
std::cerr << "Config: Failed query required key " << key
<< std::endl;
return false;
}
/* Attempt to set a test value in the global config */
if (!gConfig.set(test, val)) {
std::cerr << std::endl;
std::cerr << "Config: Failed to set test key" << std::endl;
return false;
} else {
gConfig.remove(test);
}
return true;
}
/* Setup configuration values
* Don't query the existence of the Log.Level because it's a
* mandatory value. That is, if it doesn't exist, the configuration
* table will crash or will have already crashed. Everything else we
* can survive without and use default values if the database entries
* are empty.
*/
bool trx_setup_config(struct trx_config *config)
{
std::string refstr, fillstr, divstr;
if (!testConfig())
return false;
if (config->log_level == "")
config->log_level = gConfig.getStr("Log.Level");
if (!config->port) {
if (gConfig.defines("TRX.Port"))
config->port = gConfig.getNum("TRX.Port");
else
config->port = DEFAULT_TRX_PORT;
}
if (config->addr == "") {
if (gConfig.defines("TRX.IP"))
config->addr = gConfig.getStr("TRX.IP");
else
config->addr = DEFAULT_TRX_IP;
}
if (!config->extref) {
if (gConfig.defines("TRX.Reference"))
config->extref = gConfig.getNum("TRX.Reference");
else
config->extref = DEFAULT_EXTREF;
}
if (!config->diversity) {
if (gConfig.defines("TRX.Diversity"))
config->diversity = gConfig.getNum("TRX.Diversity");
else
config->diversity = DEFAULT_DIVERSITY;
}
/* Diversity only supported on 2 channels */
if (config->diversity)
config->chans = 2;
refstr = config->extref ? "Enabled" : "Disabled";
divstr = config->diversity ? "Enabled" : "Disabled";
switch (config->filler) {
case Transceiver::FILLER_DUMMY:
fillstr = "Dummy bursts";
break;
case Transceiver::FILLER_ZERO:
fillstr = "Disabled";
break;
case Transceiver::FILLER_RAND:
fillstr = "Normal busrts with random payload";
break;
}
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Log Level............... " << config->log_level << std::endl;
ost << " Device args............. " << config->dev_args << std::endl;
ost << " TRX Base Port........... " << config->port << std::endl;
ost << " TRX Address............. " << config->addr << std::endl;
ost << " Channels................ " << config->chans << std::endl;
ost << " Samples-per-Symbol...... " << config->sps << std::endl;
ost << " External Reference...... " << refstr << std::endl;
ost << " C0 Filler Table......... " << fillstr << std::endl;
ost << " Diversity............... " << divstr << std::endl;
ost << " Tuning offset........... " << config->offset << std::endl;
std::cout << ost << std::endl;
return true;
}
static RadioDevice *usrp;
static RadioInterface *radio;
static Transceiver *transceiver;
/* Create radio interface
* The interface consists of sample rate changes, frequency shifts,
@@ -197,23 +78,24 @@ bool trx_setup_config(struct trx_config *config)
* The radio interface connects the main transceiver with the device
* object, which may be operating some other rate.
*/
RadioInterface *makeRadioInterface(struct trx_config *config,
RadioInterface *makeRadioInterface(struct trx_ctx *trx,
RadioDevice *usrp, int type)
{
RadioInterface *radio = NULL;
switch (type) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, config->sps, config->chans);
radio = new RadioInterface(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp,
config->sps, config->chans);
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps);
break;
case RadioDevice::DIVERSITY:
radio = new RadioInterfaceDiversity(usrp,
config->sps, config->chans);
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";
@@ -234,194 +116,458 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
* and decoding schemes. Also included are the socket interfaces for
* connecting to the upper layer stack.
*/
Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
{
Transceiver *trx;
VectorFIFO *fifo;
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
config->chans, GSM::Time(3,0), radio);
if (!trx->init(config->filler, config->rtsc)) {
transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr,
trx->cfg.remote_addr, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0),
radio, trx->cfg.rssi_offset);
if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc,
trx->cfg.rach_delay, trx->cfg.egprs)) {
LOG(ALERT) << "Failed to initialize transceiver";
delete trx;
return NULL;
return -1;
}
for (size_t i = 0; i < config->chans; i++) {
for (size_t i = 0; i < trx->cfg.num_chans; i++) {
fifo = radio->receiveFIFO(i);
if (fifo && trx->receiveFIFO(fifo, i))
if (fifo && transceiver->receiveFIFO(fifo, i))
continue;
LOG(ALERT) << "Could not attach FIFO to channel " << i;
delete trx;
return NULL;
return -1;
}
return trx;
return 0;
}
static void sig_handler(int signo)
{
fprintf(stdout, "Received shutdown signal");
gshutdown = true;
fprintf(stdout, "signal %d received\n", signo);
switch (signo) {
case SIGINT:
case SIGTERM:
fprintf(stdout, "shutting down\n");
gshutdown = true;
break;
case SIGABRT:
case SIGUSR1:
talloc_report(tall_trx_ctx, stderr);
talloc_report_full(tall_trx_ctx, stderr);
break;
case SIGUSR2:
talloc_report_full(tall_trx_ctx, stderr);
break;
default:
break;
}
}
static void setup_signal_handlers()
{
if (signal(SIGINT, sig_handler) == SIG_ERR) {
fprintf(stderr, "Failed to install SIGINT signal handler\n");
exit(EXIT_FAILURE);
}
if (signal(SIGTERM, sig_handler) == SIG_ERR) {
fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
exit( EXIT_FAILURE);
/* Handle keyboard interrupt SIGINT */
signal(SIGINT, &sig_handler);
signal(SIGTERM, &sig_handler);
signal(SIGABRT, &sig_handler);
signal(SIGUSR1, &sig_handler);
signal(SIGUSR2, &sig_handler);
osmo_init_ignore_signals();
}
static std::vector<std::string> comma_delimited_to_vector(char* opt)
{
std::string str = std::string(opt);
std::vector<std::string> result;
std::stringstream ss(str);
while( ss.good() )
{
std::string substr;
getline(ss, substr, ',');
result.push_back(substr);
}
return result;
}
static void print_help()
{
fprintf(stdout, "Options:\n"
" -h This text\n"
" -a UHD device args\n"
" -l Logging level (%s)\n"
" -i IP address of GSM core\n"
" -p Base port number\n"
" -d Enable dual channel diversity receiver\n"
" -x Enable external 10 MHz reference\n"
" -s Samples-per-symbol (1 or 4)\n"
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -o Set baseband frequency offset (default=auto)\n"
" -r Random burst test mode with TSC\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
" -C Filename The config file to use\n"
);
}
static void handle_options(int argc, char **argv, struct trx_config *config)
static void print_deprecated(char opt)
{
LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
<< " Please use VTY cfg option instead."
<< " All cmd line options are already being overriden by VTY options if set.";
}
static void handle_options(int argc, char **argv, struct trx_ctx* trx)
{
int option;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
bool rx_paths_set = false, tx_paths_set = false;
config->port = 0;
config->sps = DEFAULT_SPS;
config->chans = DEFAULT_CHANS;
config->rtsc = 0;
config->extref = false;
config->filler = Transceiver::FILLER_ZERO;
config->diversity = false;
config->offset = 0.0;
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:")) != -1) {
while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:")) != -1) {
switch (option) {
case 'h':
print_help();
exit(0);
break;
case 'a':
config->dev_args = optarg;
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
break;
case 'l':
config->log_level = optarg;
print_deprecated(option);
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'i':
config->addr = optarg;
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg);
break;
case 'j':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg);
break;
case 'p':
config->port = atoi(optarg);
print_deprecated(option);
trx->cfg.base_port = atoi(optarg);
break;
case 'c':
config->chans = atoi(optarg);
print_deprecated(option);
trx->cfg.num_chans = atoi(optarg);
break;
case 'd':
config->diversity = true;
case 'm':
print_deprecated(option);
trx->cfg.multi_arfcn = true;
break;
case 'x':
config->extref = true;
print_deprecated(option);
trx->cfg.clock_ref = REF_EXTERNAL;
break;
case 'g':
print_deprecated(option);
trx->cfg.clock_ref = REF_GPS;
break;
case 'f':
config->filler = Transceiver::FILLER_DUMMY;
print_deprecated(option);
trx->cfg.filler = FILLER_DUMMY;
break;
case 'o':
config->offset = atof(optarg);
print_deprecated(option);
trx->cfg.offset = atof(optarg);
break;
case 's':
config->sps = atoi(optarg);
print_deprecated(option);
trx->cfg.tx_sps = atoi(optarg);
break;
case 'b':
print_deprecated(option);
trx->cfg.rx_sps = atoi(optarg);
break;
case 'r':
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_RAND;
print_deprecated(option);
trx->cfg.rtsc_set = true;
trx->cfg.rtsc = atoi(optarg);
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
trx->cfg.filler = FILLER_NORM_RAND;
break;
case 'A':
print_deprecated(option);
trx->cfg.rach_delay_set = true;
trx->cfg.rach_delay = atoi(optarg);
trx->cfg.filler = FILLER_ACCESS_RAND;
break;
case 'R':
print_deprecated(option);
trx->cfg.rssi_offset = atof(optarg);
break;
case 'S':
print_deprecated(option);
trx->cfg.swap_channels = true;
break;
case 'e':
print_deprecated(option);
trx->cfg.egprs = true;
break;
case 't':
print_deprecated(option);
trx->cfg.sched_rr = atoi(optarg);
break;
case 'y':
print_deprecated(option);
tx_paths = comma_delimited_to_vector(optarg);
tx_paths_set = true;
break;
case 'z':
print_deprecated(option);
rx_paths = comma_delimited_to_vector(optarg);
rx_paths_set = true;
break;
case 'C':
config_file = optarg;
break;
default:
print_help();
exit(0);
goto bad_config;
}
}
if ((config->sps != 1) && (config->sps != 4)) {
printf("Unsupported samples-per-symbol %i\n\n", config->sps);
print_help();
exit(0);
/* 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());
}
if (config->rtsc > 7) {
printf("Invalid training sequence %i\n\n", config->rtsc);
print_help();
exit(0);
}
return;
bad_config:
print_help();
exit(0);
}
int main(int argc, char *argv[])
int trx_validate_config(struct trx_ctx *trx)
{
int type, chans;
RadioDevice *usrp;
RadioInterface *radio = NULL;
Transceiver *trx = NULL;
struct trx_config config;
handle_options(argc, argv, &config);
setup_signal_handlers();
/* Check database sanity */
if (!trx_setup_config(&config)) {
std::cerr << "Config: Database failure - exiting" << std::endl;
return EXIT_FAILURE;
if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) {
LOG(ERROR) << "Unsupported number of channels";
return -1;
}
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
if ((trx->cfg.egprs || trx->cfg.multi_arfcn) &&
(trx->cfg.tx_sps!=4 || trx->cfg.tx_sps!=4)) {
LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
return -1;
}
srandom(time(NULL));
return 0;
}
static int set_sched_rr(unsigned int prio)
{
struct sched_param param;
int rc;
memset(&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 */
usrp = RadioDevice::make(config.sps, config.chans,
config.diversity, config.offset);
type = usrp->open(config.dev_args, config.extref);
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(&config, usrp, type);
radio = makeRadioInterface(trx, usrp, type);
if (!radio)
goto shutdown;
/* Create the transceiver core */
trx = makeTransceiver(&config, radio);
if (!trx)
if (makeTransceiver(trx, radio) < 0)
goto shutdown;
chans = trx->numChans();
chans = transceiver->numChans();
std::cout << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl;
while (!gshutdown)
sleep(1);
return 0;
shutdown:
std::cout << "Shutting down transceiver..." << std::endl;
trx_stop();
return -1;
}
delete trx;
delete radio;
delete usrp;
int main(int argc, char *argv[])
{
int rc;
tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
msgb_talloc_ctx_init(tall_trx_ctx, 0);
g_vty_info.tall_ctx = tall_trx_ctx;
setup_signal_handlers();
g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
#ifdef HAVE_SSE3
printf("Info: SSE3 support compiled in");
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (__builtin_cpu_supports("sse3"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#else
printf(", but runtime SIMD detection disabled\n");
#endif
#endif
#ifdef HAVE_SSE4_1
printf("Info: SSE4.1 support compiled in");
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (__builtin_cpu_supports("sse4.1"))
printf(" and supported by CPU\n");
else
printf(", but not supported by CPU\n");
#else
printf(", but runtime SIMD detection disabled\n");
#endif
#endif
convolve_init();
convert_init();
osmo_init_logging2(tall_trx_ctx, &log_info);
osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info);
ctrl_vty_init(tall_trx_ctx);
trx_vty_init(g_trx_ctx);
logging_vty_add_cmds();
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();
handle_options(argc, argv, g_trx_ctx);
rate_ctr_init(tall_trx_ctx);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
exit(2);
}
rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
if (rc < 0)
exit(1);
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
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;
}

View File

@@ -0,0 +1,228 @@
/*
* Segmented Ring Buffer
*
* Copyright (C) 2015 Ettus Research LLC
*
* Author: Tom Tsou <tom@tsou.cc>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <string.h>
#include <iostream>
#include "radioBuffer.h"
RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
size_t hLen, bool outDirection)
: writeIndex(0), readIndex(0), availSamples(0)
{
if (!outDirection)
hLen = 0;
buffer = new float[2 * (hLen + numSegments * segmentLen)];
bufferLen = numSegments * segmentLen;
segments.resize(numSegments);
for (size_t i = 0; i < numSegments; i++)
segments[i] = &buffer[2 * (hLen + i * segmentLen)];
this->outDirection = outDirection;
this->numSegments = numSegments;
this->segmentLen = segmentLen;
this->hLen = hLen;
}
RadioBuffer::~RadioBuffer()
{
delete[] buffer;
}
void RadioBuffer::reset()
{
writeIndex = 0;
readIndex = 0;
availSamples = 0;
}
/*
* Output direction
*
* Return a pointer to the oldest segment or NULL if a complete segment is not
* available.
*/
const float *RadioBuffer::getReadSegment()
{
if (!outDirection) {
std::cout << "Invalid direction" << std::endl;
return NULL;
}
if (availSamples < segmentLen) {
std::cout << "Not enough samples " << std::endl;
std::cout << availSamples << " available per segment "
<< segmentLen << std::endl;
return NULL;
}
size_t num = readIndex / segmentLen;
if (num >= numSegments) {
std::cout << "Invalid segment" << std::endl;
return NULL;
} else if (!num) {
memcpy(buffer,
&buffer[2 * bufferLen],
hLen * 2 * sizeof(float));
}
availSamples -= segmentLen;
readIndex = (readIndex + segmentLen) % bufferLen;
return segments[num];
}
/*
* Output direction
*
* Write a non-segment length of samples to the buffer.
*/
bool RadioBuffer::write(const float *wr, size_t len)
{
if (!outDirection) {
std::cout << "Invalid direction" << std::endl;
return false;
}
if (availSamples + len > bufferLen) {
std::cout << "Insufficient space" << std::endl;
std::cout << bufferLen - availSamples << " available per write "
<< len << std::endl;
return false;
}
if (writeIndex + len <= bufferLen) {
memcpy(&buffer[2 * (writeIndex + hLen)],
wr, len * 2 * sizeof(float));
} else {
size_t len0 = bufferLen - writeIndex;
size_t len1 = len - len0;
memcpy(&buffer[2 * (writeIndex + hLen)], wr, len0 * 2 * sizeof(float));
memcpy(&buffer[2 * hLen], &wr[2 * len0], len1 * 2 * sizeof(float));
}
availSamples += len;
writeIndex = (writeIndex + len) % bufferLen;
return true;
}
bool RadioBuffer::zero(size_t len)
{
if (!outDirection) {
std::cout << "Invalid direction" << std::endl;
return false;
}
if (availSamples + len > bufferLen) {
std::cout << "Insufficient space" << std::endl;
std::cout << bufferLen - availSamples << " available per zero "
<< len << std::endl;
return false;
}
if (writeIndex + len <= bufferLen) {
memset(&buffer[2 * (writeIndex + hLen)],
0, len * 2 * sizeof(float));
} else {
size_t len0 = bufferLen - writeIndex;
size_t len1 = len - len0;
memset(&buffer[2 * (writeIndex + hLen)], 0, len0 * 2 * sizeof(float));
memset(&buffer[2 * hLen], 0, len1 * 2 * sizeof(float));
}
availSamples += len;
writeIndex = (writeIndex + len) % bufferLen;
return true;
}
/*
* Input direction
*/
float *RadioBuffer::getWriteSegment()
{
if (outDirection) {
std::cout << "Invalid direction" << std::endl;
return NULL;
}
if (bufferLen - availSamples < segmentLen) {
std::cout << "Insufficient samples" << std::endl;
std::cout << bufferLen - availSamples
<< " available for segment " << segmentLen
<< std::endl;
return NULL;
}
if (writeIndex % segmentLen) {
std::cout << "Internal segment error" << std::endl;
return NULL;
}
size_t num = writeIndex / segmentLen;
if (num >= numSegments)
return NULL;
availSamples += segmentLen;
writeIndex = (writeIndex + segmentLen) % bufferLen;
return segments[num];
}
bool RadioBuffer::zeroWriteSegment()
{
float *segment = getWriteSegment();
if (!segment)
return false;
memset(segment, 0, segmentLen * 2 * sizeof(float));
return true;
}
bool RadioBuffer::read(float *rd, size_t len)
{
if (outDirection) {
std::cout << "Invalid direction" << std::endl;
return false;
}
if (availSamples < len) {
std::cout << "Insufficient samples" << std::endl;
std::cout << availSamples << " available for "
<< len << std::endl;
return false;
}
if (readIndex + len <= bufferLen) {
memcpy(rd, &buffer[2 * readIndex], len * 2 * sizeof(float));
} else {
size_t len0 = bufferLen - readIndex;
size_t len1 = len - len0;
memcpy(rd, &buffer[2 * readIndex], len0 * 2 * sizeof(float));
memcpy(&rd[2 * len0], buffer, len1 * 2 * sizeof(float));
}
availSamples -= len;
readIndex = (readIndex + len) % bufferLen;
return true;
}

View File

@@ -0,0 +1,45 @@
#include <stdlib.h>
#include <stddef.h>
#include <vector>
class RadioBuffer {
public:
RadioBuffer(size_t numSegments, size_t segmentLen,
size_t hLen, bool outDirection);
~RadioBuffer();
const size_t getSegmentLen() { return segmentLen; }
const size_t getNumSegments() { return numSegments; }
const size_t getAvailSamples() { return availSamples; }
const size_t getAvailSegments() { return availSamples / segmentLen; }
const size_t getFreeSamples()
{
return bufferLen - availSamples;
}
const size_t getFreeSegments()
{
return getFreeSamples() / segmentLen;
}
void reset();
/* Output direction */
const float *getReadSegment();
bool write(const float *wr, size_t len);
bool zero(size_t len);
/* Input direction */
float *getWriteSegment();
bool zeroWriteSegment();
bool read(float *rd, size_t len);
private:
size_t writeIndex, readIndex, availSamples;
size_t bufferLen, numSegments, segmentLen, hLen;
float *buffer;
std::vector<float *> segments;
bool outDirection;
};

View File

@@ -1,26 +1,23 @@
/*
* 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/>.
*/
* 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"
@@ -33,12 +30,11 @@ extern "C" {
#define CHUNK 625
#define NUMCHUNKS 4
RadioInterface::RadioInterface(RadioDevice *wRadio,
size_t sps, size_t chans, size_t diversity,
RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
size_t rx_sps, size_t chans,
int wReceiveOffset, GSM::Time wStartTime)
: mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
sendCursor(0), recvCursor(0), underrun(false), overrun(false),
receiveOffset(wReceiveOffset), mOn(false)
: mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
underrun(false), overrun(false), receiveOffset(wReceiveOffset), mOn(false)
{
mClock.set(wStartTime);
}
@@ -50,7 +46,7 @@ RadioInterface::~RadioInterface(void)
bool RadioInterface::init(int type)
{
if ((type != RadioDevice::NORMAL) || (mMIMO > 1) || !mChans) {
if ((type != RadioDevice::NORMAL) || !mChans) {
LOG(ALERT) << "Invalid configuration";
return false;
}
@@ -65,33 +61,20 @@ bool RadioInterface::init(int type)
powerScaling.resize(mChans);
for (size_t i = 0; i < mChans; i++) {
sendBuffer[i] = new signalVector(CHUNK * mSPSTx);
recvBuffer[i] = new signalVector(NUMCHUNKS * CHUNK * mSPSRx);
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
convertSendBuffer[i] = new short[sendBuffer[i]->size() * 2];
convertRecvBuffer[i] = new short[recvBuffer[i]->size() * 2];
convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
powerScaling[i] = 1.0;
}
sendCursor = 0;
recvCursor = 0;
return true;
}
void RadioInterface::close()
{
for (size_t i = 0; i < sendBuffer.size(); i++)
delete sendBuffer[i];
for (size_t i = 0; i < recvBuffer.size(); i++)
delete recvBuffer[i];
for (size_t i = 0; i < convertSendBuffer.size(); i++)
delete convertSendBuffer[i];
for (size_t i = 0; i < convertRecvBuffer.size(); i++)
delete convertRecvBuffer[i];
sendBuffer.resize(0);
recvBuffer.resize(0);
convertSendBuffer.resize(0);
@@ -130,35 +113,26 @@ int RadioInterface::setPowerAttenuation(int atten, size_t chan)
}
int RadioInterface::radioifyVector(signalVector &wVector,
float *retVector,
bool zero)
size_t chan, bool zero)
{
if (zero) {
memset(retVector, 0, wVector.size() * 2 * sizeof(float));
return wVector.size();
}
memcpy(retVector, wVector.begin(), wVector.size() * 2 * sizeof(float));
if (zero)
sendBuffer[chan]->zero(wVector.size());
else
sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
return wVector.size();
}
int RadioInterface::unRadioifyVector(float *floatVector,
signalVector& newVector)
int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
{
signalVector::iterator itr = newVector.begin();
if (newVector.size() > recvCursor) {
if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
LOG(ALERT) << "Insufficient number of samples in receive buffer";
return -1;
}
for (size_t i = 0; i < newVector.size(); i++) {
*itr++ = Complex<float>(floatVector[2 * i + 0],
floatVector[2 * i + 1]);
}
recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
return newVector.size();
return newVector->size();
}
bool RadioInterface::tuneTx(double freq, size_t chan)
@@ -171,22 +145,39 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
return mRadio->setRxFreq(freq, chan);
}
/** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
while (1) {
sleep(60);
radioInterface->alignRadio();
pthread_testcancel();
}
return NULL;
}
void RadioInterface::alignRadio() {
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
}
bool RadioInterface::start()
{
if (mOn)
return true;
LOG(INFO) << "Starting radio device";
#ifdef USRP1
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
if (mRadio->requiresRadioAlign())
mAlignRadioServiceLoopThread.start(
(void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
if (!mRadio->start())
return false;
recvCursor = 0;
sendCursor = 0;
for (size_t i = 0; i < mChans; i++) {
sendBuffer[i]->reset();
recvBuffer[i]->reset();
}
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
@@ -215,36 +206,16 @@ bool RadioInterface::stop()
return true;
}
#ifdef USRP1
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
while (1) {
radioInterface->alignRadio();
pthread_testcancel();
}
return NULL;
}
void RadioInterface::alignRadio() {
sleep(60);
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
}
#endif
void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros)
{
if (!mOn)
return;
for (size_t i = 0; i < mChans; i++) {
radioifyVector(*bursts[i],
(float *) (sendBuffer[i]->begin() + sendCursor), zeros[i]);
}
for (size_t i = 0; i < mChans; i++)
radioifyVector(*bursts[i], i, zeros[i]);
sendCursor += bursts[0]->size();
pushBuffer();
while (pushBuffer());
}
bool RadioInterface::driveReceiveRadio()
@@ -259,10 +230,14 @@ bool RadioInterface::driveReceiveRadio()
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN();
int recvSz = recvCursor;
int readSz = 0;
int recvSz = recvBuffer[0]->getAvailSamples();
const int symbolsPerSlot = gSlotLen + 8;
int burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
int burstSize;
if (mSPSRx == 4)
burstSize = 625;
else
burstSize = symbolsPerSlot + (tN % 4 == 0);
/*
* Pre-allocate head room for the largest correlation size
@@ -276,13 +251,8 @@ bool RadioInterface::driveReceiveRadio()
*/
while (recvSz > burstSize) {
for (size_t i = 0; i < mChans; i++) {
burst = new radioVector(rcvClock, burstSize, head, mMIMO);
for (size_t n = 0; n < mMIMO; n++) {
unRadioifyVector((float *)
(recvBuffer[mMIMO * i + n]->begin() + readSz),
*burst->getVector(n));
}
burst = new radioVector(rcvClock, burstSize, head);
unRadioifyVector(burst->getVector(), i);
if (mReceiveFIFO[i].size() < 32)
mReceiveFIFO[i].write(burst);
@@ -292,22 +262,12 @@ bool RadioInterface::driveReceiveRadio()
mClock.incTN();
rcvClock.incTN();
readSz += burstSize;
recvSz -= burstSize;
tN = rcvClock.TN();
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
}
if (readSz > 0) {
for (size_t i = 0; i < recvBuffer.size(); i++) {
memmove(recvBuffer[i]->begin(),
recvBuffer[i]->begin() + readSz,
(recvCursor - readSz) * 2 * sizeof(float));
}
recvCursor -= readSz;
if (mSPSRx != 4)
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
}
return true;
@@ -331,74 +291,66 @@ VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
double RadioInterface::setRxGain(double dB, size_t chan)
{
if (mRadio)
return mRadio->setRxGain(dB, chan);
else
return -1;
return mRadio->setRxGain(dB, chan);
}
double RadioInterface::getRxGain(size_t chan)
{
if (mRadio)
return mRadio->getRxGain(chan);
else
return -1;
return mRadio->getRxGain(chan);
}
/* Receive a timestamped chunk from the device */
void RadioInterface::pullBuffer()
{
bool local_underrun;
int num_recv;
float *output;
size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
if (recvCursor > recvBuffer[0]->size() - CHUNK)
if (recvBuffer[0]->getFreeSegments() <= 0)
return;
/* Outer buffer access size is fixed */
num_recv = mRadio->readSamples(convertRecvBuffer,
CHUNK,
&overrun,
readTimestamp,
&local_underrun);
if (num_recv != CHUNK) {
LOG(ALERT) << "Receive error " << num_recv;
numRecv = mRadio->readSamples(convertRecvBuffer,
segmentLen,
&overrun,
readTimestamp,
&local_underrun);
if (numRecv != segmentLen) {
LOG(ALERT) << "Receive error " << numRecv;
return;
}
for (size_t i = 0; i < mChans; i++) {
output = (float *) (recvBuffer[i]->begin() + recvCursor);
convert_short_float(output, convertRecvBuffer[i], 2 * num_recv);
convert_short_float(recvBuffer[i]->getWriteSegment(),
convertRecvBuffer[i],
segmentLen * 2);
}
underrun |= local_underrun;
readTimestamp += num_recv;
recvCursor += num_recv;
readTimestamp += numRecv;
}
/* Send timestamped chunk to the device with arbitrary size */
void RadioInterface::pushBuffer()
bool RadioInterface::pushBuffer()
{
int num_sent;
size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
if (sendCursor < CHUNK)
return;
if (sendCursor > sendBuffer[0]->size())
LOG(ALERT) << "Send buffer overflow";
if (sendBuffer[0]->getAvailSegments() < 1)
return false;
for (size_t i = 0; i < mChans; i++) {
convert_float_short(convertSendBuffer[i],
(float *) sendBuffer[i]->begin(),
powerScaling[i], 2 * sendCursor);
(float *) sendBuffer[i]->getReadSegment(),
powerScaling[i],
segmentLen * 2);
}
/* Send the all samples in the send buffer */
num_sent = mRadio->writeSamples(convertSendBuffer,
sendCursor,
&underrun,
writeTimestamp);
writeTimestamp += num_sent;
sendCursor = 0;
/* Send the all samples in the send buffer */
numSent = mRadio->writeSamples(convertSendBuffer,
segmentLen,
&underrun,
writeTimestamp);
writeTimestamp += numSent;
return true;
}

View File

@@ -20,7 +20,10 @@
#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
@@ -38,12 +41,9 @@ protected:
size_t mSPSTx;
size_t mSPSRx;
size_t mChans;
size_t mMIMO;
std::vector<signalVector *> sendBuffer;
std::vector<signalVector *> recvBuffer;
unsigned sendCursor;
unsigned recvCursor;
std::vector<RadioBuffer *> sendBuffer;
std::vector<RadioBuffer *> recvBuffer;
std::vector<short *> convertRecvBuffer;
std::vector<short *> convertSendBuffer;
@@ -61,16 +61,14 @@ protected:
private:
/** format samples to USRP */
int radioifyVector(signalVector &wVector,
float *floatVector,
bool zero);
/** format samples to USRP */
int radioifyVector(signalVector &wVector, size_t chan, bool zero);
/** format samples from USRP */
int unRadioifyVector(float *floatVector, signalVector &wVector);
int unRadioifyVector(signalVector *wVector, size_t chan);
/** push GSM bursts into the transmit buffer */
virtual void pushBuffer(void);
virtual bool pushBuffer(void);
/** pull GSM bursts from the receive buffer */
virtual void pullBuffer(void);
@@ -86,9 +84,9 @@ public:
virtual void close();
/** constructor */
RadioInterface(RadioDevice* wRadio = NULL,
size_t sps = 4, size_t chans = 1, size_t diversity = 1,
int receiveOffset = 3, GSM::Time wStartTime = GSM::Time(0));
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();
@@ -103,7 +101,7 @@ public:
RadioClock* getClock(void) { return &mClock;};
/** set transmit frequency */
bool tuneTx(double freq, size_t chan = 0);
virtual bool tuneTx(double freq, size_t chan = 0);
/** set receive frequency */
virtual bool tuneRx(double freq, size_t chan = 0);
@@ -135,61 +133,56 @@ public:
/** get transport window type of attached device */
enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
#if USRP1
protected:
/** Minimum latency that the device can achieve */
GSM::Time minLatency() { return mRadio->minLatency(); }
protected:
/** drive synchronization of Tx/Rx of USRP */
void alignRadio();
friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
#endif
};
#if USRP1
/** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface*);
#endif
class RadioInterfaceResamp : public RadioInterface {
private:
signalVector *innerSendBuffer;
signalVector *outerSendBuffer;
signalVector *innerRecvBuffer;
signalVector *outerRecvBuffer;
void pushBuffer();
bool pushBuffer();
void pullBuffer();
public:
RadioInterfaceResamp(RadioDevice* wRadio, size_t wSPS = 4, size_t chans = 1);
RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
~RadioInterfaceResamp();
bool init(int type);
void close();
};
class RadioInterfaceDiversity : public RadioInterface {
public:
RadioInterfaceDiversity(RadioDevice* wRadio,
size_t sps = 4, size_t chans = 2);
class RadioInterfaceMulti : public RadioInterface {
private:
bool pushBuffer();
void pullBuffer();
~RadioInterfaceDiversity();
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);
private:
std::vector<Resampler *> dnsamplers;
std::vector<float> phases;
signalVector *outerRecvBuffer;
bool mDiversity;
double mFreqSpacing;
bool setupDiversityChannels();
void pullBuffer();
double setRxGain(double dB, size_t chan);
};

View File

@@ -1,248 +0,0 @@
/*
* SSE Convolution
* 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 <radioInterface.h>
#include <Logger.h>
#include "Resampler.h"
extern "C" {
#include "convert.h"
}
/* Resampling parameters for 64 MHz clocking */
#define RESAMP_64M_INRATE 20
#define RESAMP_64M_OUTRATE 80
/* Downlink block size */
#define CHUNK 625
/* Universal resampling parameters */
#define NUMCHUNKS 48
/*
* 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 size_t resamp_inrate = 0;
static size_t resamp_inchunk = 0;
static size_t resamp_outrate = 0;
static size_t resamp_outchunk = 0;
RadioInterfaceDiversity::RadioInterfaceDiversity(RadioDevice *wRadio,
size_t sps, size_t chans)
: RadioInterface(wRadio, sps, chans, 2), outerRecvBuffer(NULL),
mDiversity(false), mFreqSpacing(0.0)
{
}
RadioInterfaceDiversity::~RadioInterfaceDiversity()
{
close();
}
void RadioInterfaceDiversity::close()
{
delete outerRecvBuffer;
outerRecvBuffer = NULL;
for (size_t i = 0; i < dnsamplers.size(); i++) {
delete dnsamplers[i];
dnsamplers[i] = NULL;
}
if (recvBuffer.size())
recvBuffer[0] = NULL;
RadioInterface::close();
}
bool RadioInterfaceDiversity::setupDiversityChannels()
{
size_t inner_rx_len;
/* Inner and outer rates */
resamp_inrate = RESAMP_64M_INRATE;
resamp_outrate = RESAMP_64M_OUTRATE;
resamp_inchunk = resamp_inrate * 4;
resamp_outchunk = resamp_outrate * 4;
/* Buffer lengths */
inner_rx_len = NUMCHUNKS * resamp_inchunk;
/* Inside buffer must hold at least 2 bursts */
if (inner_rx_len < 157 * mSPSRx * 2) {
LOG(ALERT) << "Invalid inner buffer size " << inner_rx_len;
return false;
}
/* One Receive buffer and downsampler per diversity channel */
for (size_t i = 0; i < mMIMO * mChans; i++) {
dnsamplers[i] = new Resampler(resamp_inrate, resamp_outrate);
if (!dnsamplers[i]->init()) {
LOG(ALERT) << "Rx resampler failed to initialize";
return false;
}
recvBuffer[i] = new signalVector(inner_rx_len);
}
return true;
}
/* Initialize I/O specific objects */
bool RadioInterfaceDiversity::init(int type)
{
int tx_len, outer_rx_len;
if ((mMIMO != 2) || (mChans != 2)) {
LOG(ALERT) << "Unsupported channel configuration " << mChans;
return false;
}
/* Resize for channel combination */
sendBuffer.resize(mChans);
recvBuffer.resize(mChans * mMIMO);
convertSendBuffer.resize(mChans);
convertRecvBuffer.resize(mChans);
mReceiveFIFO.resize(mChans);
dnsamplers.resize(mChans * mMIMO);
phases.resize(mChans);
if (!setupDiversityChannels())
return false;
tx_len = CHUNK * mSPSTx;
outer_rx_len = resamp_outchunk;
for (size_t i = 0; i < mChans; i++) {
/* Full rate float and integer outer receive buffers */
convertRecvBuffer[i] = new short[outer_rx_len * 2];
/* Send buffers (not-resampled) */
sendBuffer[i] = new signalVector(tx_len);
convertSendBuffer[i] = new short[tx_len * 2];
}
outerRecvBuffer = new signalVector(outer_rx_len, dnsamplers[0]->len());
return true;
}
bool RadioInterfaceDiversity::tuneRx(double freq, size_t chan)
{
double f0, f1;
if (chan > 1)
return false;
if (!mRadio->setRxFreq(freq, chan))
return false;
f0 = mRadio->getRxFreq(0);
f1 = mRadio->getRxFreq(1);
mFreqSpacing = f1 - f0;
if (abs(mFreqSpacing) <= 600e3)
mDiversity = true;
else
mDiversity = false;
return true;
}
/* Receive a timestamped chunk from the device */
void RadioInterfaceDiversity::pullBuffer()
{
bool local_underrun;
int rc, num, path0, path1;
signalVector *shift, *base;
float *in, *out, rate = -mFreqSpacing * 2.0 * M_PI / 1.08333333e6;
if (recvCursor > recvBuffer[0]->size() - resamp_inchunk)
return;
/* Outer buffer access size is fixed */
num = mRadio->readSamples(convertRecvBuffer,
resamp_outchunk,
&overrun,
readTimestamp,
&local_underrun);
if ((size_t) num != resamp_outchunk) {
LOG(ALERT) << "Receive error " << num;
return;
}
for (size_t i = 0; i < mChans; i++) {
convert_short_float((float *) outerRecvBuffer->begin(),
convertRecvBuffer[i], 2 * resamp_outchunk);
if (!i) {
path0 = 0;
path1 = 2;
} else {
path0 = 3;
path1 = 1;
}
/* Diversity path 1 */
base = outerRecvBuffer;
in = (float *) base->begin();
out = (float *) (recvBuffer[path0]->begin() + recvCursor);
rc = dnsamplers[2 * i + 0]->rotate(in, resamp_outchunk,
out, resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate downsampling error";
}
/* Enable path 2 if Nyquist bandwidth is sufficient */
if (!mDiversity)
continue;
/* Diversity path 2 */
shift = new signalVector(base->size(), base->getStart());
in = (float *) shift->begin();
out = (float *) (recvBuffer[path1]->begin() + recvCursor);
rate = i ? -rate : rate;
if (!frequencyShift(shift, base, rate, phases[i], &phases[i])) {
LOG(ALERT) << "Frequency shift failed";
}
rc = dnsamplers[2 * i + 1]->rotate(in, resamp_outchunk,
out, resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate downsampling error";
}
delete shift;
}
underrun |= local_underrun;
readTimestamp += (TIMESTAMP) resamp_outchunk;
recvCursor += resamp_inchunk;
}

View File

@@ -0,0 +1,391 @@
/*
* 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 */
void RadioInterfaceMulti::pullBuffer()
{
bool local_underrun;
size_t num;
float *buf;
if (recvBuffer[0]->getFreeSegments() <= 0)
return;
/* 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;
}
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();
size_t hSize = 2 * hLen * sizeof(float);
memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
float *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";
}
}
}
/* 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();
}

View File

@@ -1,8 +1,10 @@
/*
* Radio device interface with sample rate conversion
* Written by Thomas Tsou <tom@tsou.cc>
*
* Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
* 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
@@ -56,10 +58,9 @@ static size_t resamp_outrate = 0;
static size_t resamp_outchunk = 0;
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
size_t sps, size_t chans)
: RadioInterface(wRadio, sps, chans),
innerSendBuffer(NULL), outerSendBuffer(NULL),
innerRecvBuffer(NULL), outerRecvBuffer(NULL)
size_t tx_sps, size_t rx_sps)
: RadioInterface(wRadio, tx_sps, rx_sps, 1),
outerSendBuffer(NULL), outerRecvBuffer(NULL)
{
}
@@ -70,17 +71,13 @@ RadioInterfaceResamp::~RadioInterfaceResamp()
void RadioInterfaceResamp::close()
{
delete innerSendBuffer;
delete outerSendBuffer;
delete innerRecvBuffer;
delete outerRecvBuffer;
delete upsampler;
delete dnsampler;
innerSendBuffer = NULL;
outerSendBuffer = NULL;
innerRecvBuffer = NULL;
outerRecvBuffer = NULL;
upsampler = NULL;
@@ -99,11 +96,6 @@ bool RadioInterfaceResamp::init(int type)
{
float cutoff = 1.0f;
if (mChans != 1) {
LOG(ALERT) << "Unsupported channel configuration " << mChans;
return false;
}
close();
sendBuffer.resize(1);
@@ -128,13 +120,8 @@ bool RadioInterfaceResamp::init(int type)
return false;
}
resamp_inchunk = resamp_inrate * 4;
resamp_outchunk = resamp_outrate * 4;
if (resamp_inchunk * NUMCHUNKS < 157 * mSPSTx * 2) {
LOG(ALERT) << "Invalid inner chunk size " << resamp_inchunk;
return false;
}
resamp_inchunk = resamp_inrate * 4 * mSPSRx;
resamp_outchunk = resamp_outrate * 4 * mSPSRx;
if (mSPSTx == 4)
cutoff = RESAMP_TX4_FILTER;
@@ -157,21 +144,18 @@ bool RadioInterfaceResamp::init(int type)
* and requires headroom equivalent to the filter length. Low
* rate buffers are allocated in the main radio interface code.
*/
innerSendBuffer =
new signalVector(NUMCHUNKS * resamp_inchunk, upsampler->len());
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());
innerRecvBuffer =
new signalVector(NUMCHUNKS * resamp_inchunk / mSPSTx);
convertSendBuffer[0] = new short[outerSendBuffer->size() * 2];
convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2];
sendBuffer[0] = innerSendBuffer;
recvBuffer[0] = innerRecvBuffer;
return true;
}
@@ -181,7 +165,7 @@ void RadioInterfaceResamp::pullBuffer()
bool local_underrun;
int rc, num_recv;
if (recvCursor > innerRecvBuffer->size() - resamp_inchunk)
if (recvBuffer[0]->getFreeSegments() <= 0)
return;
/* Outer buffer access size is fixed */
@@ -204,57 +188,47 @@ void RadioInterfaceResamp::pullBuffer()
/* Write to the end of the inner receive buffer */
rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
resamp_outchunk,
(float *) (innerRecvBuffer->begin() + recvCursor),
recvBuffer[0]->getWriteSegment(),
resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate upsampling error";
}
recvCursor += resamp_inchunk;
/* Set history for the next chunk */
outerRecvBuffer->updateHistory();
}
/* Send a timestamped chunk to the device */
void RadioInterfaceResamp::pushBuffer()
bool RadioInterfaceResamp::pushBuffer()
{
int rc, chunks, num_sent;
int inner_len, outer_len;
int rc;
size_t numSent;
if (sendCursor < resamp_inchunk)
return;
if (sendCursor > innerSendBuffer->size())
LOG(ALERT) << "Send buffer overflow";
chunks = sendCursor / resamp_inchunk;
inner_len = chunks * resamp_inchunk;
outer_len = chunks * resamp_outchunk;
if (sendBuffer[0]->getAvailSegments() <= 0)
return false;
/* Always send from the beginning of the buffer */
rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
(float *) outerSendBuffer->begin(), outer_len);
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 * outer_len);
powerScaling[0], 2 * resamp_outchunk);
num_sent = mRadio->writeSamples(convertSendBuffer,
outer_len,
&underrun,
writeTimestamp);
if (num_sent != outer_len) {
LOG(ALERT) << "Transmit error " << num_sent;
numSent = mRadio->writeSamples(convertSendBuffer,
resamp_outchunk,
&underrun,
writeTimestamp);
if (numSent != resamp_outchunk) {
LOG(ALERT) << "Transmit error " << numSent;
}
/* Shift remaining samples to beginning of buffer */
memmove(innerSendBuffer->begin(),
innerSendBuffer->begin() + inner_len,
(sendCursor - inner_len) * 2 * sizeof(float));
writeTimestamp += resamp_outchunk;
writeTimestamp += outer_len;
sendCursor -= inner_len;
assert(sendCursor >= 0);
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -20,15 +20,21 @@
#include "BitVector.h"
#include "signalVector.h"
/** Convolution type indicator */
enum ConvType {
START_ONLY,
NO_DELAY,
CUSTOM,
UNDEFINED,
/* 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
RACH, ///< timeslot should contain an access burst
EDGE, ///< timeslot should contain an EDGE burst
IDLE ///< timeslot is an idle (or dummy) burst
};
enum signalError {
enum SignalError {
SIGERR_NONE,
SIGERR_BOUNDS,
SIGERR_CLIP,
@@ -36,112 +42,47 @@ enum signalError {
SIGERR_INTERNAL,
};
/** Convert a linear number to a dB value */
float dB(float x);
/** Convert a dB value into a linear value */
float dBinv(float x);
/** Compute the energy of a vector */
float vectorNorm2(const signalVector &x);
/** Compute the average power of a vector */
float vectorPower(const signalVector &x);
/*
* 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(int sps);
bool sigProcLibSetup();
/** Destroy the signal processing library */
void sigProcLibDestroy(void);
/**
Convolve two vectors.
@param a,b The vectors to be convolved.
@param c, A preallocated vector to hold the convolution result.
@param spanType The type/span of the convolution.
@return The convolution result or NULL on error.
*/
signalVector *convolve(const signalVector *a, const signalVector *b,
signalVector *c, ConvType spanType,
size_t start = 0, size_t len = 0,
size_t step = 1, int offset = 0);
/**
Frequency shift a vector.
@param y The frequency shifted vector.
@param x The vector to-be-shifted.
@param freq The digital frequency shift
@param startPhase The starting phase of the oscillator
@param finalPhase The final phase of the oscillator
@return The frequency shifted vector.
*/
signalVector* frequencyShift(signalVector *y,
signalVector *x,
float freq = 0.0,
float startPhase = 0.0,
float *finalPhase=NULL);
/**
Correlate two vectors.
@param a,b The vectors to be correlated.
@param c, A preallocated vector to hold the correlation result.
@param spanType The type/span of the correlation.
@return The correlation result.
*/
signalVector* correlate(signalVector *a,
signalVector *b,
signalVector *c,
ConvType spanType,
bool bReversedConjugated = false,
unsigned startIx = 0,
unsigned len = 0);
/** Operate soft slicer on real-valued portion of vector */
bool vectorSlicer(signalVector *x);
/** 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);
int guardPeriodLength,
int sps, bool emptyPulse = false);
/** Sinc function */
float sinc(float x);
/** 8-PSK modulate a burst of bits */
signalVector *modulateEdgeBurst(const BitVector &bits,
int sps, bool emptyPulse = false);
/** Delay a vector */
signalVector *delayVector(signalVector *in, signalVector *out, float delay);
/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
signalVector *generateEdgeBurst(int tsc);
/** Add two vectors in-place */
bool addVector(signalVector &x,
signalVector &y);
/** Generate an empty burst - 4 or 1 SPS */
signalVector *generateEmptyBurst(int sps, int tn);
/** Multiply two vectors in-place*/
bool multVector(signalVector &x,
signalVector &y);
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandNormalBurst(int tsc, int sps, int tn);
/** Generate a vector of gaussian noise */
signalVector *gaussianNoise(int length,
float variance = 1.0,
complex mean = complex(0.0));
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandAccessBurst(int delay, int sps, int tn);
/**
Given a non-integer index, interpolate a sample.
@param inSig The signal from which to interpolate.
@param ix The index.
@return The interpolated signal value.
*/
complex interpolatePoint(const signalVector &inSig,
float ix);
/**
Given a correlator output, locate the correlation peak.
@param rxBurst The correlator result.
@param peakIndex Pointer to value to receive interpolated peak index.
@param avgPower Power to value to receive mean power.
@return Peak value.
*/
complex peakDetect(const signalVector &rxBurst,
float *peakIndex,
float *avgPwr);
/** Generate a dummy GSM burst - 4 or 1 SPS */
signalVector *generateDummyBurst(int sps, int tn);
/**
Apply a scalar to a vector.
@@ -149,125 +90,40 @@ complex peakDetect(const signalVector &rxBurst,
@param scale The scalar.
*/
void scaleVector(signalVector &x,
complex scale);
complex scale);
/**
Generate a modulated GSM midamble, stored within the library.
@param gsmPulse The GSM pulse used for modulation.
@param sps The number of samples per GSM symbol.
@param TSC The training sequence [0..7]
@return Success.
*/
bool generateMidamble(int sps, int tsc);
/**
Generate a modulated RACH sequence, stored within the library.
@param gsmPulse The GSM pulse used for modulation.
@param sps The number of samples per GSM symbol.
@return Success.
*/
bool generateRACHSequence(int sps);
/**
Energy detector, checks to see if received burst energy is above a threshold.
@param rxBurst The received GSM burst of interest.
Rough energy estimator.
@param rxBurst A GSM burst.
@param windowLength The number of burst samples used to compute burst energy
@param detectThreshold The detection threshold, a linear value.
@param avgPwr The average power of the received burst.
@return True if burst energy is above threshold.
@return The average power of the received burst.
*/
bool energyDetect(signalVector &rxBurst,
unsigned windowLength,
float detectThreshold,
float *avgPwr = NULL);
float energyDetect(const signalVector &rxBurst,
unsigned windowLength);
/**
RACH correlator/detector.
@param rxBurst The received GSM burst of interest.
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received RACH burst.
@param TOA The estimate time-of-arrival of received RACH burst.
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int sps,
complex *amplitude,
float* TOA);
/**
Normal burst correlator, detector, channel estimator.
@param rxBurst The received GSM burst of interest.
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
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.
@param maxTOA The maximum expected time-of-arrival
@param requestChannel Set to true if channel estimation is desired.
@param channelResponse The estimated channel.
@param channelResponseOffset The time offset b/w the first sample of the channel response and the reported TOA.
@return positive if threshold value is reached, negative on error, zero otherwise
@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 analyzeTrafficBurst(signalVector &rxBurst,
unsigned TSC,
float detectThreshold,
int sps,
complex *amplitude,
float *TOA,
unsigned maxTOA,
bool requestChannel = false,
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
int detectAnyBurst(const signalVector &burst,
unsigned tsc,
float threshold,
int sps,
CorrType type,
complex &amp,
float &toa,
unsigned max_toa);
/**
Decimate a vector.
@param wVector The vector of interest.
@param factor Decimation factor.
@return The decimated signal vector.
*/
signalVector *decimateVector(signalVector &wVector, size_t factor);
/**
Demodulates a received burst using a soft-slicer.
@param rxBurst The burst to be demodulated.
@param gsmPulse The GSM pulse.
@param sps The number of samples per GSM symbol.
@param channel The amplitude estimate of the received burst.
@param TOA The time-of-arrival of the received burst.
@return The demodulated bit sequence.
*/
SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
/**
Design the necessary filters for a decision-feedback equalizer.
@param channelResponse The multipath channel that we're mitigating.
@param SNRestimate The signal-to-noise estimate of the channel, a linear value
@param Nf The number of taps in the feedforward filter.
@param feedForwardFilter The designed feed forward filter.
@param feedbackFilter The designed feedback filter.
@return True if DFE can be designed.
*/
bool designDFE(signalVector &channelResponse,
float SNRestimate,
int Nf,
signalVector **feedForwardFilter,
signalVector **feedbackFilter);
/**
Equalize/demodulate a received burst via a decision-feedback equalizer.
@param rxBurst The received burst to be demodulated.
@param TOA The time-of-arrival of the received burst.
@param sps The number of samples per GSM symbol.
@param w The feed forward filter of the DFE.
@param b The feedback filter of the DFE.
@return The demodulated bit sequence.
*/
SoftVector *equalizeBurst(signalVector &rxBurst,
float TOA,
int sps,
signalVector &w,
signalVector &b);
/** 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 */

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