Compare commits

...

166 Commits

Author SHA1 Message Date
Sylvain Munaut
d40f11962a Add C/I computation
Change-Id: Ib4ceec553f2e5f77bf3f6777724968456a180f5e
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2019-05-14 18:24:35 +02:00
Pau Espin Pedrol
dcbcfa58e4 lms: Use smpl_buf to recover from timestamp jumps
Also take the chance to make sure we handle properly short reads (keep
reading again).

Both scenarios can be tested by running osmo-trx-lms and then using
on a terminal:
sudo kill -STOP `pidof osmo-trx-lms`; sleep 0.5; sudo kill -CONT `pidof osmo-trx-lms`

Fixes: OS#3339
Change-Id: Idfc4e69acc30afb11440b6b9cbdcfa09ff920265
2019-05-06 11:23:36 +02:00
Pau Espin Pedrol
b5def414b8 smpl_buf: Move it to device/common and create libdevice_common.la
Since in next commit osmo-trx-lms starts using smpl_buf.cpp, it seems
some automake step doesn't like including a cpp file twice from a
different directory, since race conditions can occur building it.
Instead we define the dependency by first building a static lib and then
using it on each libdevice.la (one per device type).

We already do the similar under arch/ subdir, where we have a common/
subdir and then one subdir and lib per architecture.

Change-Id: I465ad0f6d5569bb3006d711c8fd0df14391fcf35
2019-05-06 11:23:18 +02:00
Pau Espin Pedrol
fc73c073a1 Introduce LOGCHAN macro to standarize logging channel info
Change-Id: I67d869499aa16af58c863ca7b74c356bcd979936
2019-05-06 11:21:43 +02:00
Pau Espin Pedrol
a7919265f5 smpl_buf: Remove dbg log line with duplicated info
Change-Id: Ia2423707210a364fa6827b92cca087ced99b088b
2019-05-04 20:36:07 +02:00
Pau Espin Pedrol
9b394e0cc0 smpl_buf: Remove unused clk_rt variable (fixup)
Last commit removed use of the clkr_rt variable but forgot to remove the
variable itself.

Fixes: 580c48b7d5
Fixes: Coverity CID 198370
Change-Id: Ida1fc5b7b338fbeb2a7c6258f36b02da93ff2186
2019-05-04 20:05:06 +02:00
Pau Espin Pedrol
580c48b7d5 smpl_buf: Remove unused clk_rt variable
During 87b7d098e5 we dropped support for
UHD specific functionalitites, and so clk_rt is not needed anymore.

Change-Id: I37403e085ed6a541bbdecf64f1f9a821ff2753a4
2019-05-03 14:38:51 +02:00
Pau Espin Pedrol
7214fde085 device: Drop unused numberRead/numberWritten APIs
It's really not used, so let's drop unused code and simplify work for
new to come device drivers implementation.

Change-Id: I0d18f9c2584771e2f7b3d5c6b016e764e02855ff
2019-05-03 13:50:50 +02:00
Pau Espin Pedrol
51509b3895 Move smpl_buf out of uhd dir to re-use it in other devices
Change-Id: I39ac8435072cff8d4dac786b31ff4af9b61a77fe
2019-05-03 13:50:50 +02:00
Pau Espin Pedrol
ef192d303c uhd: Avoid reallocation of buffers every read
Buffer size is based on num of chans and rxBuffer size is based on num
of chans and rx_spp, and both are available and set during open(), so no
need to waste time allocating and freeing them everytime we read from
the device.

Change-Id: I8c881c9c303c80f323825d85a924d74b76d2ce47
2019-05-03 13:50:50 +02:00
Pau Espin Pedrol
ac927b2690 cosmetic: uhd: Use loglevel ERROR instead of ERR
ERR is osmo-trx legacy level, which actually converts to osmocom's
ERROR, so let's use that one directly.

Change-Id: I82f6f89a725bea7f7acfa455c20cf922cc3f8a00
2019-05-03 13:50:50 +02:00
Pau Espin Pedrol
3e9179a55e uhd: smpl_buf: Use TIMESTAMP type in str_status
Other related functions use "TIMESTAMP timestamp" so let's use same
stuff in that function.

Change-Id: I016b1a7f8db379caebc1409ca11e5ae8b759d2d4
2019-05-03 13:50:50 +02:00
Pau Espin Pedrol
87b7d098e5 uhd: smpl_buf: Drop UHD specifics out back to UHDDevice
This way smpl_buf can be re-used later by other non-UHD drivers.

Change-Id: I94061328d46a550d4147121d85baffa29c700c45
2019-05-03 13:50:50 +02:00
Pau Espin Pedrol
7bef2346c4 cosmetic: uhd: Move smpl_buf out of UHDDevice, move UHDDevice class definition to .h
* move class definition to .h file, like we do for other devices.
* move smpl_buf class to a different file inside uhd/.
* Preparation work to have smpl_buf being used in a generic way for
devices other than UHD (LMS).

Change-Id: Ib4594320da9bb7f6e9f52e7d70d11ecd11106aae
2019-05-03 13:50:47 +02:00
Pau Espin Pedrol
2876785f50 lms: Catch and log dropped packets by HW during recv
Change-Id: I23554d95b0aff585024610fc12920c9da4f3ba9e
2019-04-25 20:16:10 +02:00
Pau Espin Pedrol
713b4d81cd lms: Remove unused var m_last_tx_overruns
Change-Id: I2104205b2af7cd6c86075d5cc4f0f36bde5e5311
2019-04-25 20:16:10 +02:00
Pau Espin Pedrol
b96d9ddee3 lms: Remove references to ALERT loglevel
ALERT log level is not Osmocom standard level, it's just a define in
osmo-trx to keep compatibility with old code.

Same goes for one reference to "ERR" intead of "ERROR".

Change-Id: I0e171a8ac8a8bfa804ac97fba3d73efcfa6424b4
2019-04-25 20:16:10 +02:00
Pau Espin Pedrol
048c3ba300 lms: Log underrun/overrun events
Change-Id: I8c6b1d3e8515153e5d4079cc6620901ef8ce2449
2019-04-25 20:16:10 +02:00
Pau Espin Pedrol
75cb0b9dd6 Move duplicated thread_enable_cancel to CommonLibs
Change-Id: I1a479b59bdda01233273dfa919bd678edbe34708
2019-04-25 19:33:58 +02:00
Pau Espin Pedrol
46324d3597 cosmetic: Threads.h: Remove trailing whitespace
Change-Id: I0ae6e435a7f0480c3eaa08dccfe824456f33b015
2019-04-25 19:33:11 +02:00
Pau Espin Pedrol
bab1583a2c lms: Improve log during flush recv error
Change-Id: Id45d42599738efd6a5d89787c75779838a979330
2019-04-25 19:26:24 +02:00
Pau Espin Pedrol
541496b65b lms: flush_recv: alloc buf on stack instead of heap
No need to use the heap here since buffer is only used as a temporary
trash. Using the stack is quicker.

Change-Id: Iede8dc0903ee3865a52c8e2fd811bcde444fee33
2019-04-25 19:22:11 +02:00
Joachim Steiger
c785fb130a lms: properly call close if set_antennas() fails, add some comments
Change-Id: I9ebe986ee3a15842a15853424ee98e9a2fa6a5df
2019-04-17 18:24:11 +00:00
Joachim Steiger
2875290d95 lms: add device type detection and device specific gains
add device dependant max gain setup - limesdr mini and limenet micro need slightly reduced maximum gains to get a PASS on phase error measurements
rework clock reference setup - external clock needs to be selected before calling LMS_Init(), internal can only be set after.
remove now unused compat_LMS_VCTCXO* functions - we do not set the VCTXCO directly anymore

Change-Id: I3cf905b0a84bc1ec200891762a6646141ee37181
2019-04-17 18:24:11 +00:00
Joachim Steiger
4ce4555d0e lms: move LMS_GetLPFBWRange and LMS_Calibrate calls from open to start
bandwidth, freqency, gain stages need to be set before calibration can be successful

Change-Id: I1090effdf0f43e5183a402e4c1a1ffe5abdefd37
2019-04-17 18:24:11 +00:00
Joachim Steiger
2d130fb15d lms: move LMS_EnableChannel from Start/Stop to Open/Close device
move enable: it is important that channels are actually enabled before applying any configuration (besides external clock)
move disable: move to close, so channels are not disabled and not enabled again while osmotrx is active.

Change-Id: I82878913254ce15a85db8d006e13d5eb639793e9
2019-04-17 18:24:11 +00:00
Joachim Steiger
6c5f4bae46 lms: Remove wrong unused code copied from -uhd
Change-Id: I2bc7fcfa429d1f82c2d8e95d31dfed367f2b3f9d
2019-04-17 18:24:11 +00:00
Harald Welte
71df42550a use BSC_FD_READ and not OSMO_FD_READ
We haven't even released any tagged version of libosmocore yet which
includes support for the renamed OSMO_FD_READ constant.  Let's avoid
any breakage and use the new constants only with considerable delay,
at the very least only when released libosmocore versions provide it.

Change-Id: Idb57077b2a4b2a71dd5d75a24ded8bb5887da188
2019-04-16 17:18:58 +02:00
Vadim Yanitskiy
24f05ea1f7 doc/configuration.adoc: fix incorrect number of physical RF channels for B210
Change-Id: I58e2bf5dd99e1655ebd2ad80f6ed2bb178f0e88d
2019-04-11 07:34:04 +00:00
Tom Tsou
d280045884 multi-ARFCN: fix maximum number of carriers limitation
Maximum number of carriers is fixed to 3 channels on a single
physical RF channel in multi-ARFCN mode. For some reason, it
was limited to 5.

Let's fix this, and also follow this limitation in the
following VTY command handlers:

  - cfg_multi_arfcn_cmd,
  - cfg_chan_cmd.

Change-Id: I66a1462f368458afd313ee6f0bc0abc496dde817
2019-04-11 07:33:40 +00:00
Pau Espin Pedrol
5e6f3e0cad osmo-trx: Check return code of osmo_fd_register
Fixes Coverity CID 197513.
Change-Id: I93fbcb062439a547379aaecba283d107fdc9cb59
2019-04-02 11:45:06 +02:00
Pau Espin Pedrol
21032b75c0 osmo-trx: Use signalfd to serialize signals in main thread ctx
This should avoid prolematic scenarios where different signal handlers
are running on different thread in parallel. Furthermore, we make sure
those signals are always run by main loop thread.

Change-Id: I9b9d9793be9af11dbe433e0ce09b7ac57a3bdfb5
2019-03-29 19:20:08 +01:00
Pau Espin Pedrol
d01c7b98b6 osmo-trx: Avoid handling signals after shutdown triggered
Recently a blocked osmo-trx process was found after ending SIGTERM to
it.
Apparently one thread was handling SIGTERM and calling fprintf()
(grabbing libc lock) while another thread was handling another signal
and also grabbing similar lock. Both thread looked deadlocked there.
Probably this change doesn't fix the block on its own, but at least
simplifies scenarios inside signal ctx which can go wrong.

Change-Id: If91621913b8b03d8a0f4c863be0b0d479f97e8a1
2019-03-29 18:51:28 +01:00
Sylvain Munaut
158ea5bc66 tests: Re-enable the convolve_test by default
Change-Id: Ia26ef75bb11482fc0db3b790db1c93c8b74229d1
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2019-01-24 20:02:54 +00:00
Sylvain Munaut
3733ed5097 tests: Rework the convolve_test
Besides just general cleanup, the major changes are :
 - Fully internal generation of reference data that doesn't
   depend on glibc or even on any floating point math
 - Golden results are included in a .h
   Due to varying precision of different implementation or
   architecture, any kind of textual compare is impossible, so
   we include golden values and compare results of both the
   'base' implementation the potentially 'optimized' one again
   this set of values with a small error tolerance

Change-Id: I4e203d2c4b778af77d630ed15d4cef6b0c0eb76d
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2019-01-24 20:02:46 +00:00
Vadim Yanitskiy
a8b3565246 VTY: add extended (11-bit) RACH detection toggle
Since I838c21db29c54f1924dd478c2b34b46b70aab2cd we have both TS1
and TS2 synch. sequences, in addition to "default" TS0. Let's
finally introduce the VTY configuration parameter, that can
be used to toggle optional detection of both TS1 and TS2.

Note: we keep this optional because of potentially bad impact on
performance. There's no point in paying the performance penalty
unless upper levels (BTS, PCU) actually make use of it.

Change-Id: I1aee998d83b06692d76a83f79748f9129a2547e8
Related: OS#3054
2019-01-24 15:47:48 +01:00
Sylvain Munaut
a3934a11a4 convolve: Remove support for step, offset parameters
- Those are not used any where
 - Those are not supported by the sse/neon accelerated versions
 - And I see very little use cases for those.

Change-Id: Ic850269a0ed5d98c0ea68980afd31016ed555b48
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2019-01-21 10:34:51 +01:00
Sylvain Munaut
acf804c034 arm/convolve: Fix the vfp4 real convolution for h_len=12
Change-Id: Ic73f0746edd3f1f22bb1d79d4c64aa740691dd48
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2019-01-21 10:34:51 +01:00
Harald Welte
d20b7fa579 Bump version: 0.4.0.125-7c78 → 1.0.0
Change-Id: Ibb888f55a1ab27088d5f21ee8ad699f4f747abde
2019-01-20 20:54:40 +01:00
Harald Welte
77e18352fb cosmetic: Don't call the SDR "USRP" in error message
Transceiver.cpp is used for all SDR hardware we support, not just USRP.

Change-Id: I9b7ddb0d679f111407704048ef3ddd964db49441
2019-01-11 22:42:29 +01:00
Harald Welte
f97d75b355 usrp1: Fix formatting of log message (missing space)
Change-Id: I378a8081a224acda3ee2af9b6aa0d680de884695
2019-01-11 22:42:29 +01:00
Harald Welte
a944001873 usrp1: Remove uninitialized skipRx logic
There appears to have been some logic to operate a USRP1 in
transmit-only GSM mode.  This is achieved using the skipRx member
of the transceiver object.  However, there's nobody that ever
initializes it properly, and hence the feature is not possible to
use anyway.

I don't think this has any valid use case, so let's remove it.

Change-Id: I616193f1e9aaefbf4ceb26761657811093f28b6f
2019-01-11 21:46:45 +01:00
Oliver Smith
42c165605a LMSDeviceTest: fix link errors on OBS
Link LMSDeviceTest against LMS_LIBS, so it does not only compile on
Debian, but also on Ubuntu and openSUSE. Thanks to roox for figuring
this out.

Related: OS#3654
Change-Id: I6980d4290f623485a77db10fea6d17de0321c092
2018-12-17 09:43:45 +00:00
Pau Espin Pedrol
affd351787 configure.ac: check boost only if USRP1 support is enabled
boost is only used in USRPDevice class.

It looks like it can be removed entirely quite easily, since only
boost::shared_ptr is used for 2 variables. That's left for somebody with
the device and willingness to test the changes.

Change-Id: I4c3fa3ff58fd552d0cb4c4cf2033615d84c07c96
2018-12-14 20:05:36 +00:00
Pau Espin Pedrol
03b11620d9 lms: Fix build against LimeSuite > 18.10
LimeSuite dc124e4e2ed9b549b142410af172f0592f9f0c23 > 18.10 broke API
compatibility.

OS#3729
Change-Id: Idf500a5b39a857233f728c6a4316c00a99374556
2018-12-14 13:30:07 +01:00
Pau Espin Pedrol
25185886f0 TimevalTest: Make test deterministic with fake time
Change-Id: I74e577a0142fb6d1ef3630e02aff9910b191bff9
2018-12-13 13:58:24 +00:00
Pau Espin Pedrol
47031405f5 Timeval: Move to osmo_clock_gettime
Change-Id: I24da3e1136c5396062662be1d10b07b4d97cfc2e
2018-12-13 13:58:24 +00:00
Pau Espin Pedrol
0646b3ce75 Timeval: Move implementation to use clock_gettime and timespec
According to gettimeofday manual:
"Applications should use the clock_gettime() function instead of the
obsolescent gettimeofday() function."

Furthermore, it may be desirable in the future to use other clocks such
as monotonic.

Change-Id: I2286998c5eefbf3c3dfb105c223daec7a1083803
2018-12-13 13:58:24 +00:00
Pau Espin Pedrol
46cf9efc8e Timeval: passed() returns true if time is equal
Change-Id: I96a9d26657f85447609693bc6932d218d354b84a
2018-12-13 13:58:24 +00:00
Oliver Smith
871713bf6a LMSDevice: make use of dev-args in osmo-trx.cfg
Allow selecting a specific LimeSDR device by setting dev-args in the
config file. Split up the given dev-args address by comma and select
the device where all substrings can be found.

I could not test this with real hardware, but I have added a test case
to make sure this works as expected.

Related: OS#3654
Change-Id: Ib9aaa066a01bf9de3f78234d7ada884d6f28c852
2018-12-13 11:07:02 +01:00
Oliver Smith
8d9a05ce5b osmo-trx.cpp: move comma_delimited_to_vector() to Utils.cpp
Make the "opt" argument const. This function will also be used by
LMSDevice.cpp in a follow-up commit.

Related: OS#3654
Change-Id: If3f0f682ca453c2b0a06175ec9626567932cfce6
2018-12-12 17:26:32 +01:00
Pau Espin Pedrol
fe865f45d7 lms: Do {under,over}run checks even if LMS_RecvStream fails
Under failure, it could still be that stream status is updated, so let's
father that in all cases.

Change-Id: I4e2b8be06d2993db1bab233948a8ee774b8ac4ee
2018-12-07 11:18:32 +01:00
Pau Espin Pedrol
e5b6664419 lms: Move {under,over}run checks into separate method
Change-Id: I7f450aa79f6285d14826c40ecfdd9490d00509ef
2018-12-07 11:18:28 +01:00
Pau Espin Pedrol
1595ddaa5f transceiver: Log TRXCTRL iface responses towards osmo-bts-trx
Change-Id: I8128c42e63ab1fcd2a58812f3b7cf94435b5bbd8
2018-12-07 11:15:10 +01:00
Pau Espin Pedrol
441d82add9 Add TRXCTRL log category
This log category is applied to messages related to TRX CTRL socket
interface, and it's printed in yellow, same color used in osmo-bts-trx
for TRX category (so same messages are printed with same color in both
sides).

Change-Id: I98ec5e416272783ad3fbadf70478a4e48ae64983
2018-12-07 11:15:02 +01:00
Oliver Smith
867cea575b contrib: fix makedistcheck with disabled systemd
EXTRA_DIST files need to be distributed, no matter if the systemd option
is configured or not.

Change-Id: I2e417f4c453987df1ac04f0ff41bf000da4b5c49
2018-12-06 16:31:30 +00:00
Pau Espin Pedrol
2f53ea4cf2 transceiver: log chan on CTRL command received
Change-Id: Ia3b2a35e03a8aaddd6efebc8db2ccca872f51f20
2018-12-05 19:43:46 +00:00
Pau Espin Pedrol
138caaf09d lms: Improve Set{Rx,Tx}{Gain,Freq} logging
Change-Id: I6713a27c5f74beb7fcfdd712fcf695afccbc3d76
2018-12-05 19:43:46 +00:00
Pau Espin Pedrol
3fadcad33e lms: Allow setting Tx/RxFreq for lchan!=0
Related: OS#3346
Change-Id: I9dd0bb41d1863111d28947fc0d7c7e7ecfaf5fa8
2018-12-05 19:43:46 +00:00
Pau Espin Pedrol
eaa0144dcb lms: Allow setting Tx/RxGain for chan!=0
Related: OS#3346
Change-Id: I5fae92a7ed5e2d92be12b9dfd33fc56195030c0b
2018-12-05 19:43:46 +00:00
Pau Espin Pedrol
f7331764ac SigProcLib: Improve Vector buffer allocation mess
Original issue: In order to use SSE instructions, 16-byte aligned memory
chunks are needed, and C++ version < C++11 doesn't provide for a native
new/delete store. For that reason, memalign() must be used in the
implementation of convolve_h_alloc() for some buffers.
On the other side, The C++ code relies on C++ "new T[]" operator to
allocate a chunk of memory containing an array of class instances. As
classes are complex types, they cannot be allocated through C structures
(calling malloc). Experimentally can be seen too that it's unreliable
and the process will crash during startup if malloc() is used and then a
Complex<> deferred from it.

Previous implementation allowed for use of convolve_h_alloc or new[]
based on how the (signal)Vector is called, because then the buffer is
not going to be managed internally. But that's unreliable since resize()
calling resize() on it could use "delete" operator on a malloc'ed
buffer, and end up having a new new[] allocated buffer. It was also
found that some of the callers were actually leaking memory through ASan (because the
buffer is not managed by the Vector instance).

IMHO best option would be to rewrite all this code using C structures
and malloc/free exclusively, since it would make all this cod eeasier to
maintain.

But for now, let's extend the Vector class to allow specifying an
external alloc/free function and let the Vector instance take care of
the ownership of the buffer in all scenarios.

Change-Id: Ie484a4762a7f77fe1b105188ea03a6f025730b82
2018-12-05 19:41:34 +00:00
Oliver Smith
800c029c70 jenkins.sh: run "make distcheck"
Run distcheck in CI to be consistent with other Osmocom projects.

Change-Id: I7d3fa0fe12e13ca13d3330158425718e5c34d00d
2018-12-05 16:26:35 +01:00
Oliver Smith
522cbe91d3 contrib/jenkins.sh: build and publish manuals
Add new environment variables WITH_MANUALS and PUBLISH to control if
the manuals should be built and uploaded. Describe all environment vars
on top of the file.

When WITH_MANUALS is set, install osmo-gsm-manuals like any other
dependency and add --enable-manuals to the configure flags (for "make"
and "make distcheck"). Add the bin subdir of the installed files to
PATH, so osmo-gsm-manuals-check-depends can be used by ./configure.

Related: OS#3385
Change-Id: I54fdd18e405b2c84762ea06d91359867ceec8184
2018-12-05 13:10:38 +01:00
Pau Espin Pedrol
ed361f9912 lms: Close device on LMS_Init failure
Change-Id: I0b307452a9e122a0ea39a46f096423c9a5293d30
2018-12-04 15:57:37 +00:00
Pau Espin Pedrol
2d085e290b osmo-trx: Change some lines to use libosmocore logging instead of cout
Change-Id: I66e3c37014ba12cd002e5b678bc0a6026f5dfc7e
2018-12-04 15:57:37 +00:00
Pau Espin Pedrol
5cea18e5cb lms: Make sure LMS_Close is called when Device is torn down
This change fixes lots of memory leaks inside libLimeSuite as
announced by ASan after exiting the osmo-trx process (throgh
CTRL+C for instance).

This way also we make sure libLimeSuite can communicate with the HW and
close whatever subsystems were enabled during LMS_Open time.

Change-Id: I56ffb87079e34aa2d0322fd2ca6429742f9f7640
2018-12-04 15:57:37 +00:00
Oliver Smith
4adc4eb8d0 Fix DISTCHECK_CONFIGURE_FLAGS override
Set AM_DISTCHECK_CONFIGURE_FLAGS in Makefile.am instead of
DISTCHECK_CONFIGURE_FLAGS. This is the recommended way from the
automake manual, as otherwise the flag can't be changed by the user
anymore.

Related: OS#3718
Change-Id: I725026cd2dda333085a263c503058aac4305197d
2018-12-04 15:41:40 +01:00
Pau Espin Pedrol
41c68aa41c PointerFIFO: Fix memleak of ListNode
Found by ASan. when PointerFIFO::release() is called, alloicated node
being released is actually stored into an internal list for later-reuse
without having to access memory allocator. However, nodes from this
list are never freed.

Change-Id: I40e5e28603cde67005d9d92772967b05465ea2b8
2018-12-03 12:59:12 +00:00
Pau Espin Pedrol
39e0bbacca radioInterface: Fix memleak during close()
destructors of pointers created through "new" must be destroyed manually
through "delete".

Change-Id: I10d37579f16bec89cc762f200a8951218305c708
2018-12-03 12:20:07 +01:00
Pau Espin Pedrol
b4ea7b5211 lms: Destroy streams on device stop
They are recreated during start(). Actually, if they are not stopped
here, during start() after stop(), LMS_SetupStream() will fail because
it will detect the streams are already opened.

Change-Id: I70d47c287aabdabc5dc1304a942d130aeb10bdc5
2018-12-03 11:34:25 +01:00
Pau Espin Pedrol
69869bd58f lms: Fix start after stop of device
Change-Id: I56358a1d1601853bc6dd2e6bb5f80798d0ba84b2
2018-12-03 11:19:52 +01:00
d0gtail
ebb37693a5 UHDDevice: log exception information on device open failure
Change-Id: Ia84ddcf50cc83f9326b22bfdfb4f259b4e0bc5f1
2018-12-02 20:01:26 +00:00
Harald Welte
55928f23cb lms: Set Rx gain to midpoint, as comment suggests.
So far, the Rx gain was set to 34 dB, wile the comment stated it
would be set to half-point, which is 73/2=36dB.  Let's adjust the
code to match the comment.

Change-Id: Idc646def53b83faf4e6c011fb595fa436e223b32
2018-11-29 13:26:37 +01:00
Harald Welte
0277b58f6d lms: User correct scale factor for transmit samples
Due to (I believe) a copy+paste mistake from the USRP1 code,
we were using only a scale range of up to 9830 when transmitting
samples, rather than the full 16 bit signed integer range up to
32767.

As a result, we were loosing almost two bits (MSBs) of resolution
as well as a lot of transmit power.

This changes the scale factor to 0.707 (1/sqrt(2)).

Please note that the much higher DAC output level means that the analog
gain should be reduced.  The theoretic range of up to 73dB should not
be used, but Lime Microsystems suggest a value of 61..67 dB.  This can
be achieved by using a "osmotrx tx-attenuation" value of 6..12 inside
the osmo-bts-trx configuration file.

Related: OS#3341
Related: OS#3342
Change-Id: I71702feaa11f53e7614a6938a984dd748405474a
2018-11-29 13:23:08 +01:00
Oliver Smith
be8a83f681 build manuals moved here from osmo-gsm-manuals.git
Moved to doc/manuals/, with full commit history, in preceding merge commit.
Now incorporate in the build system.

Build with:

$ autoreconf -fi
$ ./configure --enable-manuals
$ make

Shared files from osmo-gsm-manuals.git are found automatically if
- the repository is checked out in ../osmo-gsm-manuals; or
- if it osmo-gsm-manuals was installed with "make install"; or
- OSMO_GSM_MANUALS_DIR is set.

Related: OS#3385
Change-Id: I2762171af0bf719a34ba12a0c2e4dcc206098beb
2018-11-27 18:36:41 +01:00
Neels Hofmeyr
d9a460dce9 Merge history from osmo-gsm-manuals.git 2018-11-27 18:36:29 +01:00
Pau Espin Pedrol
f570b4af62 osmotrx: Introduce code architecture chapter
Change-Id: I21084e6315d79a1adcb305e12343da218837dc31
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
46560ea254 osmotrx: Create a common chapter for section documenting backends
Change-Id: I6bea1ccca4ce72b92641a36f9f5894ac9a6cae72
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
038ab45137 osmotrx: configuration: Add section to document multi-arfcn feature
Change-Id: Id04c7f7c36d8a8be8145ef8009ece26ef1a7cdff
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
6b50b62b12 osmotrx: Write initial documentation for several supported devices
Change-Id: I902b01ca661416eb9705afac8a34ec8d2fb13789
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
3984256273 osmotrx: Split Device specific section from backend one
Device specific sections will be added to this new chapter.

Change-Id: Id96cbf857e2ef92d9ad2cb58fd18132f981447d7
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
d06172ce32 trx: Add Hardware device support section
Change-Id: I87fc54cf1751f31a3e92d6503625e984f9a6130f
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
762c951faa trx: Add Hardware architecture support section
Change-Id: I38388bcb2ea7c0481052d3bca589cb34965ea7fa
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
e47d7e964a trx: Add reference to project wiki page in overfiew section
Change-Id: I83cbd67909c42f4ed4857834e23c07ad4e2e22aa
2018-11-27 18:36:18 +01:00
Harald Welte
3cee25ac68 vty-ref: Update URI of docbook 5.0 schema
... to match the /etc/xml/catalog file on debian (no "www" in hostname)

Change-Id: Id9f3579c7f2bc3af13fe30b5268f249b6f59ed0d
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
0bd0782a39 Introduce chapter trx_if.adoc and add it to OsmoTRX and OsmoBTS
This chapter defines the protocol used between osmo-trx and
osmo-bts-trx.

Most of the text comes originally from osmo-trx.git/README, as it's the
only known documentation of the protocol other than the code itself.

Change-Id: I56c418eef0f826ae1aadbed5b151fbed241c7885
2018-11-27 18:36:18 +01:00
Pau Espin Pedrol
8072650406 Introduce OsmoTRX manual
Change-Id: I19ee9d6cda02fb7200c96c0ac129e69825b096cd
2018-11-27 18:36:18 +01:00
Neels Hofmeyr
c6541e6c26 Importing history from osmo-gsm-manuals.git 2018-11-27 18:36:03 +01:00
Pau Espin Pedrol
1e2c0105e2 lms: Apply LMS->OSMO log level conversion
Change-Id: I3a8fbcc7cacebc7038c60175a8ae43b21f713cbb
2018-11-23 14:39:51 +01:00
Pau Espin Pedrol
32b3c2e4b2 lms: Use LimeSuite.h log level defines instead of hardcoded values
Change-Id: Iee693da1a0fa1db23deafa8d3845c04171e24799
2018-11-23 14:39:06 +01:00
Pau Espin Pedrol
1c4bbadda6 UHDDevice: setRxGain on chan 0 when using multi-arfcn
When using multi-arfcn feature, several logical channels (arfcn) are multiplxed
into one physical transceiver, as can be seen in
uhd_device::set_channels.

As a result, when multi-arfcn is enabled some properties are actually
shared for those logical channels, and internally mapped to the first
(only existing) channel, and per-channel internal array variables are allocated
accordingly (size() == 1).

When setting RxGain, we need to set the correct existing physical
channel. Same check is done in getRxGain, and then we apply the RxGain correctly and
we avoid outputing an error "Requested non-existent channel" immediatelly after.

Change-Id: I5b02bb1ef6450dc48be7b8058d96a5691847d3cc
2018-11-19 11:47:37 +01:00
Pau Espin Pedrol
08dfe237c8 ChannelizerBase: Fix ASan alloc-dealloc-mismatch
Fixes following Adress Sanitizer warning:
=================================================================
==27120==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x60c00003d900
    #0 0x7f2f216de421 in operator delete(void*, unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cc:151
    #1 0x561641ce18b7 in ChannelizerBase::~ChannelizerBase() (/build/new/out/bin/osmo-trx-uhd+0x4a8b7)
    #2 0x561641ce1cad in Channelizer::~Channelizer() (/build/new/out/bin/osmo-trx-uhd+0x4acad)
    #3 0x561641cd5160 in RadioInterfaceMulti::close() (/build/new/out/bin/osmo-trx-uhd+0x3e160)
    #4 0x561641cd5047 in RadioInterfaceMulti::~RadioInterfaceMulti() (/build/new/out/bin/osmo-trx-uhd+0x3e047)
    #5 0x561641cd5093 in RadioInterfaceMulti::~RadioInterfaceMulti() (/build/new/out/bin/osmo-trx-uhd+0x3e093)
    #6 0x561641ca3fdd in trx_stop() (/build/new/out/bin/osmo-trx-uhd+0xcfdd)
    #7 0x561641ca4b32 in main (/build/new/out/bin/osmo-trx-uhd+0xdb32)
    #8 0x7f2f1f555222 in __libc_start_main (/usr/lib/libc.so.6+0x24222)
    #9 0x561641ca195d in _start (/build/new/out/bin/osmo-trx-uhd+0xa95d)

0x60c00003d900 is located 0 bytes inside of 128-byte region [0x60c00003d900,0x60c00003d980)
allocated by thread T0 here:
    #0 0x7f2f216dcf19 in operator new[](unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cc:93
    #1 0x561641ce142d in ChannelizerBase::init() (/build/new/out/bin/osmo-trx-uhd+0x4a42d)
    #2 0x561641cd5865 in RadioInterfaceMulti::init(int) (/build/new/out/bin/osmo-trx-uhd+0x3e865)
    #3 0x561641ca1cb7 in makeRadioInterface(trx_ctx*, RadioDevice*, int) (/build/new/out/bin/osmo-trx-uhd+0xacb7)
    #4 0x561641ca44c6 in trx_start(trx_ctx*) (/build/new/out/bin/osmo-trx-uhd+0xd4c6)
    #5 0x561641ca4b05 in main (/build/new/out/bin/osmo-trx-uhd+0xdb05)
    #6 0x7f2f1f555222 in __libc_start_main (/usr/lib/libc.so.6+0x24222)

SUMMARY: AddressSanitizer: alloc-dealloc-mismatch /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cc:151 in operator delete(void*, unsigned long)
==27120==HINT: if you don't care about these errors you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
==27120==ABORTING

Change-Id: Ib7c98ae478f319573dd86a0d2cb264f676bb3859
2018-11-01 18:23:32 +01:00
Vadim Yanitskiy
444ff34396 sigProcLib: add a CorrType for extended (11-bit) RACH
This is a preparatory change that enables a possibility to choose
the amount of synch. sequences to be used for Access Burst (RACH)
detection. The VTY flag will be introduced in further changes.

There are two correlation types now:

  - RACH (default) - TS0 only;
  - EXT_RACH - all TS0, TS1, and TS2 together.

Change-Id: Ia4f20524350bb8c380d7e10360758eddae8b03e9
Related: OS#3054
2018-10-24 19:28:47 +00:00
Vadim Yanitskiy
a79bc70737 sigProcLib: introduce both TS1 and TS2 RACH synch. sequences
According to 3GPP TS 05.02, section 5.2.7, there are three
synch. sequences for Access Bursts:

  - TS0: GSM, GMSK (default),
  - TS1: EGPRS, 8-PSK,
  - TS2: EGPRS, GMSK.

Let's prepare everythyng to be able to detect all TS0-3 synch.
sequences, but keep detection of both TS1 and TS2 disabled
until the corresponding VTY option is introduced.

Change-Id: I838c21db29c54f1924dd478c2b34b46b70aab2cd
Related: OS#3054
2018-10-24 19:28:47 +00:00
Oliver Smith
a439fed166 Add long parameters (--help, --version, ...)
Makes osmo-trx-* more consistent with other Osmocom programs, and
allows an unified test for not having "UNKNOWN" in --version.

Related: OS#3578
Change-Id: I90cf01d972aa10b48c59b67a1e7f82a4255ef526
2018-10-23 11:34:43 +00:00
Harald Welte
8fb0c3dce4 trx_validate_config(): Fix validation of rx_sps
Change-Id: I7e932cff59335add09c76caba6f9ac1e7cf69022
Fixes: Coverity CID#188871
2018-10-21 13:48:26 +02:00
Harald Welte
f5e1cf8790 SocketsTest.testReaderIP(): Zero terminate received buffer
Change-Id: Icd144e672ab15cfb0955897dd6eb529c56908eba
2018-10-21 12:14:55 +02:00
Pau Espin Pedrol
e7d267f178 jenkins.sh: Enable build of osmo-trx-lms
Change-Id: Idfa7ce566ad69b181bcadeee7ff023c77ecd4d6b
2018-10-09 14:08:41 +02:00
Pau Espin Pedrol
06f3b622b8 examples: Use logging level 'set-all' instead of 'all'
Change-Id: Ic52ce0defc774acb8e24947ed22cf2a56b8b0bb0
2018-10-05 16:49:23 +02:00
Pau Espin Pedrol
74bcc562a9 Transciever: Log values causing Tx underrun
Change-Id: I68e18075eade55034567d96fc774d00a794afeeb
2018-10-02 17:30:28 +02:00
Pau Espin Pedrol
55dd2aa5ab CommonLibs/Makefile.am: Specify libcommon_la_LIBADD
This way the dependencies are passed after the .la object, which seems
to be the correct order. Some setups may fail to find some symbols from
libosmocore otherwise (OBS i586).

Change-Id: I22c80055bcffd4179a0a8ca76533ba7aaa38c859
2018-10-02 09:34:55 +02:00
Pau Espin Pedrol
5b60c98769 Use pthread_setname_np to name threads
osmo-trx can start a considerable amount of threads that can make
debugging it challenging at least. By using phtread_setname_np, the
system sets a meaningful name to the thread which can be seen while
debugging with gdb or by printing /proc/$pid/task/$tid/comm.

Now we also log system TID when setting the name so we can identify
different tasks in /proc even if pthread_setname_np fails.

Change-Id: I84711739c3e224cb383fd12b6db933785b28209e
2018-09-28 23:17:57 +00:00
Harald Welte
207d8a2624 ensure well-formed example config files
Change-Id: I9be2bab3b88a96e94e463e5ab22f9814bd6de950
2018-09-25 18:51:51 +00:00
Vadim Yanitskiy
92fc186450 device/lms/LMSDevice.cpp: fix compilation warning
The following warning was observed with GCC 4.8.5:

  make[4]: Entering directory `.../osmo-trx/Transceiver52M/device/lms'
    CXX      LMSDevice.lo
  LMSDevice.cpp: In member function 'LMSDevice::writeSamples()':
  LMSDevice.cpp:582:22: warning: 'rc' may be used uninitialized
                        in this function [-Wmaybe-uninitialized]
    samplesWritten += rc;

Let's fix this by zero-initializing 'rc'.

Change-Id: I4b4a061fc12e5fd1db8d1087d8e0c46ff1e23412
2018-09-20 16:17:19 +07:00
Pau Espin Pedrol
4d179abfd0 cosmetic: Use proper whitespace in several for loops
Change-Id: I82bdeb8a3fa38f3d125e8cbccc3eddbf2b8d8f58
2018-09-13 14:27:48 +00:00
Pau Espin Pedrol
6fdfb68b9e Install sample cfg file to /etc/osmocom
Change-Id: Ib871a3cd14386ef6e6b512a3d66e7e7a839295a5
2018-09-12 20:51:29 +02:00
Pau Espin Pedrol
5ac2cb3662 Install systemd services with autotools
Change-Id: Ia1a4fb62dee35737ece1f3501f352501eba2449e
2018-09-12 14:17:26 +02:00
Pau Espin Pedrol
f4ee021b8c jenkins.sh: Add --enable-werror flag to osmo-trx configure step
This way we catch newly introduced compilation warnings in osmo-trx.

Change-Id: I7effaf243fd6e7fe69728cfce5f97e0b5bd4743c
2018-09-10 10:33:39 +02:00
Pau Espin Pedrol
e62555370e Vector: Copy arrays in a sane way for non-trivially copyable types
Avoids this type of compilation warnings:
‘void* memcpy(void*, const void*, size_t)’ writing to an object of non-trivially copyable type ‘class Complex<float>’; use copy-assignment or copy-initialization instead [-Werror=class-memaccess]

Change-Id: I9724454dfb7b87f74f39074e4004580ac3b5fe5c
2018-09-10 10:33:34 +02:00
Pau Espin Pedrol
1f15152968 radioInterfaceMulti:pullBuffer: Sanely convert float array to complex array
Fixes this type of compilation warning:
‘void* memcpy(void*, const void*, size_t)’ writing to an object of non-trivially copyable type ‘class Complex<float>’; use copy-assignment or copy-initialization instead [-Werror=class-memaccess]

Change-Id: Ibb2380a0a335ce798fe87221519fbbebade53054
2018-09-10 10:30:06 +02:00
Pau Espin Pedrol
21ce05c54f cosmetic: Fix trailing whitespace in several files
Change-Id: Ibf5a69f0a3a309e87814635fd903b114fe68890c
2018-09-10 10:30:06 +02:00
Pau Espin Pedrol
5e68cde779 SigProcLib: Use available copyTo Vector API instead of memcopy
This change allows to remove some wrong use of code as per compilation
warning:
osmo-trx/Transceiver52M/sigProcLib.cpp:1266:40: error:
‘void* memcpy(void*, const void*, size_t)’ writing to an object of non-trivially copyable type ‘class Complex<float>’; use copy-assignment or copy-initialization instead [-Werror=class-memaccess]
   midMidamble->size() * sizeof(complex));

Change-Id: Id446711349bec70fa4e7c8efe0f7f9faf7e4f277
2018-09-10 10:30:06 +02:00
Pau Espin Pedrol
1f4a009c67 UHHDDevice: Replace deprecated header uhd/utils/thread_priority.hpp
Fixes compilation warning:
In file included from osmo-trx/Transceiver52M/device/uhd/UHDDevice.cpp:31:
/usr/include/uhd/utils/thread_priority.hpp:10:17: note: #pragma message: This header is deprecated - please use <uhd/utils/thread.hpp> instead.

Header was moved in uhd.git c33928d2bbdd27688c3475e77fc461e7d16eba5a.

Change-Id: I6299df48a5e14c54eaa07288d166c705eb9ebdbe
2018-09-10 10:30:06 +02:00
Harald Welte
544ce0d6b7 update git-version-gen to generate proper version numbers
We don't want the version of the last tagged version, but the version
number uniquely representing the current HEAD.  Use the script from
libosmocore.

I suspect that this somehow got broken in commit 00d5114717

Related: OS#3517
Change-Id: Iba3212aa417dce4240c5c27eb4f12afcd9c95e5b
2018-09-04 18:01:35 +00:00
Pau Espin Pedrol
b148d05542 configure.ac: Specify default language as C++
This is useful if we add more AC_CHECK_HEADER or similar configure tests including
C++ header files or required C++ features, since otherwise gcc is used
by default and test fail.

Change-Id: Iee757c78b72290c5d2a4c31339800a4e72b6be23
2018-09-04 18:00:57 +00:00
Pau Espin Pedrol
970096932e radioInterface: Fix variable storing integer return value
Change-Id: I0a0a06a6d16a228cfcb7bd746bab2d79f10ce244
2018-09-04 16:35:27 +02:00
Pau Espin Pedrol
db936b9b55 osmo-trx: Add osmo_signal to stop whole transceiver chain correctly on error
Transceiver::stop() can only be called from either CTRL iface thread or
from main thread (running osmocom loop). That's because stop attempts to
cancel and then join all the other threads, which would then lock if
attempting to stop from some of them.
As a result, the best option is to indicate to the user of the
transceiver option (osmo-trx.cpp) to stop it in a correct fashion by
destroying the object from the main thread.

Change-Id: Iac1d2dbe2328e735db2d4b933cb67b1af1babca1
2018-09-04 16:35:23 +02:00
Pau Espin Pedrol
49ad759072 lms: Return error on device read timeout
If LImeSDR device is unplugged or its fw crashes during operation,
reading from the device will fail and will first receive short reads and
finally 0 byte reads. Let's quickly notify these events to upper layers
instead of trying to process the buffer and checking timestamps for
something we know it's already not useful.

Related: OS#3340
Change-Id: Ib1af8cdd6cdadf581b039882add4049eea45a0f7
2018-09-03 18:53:52 +02:00
Pau Espin Pedrol
8e498bfd35 radioInterface: forward errors from RadioDevice to Transceiver in recv path
Change-Id: Id7b08b19d6575c79b4d57db656a17ff05bb61ee9
2018-09-03 18:53:52 +02:00
Pau Espin Pedrol
46444637c6 cosmetic: Fix trailing whitespace
Change-Id: Ia647cfed0acb35adeb9b3b7824170d06c0369ef7
2018-09-03 18:53:41 +02:00
Pau Espin Pedrol
86be40b4eb Logger: Disable pthread cancel point inside Logger destructor
pthread_cancel is implemented in c++ using exception handlers. In
destructor of Log object, the log function is called which will
eventually call fputs() to write to a file. Since that function is
considered a cancelation point, if pthread_cancel has been called the
exception handler will start unstacking frames and calling destructors
in the process. At some point this will cause a runtime exception in c++
which will call std::terminate() to abort the process.

The solution is thus to avoid starting the cancellation process inside the
destructor.

This behavior was spotted while calling the destructor of Transceiver
object in forthcoming patches.

See a more detailed example here:
https://skaark.wordpress.com/2010/08/26/pthread_cancel-considered-harmful/

Change-Id: I71ca90f3fbc73df58b878a03361f7b7831d838b4
2018-09-03 15:22:53 +02:00
Pau Espin Pedrol
288d8af070 configure.ac: Add --enable-werror option
Change-Id: Ib2782aecd400398bf36427f255c2a427ef781e06
2018-08-30 17:30:15 +02:00
Pau Espin Pedrol
aae403f0c9 vty: Fix typo in gpsdo clock reference type
Change-Id: I3f553c2cec9689524728cacb15b7daaff8166925
2018-08-27 16:50:19 +02:00
Harald Welte
5cc8858d8f logging: Introduce new "DDEV" category for device-specific code
The DMAIN category got too overloaded.  Let's have the code in
Transceive52M/device/* use the new DDEV category.

Also, in some cases the log levels have been adjusted to ensure
that enabling INFO level should not result in a complete overflow
of messages during normal operation.

Change-Id: I844fe4a75bf277cd3cc5bd8fa06e06ad97b2ea95
2018-08-17 19:57:40 +02:00
Pau Espin Pedrol
70d0344b31 configure.ac: Fix typo in with-lms help string
Change-Id: I7777d027fffa50dddf3f0a3c0bf2173aa8497be3
2018-08-17 17:17:05 +02:00
Alexander Couzens
62002fb8ac debian: add patches for debian8
The osmocom-nightly/release can create debian8 package by patching
rules and control files

Change-Id: I261302d2ed16e76540073589504e7426e23d00a1
2018-08-09 05:02:36 +02:00
Harald Welte
03b3c30533 Fix config file saving of {tx,rx}-path VTY config strings
We were missing one indent level when writing the rx-path and tx-path

Change-Id: I5d5b02c71d39220cabc2a23d059908ef3c6350e0
Closes: OS#3435
2018-07-31 15:48:18 +02:00
Pau Espin Pedrol
a4b569d936 debian: Explicitly enable osmo-trx-uhd build
osmotrxuhd is already being built since it's enabled by default, but
let's make it more explicit that we are building it too.

Change-Id: Ie9c224485cce047cd3ee4600ff7fbdb082355cdc
2018-06-25 17:02:22 +02:00
Pau Espin Pedrol
eb0945d85d debian: Enable build of osmo-trx-lms
Change-Id: I4711e97c844e582601a588fdc359fc020bcee001
2018-06-25 16:09:25 +02:00
Pau Espin Pedrol
f968507912 Use correct paths when installing example files
Fixes: c7756e73b7
Change-Id: If55d14768727c7119d86da60413861674dd3538b
2018-06-25 12:57:17 +02:00
Pau Espin Pedrol
58c89fb8d6 lms: Allow values diff than 34dB to be set by setRxGain()
Until now, setRxGain in LMSDevice did not take into account the setter
parameter and was always using hardcoded 34dB, which was experimentally
found to be a good default value.

Let's force that value during initialization, but still allow the upper
layers (controlled by BTS) to set different values. osmo-bts only sends
a SETRXGAIN command (which calls setRxGain in osmo-trx) if a value is
explicitly set in its VTY config, so we are on the safe side if the user
doesn't explicitly configure a desired dB.

Change-Id: I5684e675281a3f581855dbb56d199a6fe238a712
2018-06-20 07:41:16 +00:00
Pau Espin Pedrol
16e7e20f85 Add -V param to print version
Change-Id: I9f2d6c4b1a508aceb1ccc0559f0902eedf2ec5af
2018-06-20 07:38:36 +00:00
Pau Espin Pedrol
c7756e73b7 debian: Add cfg file examples for osmo-trx-{lms,uhd}
Sort cfg files according to their osmo-trx binary.
Install them during make install.
Add the installed cfg files to related debian packages.

Change-Id: I905cdac30b441e4df0a3f5c0924d1637b9f67b90
2018-06-19 11:31:45 +02:00
Pau Espin Pedrol
7ed686b223 contrib: Add systemd services for all backends
Change-Id: I38a7ff7b9dafc3d6aa0426777036e3d7666045a7
2018-06-19 11:05:04 +02:00
Pau Espin Pedrol
2ab6ddb0de debian: Add package osmo-trx-lms
Change-Id: I3213c66907fbf0c7e531835b4993fa1bdc89edc3
2018-06-19 11:05:04 +02:00
Harald Welte
b229439b31 usrp1: Fail in case of unsupported configuration
There might be some configuration that's not supported by osmo-bts-usrp1,
and we should reject that properly.

Change-Id: Ic7308ce0c57439fe97668bd31801c4bf76b797ad
Closes: OS#3348
2018-06-14 12:38:09 +02:00
Harald Welte
ffb3301bd8 lms: Fail in case of unsupported configuration
There might be some configuration that's not supported by osmo-bts-lms,
and we should reject that properly.

Change-Id: I6f82edce589030a4407f6150fb7e8abe6417c1f2
Closes: OS#3347
2018-06-14 12:38:09 +02:00
Harald Welte
62b7900fd7 lms: Fix coding style
In Change-Id Ib2fca81b76d027b08e2891056fa076d071597783 we introduced
some coding style violations.  Let's make newly-added code follows
standard Osmocom coding style.

Change-Id: Ib7ddd275014f03a2eed3cddc02b1356e2b00c0bc
2018-06-14 12:38:09 +02:00
Harald Welte
61707e8b23 radioDevice: better encapsulation in base class
It's not good style to have the derived classes initialize members
inherited from the base class using "this->foo = bar".  Rather, let's
make the base class have a constructor, and call that constructor to
initialize the members of the base class.

While doing this
* rename 'offset' to 'lo_offset' to avoid confusion with timestamp offset
* move 'InterfaceType' into the base class
* move 'chans' into the base class
* move 'rx_sps' into the base class
* mark base class members as 'protected'

Change-Id: Ib885675a7612a392aa7f75fca81269ddcff2f6ab
2018-06-14 12:38:04 +02:00
Harald Welte
ce70ba529c radioDevice: Move tx_sps from derived into base class
All three derived classes use a tx_sps member, let's move this into
the base class.

Change-Id: I73b4aa2705c5049561e2d7b21301a0d2b3c96ced
2018-06-13 23:46:27 +02:00
Harald Welte
cfb9dac70d lms: Call set_antennas() during open() method
Without this call, the antenna/path configuration is not applied.

Change-Id: I0bca58266b59f1315ec72b6407fe4f4495aff678
2018-06-13 23:46:27 +02:00
Harald Welte
105a61e689 lms: Fix support for rx_paths / tx_paths
Before this patch, any configuration in osmo-trx.cfg regarding the rx
and tx "antenna" (path) would have been completely ignored, as the
radioDevice::make() function would simply drop those arguments to the
floor.

Change-Id: Ie50f854abbc9dcf351cddc052d10206382e1d5d3
2018-06-13 23:46:27 +02:00
Harald Welte
2407314f2e move set_antennas() from UHD to generic radioDevice base class
Change-Id: I806143e9db21f0be4dcc6a376b3a630be7aeb3ba
2018-06-13 23:46:27 +02:00
Zydrunas Tamosevicius
ff4418539c lms: Reduce Rx gain from 47 to 34 dB
Initially, Rx gain was hardcoded to be 47. This was too high for our
setup and we were constantly getting "clipping detected" messages.

Reducing Rx gain to 34 solved the issue. However, it looks like gains
should be controlled through configuration files.

Change-Id: I30580f18c4ad630c09f725b1d24c125fc3119809
2018-06-13 21:45:32 +00:00
Zydrunas Tamosevicius
0494d05916 lms: fix LMS_StartStream() handling for multiple channels
LMS_StartStream() (in LMSDevice::start()) was moved to separate loop. It
is because LMS_SetupStream() would fail for second channel if streaming
has already been started (LMS_StartStream()) for single channel
configuration.

Change-Id: I6704bb92864aa81417507c4ae24a22f41dc529c1
2018-06-13 21:45:32 +00:00
Zydrunas Tamosevicius
fd268b6f5a lms: Reduce log level of "send buffer of len ..."
Log level of "send buffer of len ..." messages was changed as it was
causing problems on some machines.

Change-Id: I605d50e81966c7bd169b27788d62af6fb54c84e1
2018-06-13 21:45:32 +00:00
Zydrunas Tamosevicius
43f3678b40 lms: Use same timestamp offset like when using LimeSDR via UHD
The tx timestamp offset was not set. We set it to the same value as it
was in UHD interface for LimeSDR

Change-Id: I78bc40cd575097f71a5f82b63467fa81c3f8d837
2018-06-13 21:45:32 +00:00
Pau Espin Pedrol
6493dc838b lms: Check LPBFW to set is within supported range
As of LimeSuite 618fbb9c3188b36d75ad5785a97b8887dcc468f6, it seems 5e6
is within the returned range, but LMS_SetLPFBW fails anyway.

See for more information: https://github.com/myriadrf/LimeSuite/issues/184

Change-Id: I967e7da7c0e3e8138b76733ee4a0e6311d20b62e
2018-06-13 21:45:32 +00:00
Pau Espin Pedrol
f7905ac548 lms: Makefile.am: Reorder params to fix link issue
It seems the order in which static code and -lfoo is passed to the
linker matters.

This commit is a lms specific follow-up of commit
2a8183bdf0.

Change-Id: I59c20d268ecac4c22689124165c47295bd9176d4
2018-06-13 21:45:32 +00:00
Pau Espin Pedrol
0c27938d44 LMSDevice: Fix setup failure with LimeSuite > 18.04.1
Fixes: https://github.com/myriadrf/LimeSuite/issues/184

Change-Id: Ia9f37995cd10d19d6820e3e12b8ee8f3efbff5d4
2018-06-13 21:45:32 +00:00
Pau Espin Pedrol
587916e810 LMSDevice: Set correct values for Max{Tx,Rx}Gain
Change-Id: I3b3a7080a69e15d8d6770186810d922227439099
2018-06-13 21:45:32 +00:00
Harald Welte
79024867de LMSDevice: Reduce Rx logging verbosity: Only log unexpected timestamps
Change-Id: I06b35efb7368616b9f4d348da574cd524ffe3ea6
2018-06-13 21:45:32 +00:00
Harald Welte
4aec1f1cf5 LMS_Device: Set ts_offset to 0.
I'm not quite sure what the ts_offset is for, but by using "0"
we are now receiving exactly the timestamp that we're expecting:

LMSDevice.cpp:486 [tid=140576250332928] chan 0 recv buffer of len 2500 expect 305ed0 got 305ed0 (305ed0) diff=0

Change-Id: I270c94945b1af9662cfc468cfda1ae3af3ac0a27
2018-06-13 21:45:32 +00:00
Harald Welte
68f054169b LMSDevice: Fix initial timestamp offset of 2500
ts_initial must not point to the timestamp of the first sample
in the last "flush" sample buffer, but to the first timestamp we
expect in the next buffer.

Change-Id: I23af62870544d4c6cf5f6e2d6578936603bceb91
2018-06-13 21:45:32 +00:00
Harald Welte
ea8be22e50 LMSDevice: Set low-pass filters to smallest possible option
Rx 1.4 MHz, Tx 5MHz.  Both massively too wide for GSM, but there's
no smaller band-width available.

Change-Id: I9723c9a2ea77f65bfa9d796d7c44adc2417e89cf
2018-06-13 21:45:32 +00:00
Harald Welte
a5054b398b LMSDevice: Typo fix: s/Internal/External
Change-Id: Icacfe6da90a89c7f00d62c580948fb913998eaa7
2018-06-13 21:45:32 +00:00
Harald Welte
a438114173 LMSDevice: Print sample rate range + actual sample rate after setting it
Change-Id: I19c1a5b2d2431b8d39e277244e313f6e559e4d25
2018-06-13 21:45:32 +00:00
Harald Welte
9cb4f27112 LMSDevice: Call LMS_Init() before setting sample rate
LMS_Init() will override basically all device settings with their
default value, including the sample rate.  We hence have to make sure
to call it before any other API function that changes the device config
such as sample rate, frequency, filter bandwidth, ...

Change-Id: I4cdbae8406b5e1e93da491e90f8bad41d4be748b
2018-06-13 21:45:32 +00:00
Harald Welte
c38e45e9dc update .gitignore to include osmo-trx-lms
Change-Id: I52efd2f71eb61baa80427ab9f7b518f17d514792
2018-06-13 21:45:32 +00:00
Pau Espin Pedrol
1f50fedb5f build: Add support for LimeSuite device backend
Change-Id: I239e1b37263a62b374d84974c9347e3654072e87
2018-06-13 21:45:32 +00:00
Pau Espin Pedrol
c7a0bf1ffc lms: Several improvements and compilation/runtime fixes
Continuation of initial work done on LimeSuite support from Harald.

Change-Id: Ib2fca81b76d027b08e2891056fa076d071597783
2018-06-13 21:45:32 +00:00
Harald Welte
940738e86a Initial work towards direct LimeSuite support in OsmoTRX
This is work in progress towards a direct LimeSuite driver in OsmoTRX,
bypassing the currently rather complex stack of wrappers by going
through UHD, SoapyUHD, SoapySDR and LimeSuite.

Change-Id: Iaef29c4c2585ef8c2f94866c9591919f538c1a2d
2018-06-13 21:45:32 +00:00
Pau Espin Pedrol
8c1e2bddff examples: Set rt-prio 18 and print file basename
Change-Id: I16fbdd46f2d9b6f3e79a4bb357f6a7fbed14244a
2018-06-12 18:23:40 +02:00
Vadim Yanitskiy
01eea0aa42 trx_vty.c: fix: use CONFIG_NODE as parent by default
There are some configuration nodes, which are handled by extenral
libraries, such as libosmoctrl. So, when switching back to the
parent node, this should be kept in mind.

Instead of aborting, let's got to the CONFIG_NODE by default.

Fixes: OS#3250
Change-Id: Ia0600a46d19825806e5aed9257b6c57c3907808b
2018-05-09 15:19:56 +07:00
Pau Espin Pedrol
55df1e43e3 UHDDevice: Fix setup failure with LimeSuite > 18.04.1
Fixes: https://github.com/myriadrf/LimeSuite/issues/184

Change-Id: I48ead8b8996981263297b66c0c7d3d0972261316
2018-05-08 20:49:04 +02:00
Pau Espin Pedrol
e9424e241f doc: examples: Add umtrx sample config
Change-Id: Id38de0bbbe75e5e6bbb0de2eecb7d1984786d528
2018-05-04 14:01:50 +00:00
107 changed files with 5809 additions and 1174 deletions

13
.gitignore vendored
View File

@@ -4,6 +4,7 @@
*.la
Transceiver52M/osmo-trx-uhd
Transceiver52M/osmo-trx-usrp1
Transceiver52M/osmo-trx-lms
# tests
tests/CommonLibs/BitVectorTest
@@ -17,6 +18,7 @@ tests/CommonLibs/URLEncodeTest
tests/CommonLibs/VectorTest
tests/CommonLibs/PRBSTest
tests/Transceiver52M/convolve_test
tests/Transceiver52M/LMSDeviceTest
# automake/autoconf
*.in
@@ -50,3 +52,14 @@ tests/testsuite.log
# vim
*.sw?
# manuals
doc/manuals/*.html
doc/manuals/*.svg
doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build

View File

@@ -29,6 +29,25 @@
#include "LinkedLists.h"
PointerFIFO::~PointerFIFO()
{
ListNode *node, *next;
node = mHead;
while (node != NULL) {
next = node->next();
delete node;
node = next;
}
node = mFreeList;
while (node != NULL) {
next = node->next();
delete node;
node = next;
}
}
void PointerFIFO::push_front(void* val) // by pat
{
// Pat added this routine for completeness, but never used or tested.

View File

@@ -70,6 +70,7 @@ class PointerFIFO {
:mHead(NULL),mTail(NULL),mFreeList(NULL),
mSize(0)
{}
~PointerFIFO();
unsigned size() const { return mSize; }
unsigned totalSize() const { return 0; } // Not used in this version.

View File

@@ -44,6 +44,8 @@ std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
Log::~Log()
{
int old_state;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
const char *fmt = neednl ? "%s\n" : "%s";
@@ -51,6 +53,7 @@ Log::~Log()
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
pthread_setcancelstate(old_state, NULL);
}
ostringstream& Log::get()

View File

@@ -53,6 +53,12 @@ extern "C" {
#define LOGC(category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
#define LOGLV(category, level) \
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
#define LOGCHAN(chan, category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "][chan=" << chan << "] "
/**
A C++ stream-based thread-safe logger.
This object is NOT the global logger;

View File

@@ -34,8 +34,10 @@ libcommon_la_SOURCES = \
Threads.cpp \
Timeval.cpp \
Logger.cpp \
Utils.cpp \
trx_vty.c \
debug.c
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
noinst_HEADERS = \
BitVector.h \
@@ -47,6 +49,8 @@ noinst_HEADERS = \
Timeval.h \
Vector.h \
Logger.h \
Utils.h \
trx_vty.h \
debug.h \
osmo_signal.h \
config_defs.h

View File

@@ -24,11 +24,17 @@
*/
#include <string.h>
#include <sys/types.h>
#include "Threads.h"
#include "Timeval.h"
#include "Logger.h"
#ifndef gettid
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif
using namespace std;
@@ -102,6 +108,25 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
}
void set_selfthread_name(const char *name)
{
pthread_t selfid = pthread_self();
pid_t tid = gettid();
if (pthread_setname_np(selfid, name) == 0) {
LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
} else {
char buf[256];
int err = errno;
char* err_str = strerror_r(err, buf, sizeof(buf));
LOG(NOTICE) << "Thread "<< selfid << " (task " << tid << ") set name \"" << name << "\" failed: (" << err << ") " << err_str;
}
}
void thread_enable_cancel(bool cancel)
{
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}
void Thread::start(void *(*task)(void*), void *arg)
{

View File

@@ -55,7 +55,7 @@ void unlockCout(); ///< call after writing cout
#define OBJDCOUT(text) {}
#else
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
#endif
//@}
//@}
@@ -141,6 +141,9 @@ class Signal {
#define START_THREAD(thread,function,argument) \
thread.start((void *(*)(void*))function, (void*)argument);
void set_selfthread_name(const char *name);
void thread_enable_cancel(bool cancel);
/** A C++ wrapper for pthread threads. */
class Thread {
@@ -150,7 +153,7 @@ class Thread {
pthread_attr_t mAttrib;
// FIXME -- Can this be reduced now?
size_t mStackSize;
public:

View File

@@ -27,43 +27,49 @@
#include "Timeval.h"
extern "C" {
#include <osmocom/core/timer.h>
}
using namespace std;
void Timeval::now()
{
osmo_clock_gettime(CLOCK_REALTIME, &mTimespec);
}
void Timeval::future(unsigned offset)
{
now();
unsigned sec = offset/1000;
unsigned msec = offset%1000;
mTimeval.tv_usec += msec*1000;
mTimeval.tv_sec += sec;
if (mTimeval.tv_usec>1000000) {
mTimeval.tv_usec -= 1000000;
mTimeval.tv_sec += 1;
mTimespec.tv_nsec += msec*1000*1000;
mTimespec.tv_sec += sec;
if (mTimespec.tv_nsec > 1000*1000*1000) {
mTimespec.tv_nsec -= 1000*1000*1000;
mTimespec.tv_sec += 1;
}
}
struct timespec Timeval::timespec() const
{
struct timespec retVal;
retVal.tv_sec = mTimeval.tv_sec;
retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
return retVal;
return mTimespec;
}
bool Timeval::passed() const
{
Timeval nowTime;
if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
if (nowTime.mTimespec.tv_sec < mTimespec.tv_sec) return false;
if (nowTime.mTimespec.tv_sec > mTimespec.tv_sec) return true;
if (nowTime.mTimespec.tv_nsec >= mTimespec.tv_nsec) return true;
return false;
}
double Timeval::seconds() const
{
return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
return ((double)mTimespec.tv_sec) + 1e-9*((double)mTimespec.tv_nsec);
}
@@ -72,8 +78,8 @@ long Timeval::delta(const Timeval& other) const
{
// 2^31 milliseconds is just over 4 years.
int32_t deltaS = other.sec() - sec();
int32_t deltaUs = other.usec() - usec();
return 1000*deltaS + deltaUs/1000;
int32_t deltaNs = other.nsec() - nsec();
return 1000*deltaS + deltaNs/1000000;
}
@@ -89,7 +95,7 @@ ostream& operator<<(ostream& os, const Timeval& tv)
ostream& operator<<(ostream& os, const struct timespec& ts)
{
os << ts.tv_sec << "," << ts.tv_nsec;
os << ts.tv_sec << "," << ts.tv_nsec/1000;
return os;
}

View File

@@ -42,12 +42,12 @@ class Timeval {
private:
struct timeval mTimeval;
struct timespec mTimespec;
public:
/** Set the value to gettimeofday. */
void now() { gettimeofday(&mTimeval,NULL); }
/** Set the value to current time. */
void now();
/** Set the value to gettimeofday plus an offset. */
void future(unsigned ms);
@@ -55,16 +55,18 @@ class Timeval {
//@{
Timeval(unsigned sec, unsigned usec)
{
mTimeval.tv_sec = sec;
mTimeval.tv_usec = usec;
mTimespec.tv_sec = sec;
mTimespec.tv_nsec = usec*1000;
}
Timeval(const struct timeval& wTimeval)
:mTimeval(wTimeval)
{}
{
mTimespec.tv_sec = wTimeval.tv_sec;
mTimespec.tv_nsec = wTimeval.tv_sec*1000;
}
/**
Create a Timeval offset into the future.
Create a Timespec offset into the future.
@param offset milliseconds
*/
Timeval(unsigned offset=0) { future(offset); }
@@ -76,8 +78,9 @@ class Timeval {
/** Return total seconds. */
double seconds() const;
uint32_t sec() const { return mTimeval.tv_sec; }
uint32_t usec() const { return mTimeval.tv_usec; }
uint32_t sec() const { return mTimespec.tv_sec; }
uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
uint32_t nsec() const { return mTimespec.tv_nsec; }
/** Return differnce from other (other-self), in ms. */
long delta(const Timeval& other) const;
@@ -88,11 +91,11 @@ class Timeval {
/** Remaining time in ms. */
long remaining() const { return -elapsed(); }
/** Return true if the time has passed, as per gettimeofday. */
/** Return true if the time has passed, as per clock_gettime(CLOCK_REALTIME). */
bool passed() const;
/** Add a given number of minutes to the time. */
void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; }
void addMinutes(unsigned minutes) { mTimespec.tv_sec += minutes*60; }
};

36
CommonLibs/Utils.cpp Normal file
View File

@@ -0,0 +1,36 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <vector>
#include <string>
#include <sstream>
std::vector<std::string> comma_delimited_to_vector(const char* opt)
{
std::string str = std::string(opt);
std::vector<std::string> result;
std::stringstream ss(str);
while( ss.good() )
{
std::string substr;
getline(ss, substr, ',');
result.push_back(substr);
}
return result;
}

24
CommonLibs/Utils.h Normal file
View File

@@ -0,0 +1,24 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <vector>
#include <string>
std::vector<std::string> comma_delimited_to_vector(const char* opt);

View File

@@ -32,11 +32,14 @@
#include <string.h>
#include <iostream>
#include <assert.h>
#include <stdlib.h>
// We cant use Logger.h in this file...
extern int gVectorDebug;
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
typedef void (*vector_free_func)(void* wData);
typedef void *(*vector_alloc_func)(size_t newSize);
/**
A simplified Vector template with aliases.
@@ -60,6 +63,8 @@ template <class T> class Vector {
T* mData; ///< allocated data block, if any
T* mStart; ///< start of useful data
T* mEnd; ///< end of useful data + 1
vector_alloc_func mAllocFunc; ///< function used to alloc new mData during resize.
vector_free_func mFreeFunc; ///< function used to free mData.
public:
@@ -85,9 +90,19 @@ template <class T> class Vector {
/** Change the size of the Vector, discarding content. */
void resize(size_t newSize)
{
if (mData!=NULL) delete[] mData;
if (mData!=NULL) {
if (mFreeFunc)
mFreeFunc(mData);
else
delete[] mData;
}
if (newSize==0) mData=NULL;
else mData = new T[newSize];
else {
if (mAllocFunc)
mData = (T*) mAllocFunc(newSize);
else
mData = new T[newSize];
}
mStart = mData;
mEnd = mStart + newSize;
}
@@ -107,7 +122,7 @@ template <class T> class Vector {
void clone(const Vector<T>& other)
{
resize(other.size());
memcpy(mData,other.mStart,other.bytes());
other.copyTo(*this);
}
@@ -116,29 +131,31 @@ template <class T> class Vector {
//@{
/** Build an empty Vector of a given size. */
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
{ resize(wSize); }
/** Build a Vector by moving another. */
Vector(Vector<T>&& other)
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc)
{ other.mData=NULL; }
/** Build a Vector by copying another. */
Vector(const Vector<T>& other):mData(NULL) { clone(other); }
Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); }
/** Build a Vector with explicit values. */
Vector(T* wData, T* wStart, T* wEnd)
:mData(wData),mStart(wStart),mEnd(wEnd)
Vector(T* wData, T* wStart, T* wEnd, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(wData),mStart(wStart),mEnd(wEnd), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
{ }
/** Build a vector from an existing block, NOT to be deleted upon destruction. */
Vector(T* wStart, size_t span)
:mData(NULL),mStart(wStart),mEnd(wStart+span)
Vector(T* wStart, size_t span, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(NULL),mStart(wStart),mEnd(wStart+span),mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
{ }
/** Build a Vector by concatenation. */
Vector(const Vector<T>& other1, const Vector<T>& other2)
:mData(NULL)
Vector(const Vector<T>& other1, const Vector<T>& other2, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
{
resize(other1.size()+other2.size());
memcpy(mStart, other1.mStart, other1.bytes());
@@ -162,6 +179,8 @@ template <class T> class Vector {
mData=other.mData;
mStart=other.mStart;
mEnd=other.mEnd;
mAllocFunc=other.mAllocFunc;
mFreeFunc=other.mFreeFunc;
other.mData=NULL;
}
@@ -204,10 +223,15 @@ template <class T> class Vector {
*/
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
{
T* base = other.mStart + start;
assert(base+span<=other.mEnd);
unsigned int i;
T* dst = other.mStart + start;
T* src = mStart;
assert(dst+span<=other.mEnd);
assert(mStart+span<=mEnd);
memcpy(base,mStart,span*sizeof(T));
for (i = 0; i < span; i++, src++, dst++)
*dst = *src;
/*TODO if not non-trivially copyable type class, optimize:
memcpy(dst,mStart,span*sizeof(T)); */
}
/** Copy all of this Vector to a segment of another Vector. */
@@ -282,7 +306,7 @@ template <class T> class Vector {
T* end() { return mEnd; }
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
//@}
};

View File

@@ -10,6 +10,24 @@ static const struct log_info_cat default_categories[] = {
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXCTRL] = {
.name = "DTRXCTRL",
.description = "TRX CTRL interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DDEV] = {
.name = "DDEV",
.description = "Device/Driver specific code",
.color = NULL,
.enabled = 1, .loglevel = LOGL_INFO,
},
[DLMS] = {
.name = "DLMS",
.description = "Logging from within LimeSuite itself",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {

View File

@@ -5,4 +5,7 @@ extern const struct log_info log_info;
/* Debug Areas of the code */
enum {
DMAIN,
DTRXCTRL,
DDEV,
DLMS,
};

35
CommonLibs/osmo_signal.h Normal file
View File

@@ -0,0 +1,35 @@
/* Generic signalling/notification infrastructure */
/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/signal.h>
/* Signalling subsystems */
enum signal_subsystems {
SS_TRANSC,
};
/* SS_TRANSC signals */
enum SS_TRANSC {
S_TRANSC_STOP_REQUIRED, /* Transceiver fatal error, it should be stopped */
};

View File

@@ -39,7 +39,7 @@ static struct trx_ctx* g_trx_ctx;
static const struct value_string clock_ref_names[] = {
{ REF_INTERNAL, "internal" },
{ REF_EXTERNAL, "external" },
{ REF_GPS, "gspdo" },
{ REF_GPS, "gpsdo" },
{ 0, NULL }
};
@@ -236,12 +236,16 @@ DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
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_SUCCESS;
}
if (trx->cfg.num_chans > TRX_MCHAN_MAX) {
vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s",
TRX_MCHAN_MAX, VTY_NEWLINE);
return CMD_WARNING;
}
trx->cfg.multi_arfcn = true;
return CMD_SUCCESS;
}
@@ -304,6 +308,21 @@ DEFUN(cfg_egprs, cfg_egprs_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
"ext-rach (disable|enable)",
"Enable extended (11-bit) RACH (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0)
trx->cfg.ext_rach = false;
if (strcmp("enable", argv[0]) == 0)
trx->cfg.ext_rach = true;
return CMD_SUCCESS;
}
DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
"rt-prio <1-32>",
"Set the SCHED_RR real-time priority\n"
@@ -339,7 +358,12 @@ DEFUN(cfg_chan, cfg_chan_cmd,
if (idx >= TRX_CHAN_MAX) {
vty_out(vty, "Chan list full.%s", VTY_NEWLINE);
return CMD_WARNING;
} else if (trx->cfg.multi_arfcn && trx->cfg.num_chans >= TRX_MCHAN_MAX) {
vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s",
TRX_MCHAN_MAX, VTY_NEWLINE);
return CMD_WARNING;
}
if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */
vty_out(vty, "Non-existent or non-consecutive chan %d.%s",
idx, VTY_NEWLINE);
@@ -417,6 +441,7 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE);
vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE);
vty_out(vty, " ext-rach %s%s", trx->cfg.ext_rach ? "enable" : "disable", VTY_NEWLINE);
if (trx->cfg.sched_rr != 0)
vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE);
@@ -424,9 +449,9 @@ static int config_write_trx(struct vty *vty)
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);
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);
vty_out(vty, " tx-path %s%s", chan->tx_path, VTY_NEWLINE);
}
return CMD_SUCCESS;
@@ -454,6 +479,7 @@ static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Extended RACH support: %s%s", trx->cfg.ext_rach ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr,
trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE);
@@ -503,7 +529,9 @@ static int trx_vty_go_parent(struct vty *vty)
vty->index_sub = NULL;
break;
default:
OSMO_ASSERT(0);
vty->node = CONFIG_NODE;
vty->index = NULL;
vty->index_sub = NULL;
}
return vty->node;
@@ -562,6 +590,7 @@ int trx_vty_init(struct trx_ctx* trx)
install_element(TRX_NODE, &cfg_rssi_offset_cmd);
install_element(TRX_NODE, &cfg_swap_channels_cmd);
install_element(TRX_NODE, &cfg_egprs_cmd);
install_element(TRX_NODE, &cfg_ext_rach_cmd);
install_element(TRX_NODE, &cfg_rt_prio_cmd);
install_element(TRX_NODE, &cfg_filler_cmd);

View File

@@ -6,7 +6,10 @@
extern struct vty_app_info g_vty_info;
/* Maximum number of physical RF channels */
#define TRX_CHAN_MAX 8
/* Maximum number of carriers in multi-ARFCN mode */
#define TRX_MCHAN_MAX 3
/* Samples-per-symbol for downlink path
* 4 - Uses precision modulator (more computation, less distortion)
@@ -57,6 +60,7 @@ struct trx_ctx {
double offset;
double rssi_offset;
bool swap_channels;
bool ext_rach;
bool egprs;
unsigned int sched_rr;
unsigned int num_chans;

View File

@@ -54,7 +54,10 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
// |-head-||---------midamble----------------------||--------------data----------------||t|
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");

View File

@@ -52,7 +52,9 @@ extern const BitVector gEdgeTrainingSequence[];
extern const BitVector gDummyBurst;
/** Random access burst synch. sequence */
extern const BitVector gRACHSynchSequence;
extern const BitVector gRACHSynchSequenceTS0;
extern const BitVector gRACHSynchSequenceTS1;
extern const BitVector gRACHSynchSequenceTS2;
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
extern const BitVector gRACHBurst;

View File

@@ -28,9 +28,11 @@ AM_CXXFLAGS = -Wall -pthread
# Order must be preserved
SUBDIRS = \
doc \
CommonLibs \
GSM \
Transceiver52M \
contrib \
tests
EXTRA_DIST = \
@@ -40,6 +42,9 @@ EXTRA_DIST = \
COPYING \
README
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
.PHONY: release
@RELMAKE@

View File

@@ -88,7 +88,7 @@ bool Channelizer::rotate(const float *in, size_t len)
convolve_real(hInputs[i], blockLen,
subFilters[i], hLen,
hOutputs[i], blockLen,
0, blockLen, 1, 0);
0, blockLen);
}
cxvec_fft(fftHandle);

View File

@@ -239,7 +239,7 @@ ChannelizerBase::~ChannelizerBase()
for (size_t i = 0; i < m; i++) {
free(subFilters[i]);
delete hist[i];
delete[] hist[i];
}
fft_free(fftInput);

View File

@@ -23,7 +23,7 @@ include $(top_srcdir)/Makefile.common
SUBDIRS = arch device
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
rev2dir = $(datadir)/usrp/rev2
@@ -99,3 +99,13 @@ osmo_trx_usrp1_LDADD = \
$(USRP_LIBS)
osmo_trx_usrp1_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS)
endif
if DEVICE_LMS
bin_PROGRAMS += osmo-trx-lms
osmo_trx_lms_SOURCES = osmo-trx.cpp
osmo_trx_lms_LDADD = \
$(builddir)/device/lms/libdevice.la \
$(COMMON_LDADD) \
$(LMS_LIBS)
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
endif

View File

@@ -143,7 +143,7 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
convolve_real(in, in_len,
reinterpret_cast<float *>(partitions[path]),
filt_len, &out[2 * i], out_len - i,
n, 1, 1, 0);
n, 1);
}
return out_len;

View File

@@ -102,7 +102,7 @@ bool Synthesis::rotate(float *out, size_t len)
convolve_real(hInputs[i], blockLen,
subFilters[i], hLen,
hOutputs[i], blockLen,
0, blockLen, 1, 0);
0, blockLen);
}
/* Interleave into output vector */

View File

@@ -27,6 +27,10 @@
#include "Transceiver.h"
#include <Logger.h>
extern "C" {
#include "osmo_signal.h"
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -115,7 +119,7 @@ Transceiver::Transceiver(int wBasePort,
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset),
rssiOffset(wRssiOffset), sig_cbfn(NULL),
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
mWriteBurstToDiskMask(0)
@@ -155,7 +159,8 @@ Transceiver::~Transceiver()
* are still expected to report clock indications through control channel
* activity.
*/
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge)
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
bool edge, bool ext_rach)
{
int d_srcport, d_dstport, c_srcport, c_dstport;
@@ -169,6 +174,7 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool
return false;
}
mExtRACH = ext_rach;
mEdge = edge;
mDataSockets.resize(mChans);
@@ -219,6 +225,17 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool
return true;
}
void Transceiver::setSignalHandler(osmo_signal_cbfn cbfn)
{
if (this->sig_cbfn)
osmo_signal_unregister_handler(SS_TRANSC, this->sig_cbfn, NULL);
if (cbfn) {
this->sig_cbfn = cbfn;
osmo_signal_register_handler(SS_TRANSC, this->sig_cbfn, NULL);
}
}
/*
* Start the transceiver
*
@@ -373,7 +390,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
state = &mStates[i];
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->USRP interface ("
LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
if (state->mRetrans)
updateFillerTable(i, burst);
@@ -482,16 +499,16 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
break;
case IV:
case VI:
return RACH;
return mExtRACH ? EXT_RACH : RACH;
break;
case V: {
int mod51 = burstFN % 51;
if ((mod51 <= 36) && (mod51 >= 14))
return RACH;
return mExtRACH ? EXT_RACH : RACH;
else if ((mod51 == 4) || (mod51 == 5))
return RACH;
return mExtRACH ? EXT_RACH : RACH;
else if ((mod51 == 45) || (mod51 == 46))
return RACH;
return mExtRACH ? EXT_RACH : RACH;
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
return RACH;
else
@@ -509,7 +526,7 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
case XIII: {
int mod52 = burstFN % 52;
if ((mod52 == 12) || (mod52 == 38))
return RACH;
return mExtRACH ? EXT_RACH : RACH;
else if ((mod52 == 25) || (mod52 == 51))
return IDLE;
else
@@ -620,9 +637,11 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
}
unsigned max_toa = (type == RACH || type == EXT_RACH) ?
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
/* Detect normal or RACH bursts */
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa,
(type==RACH)?mMaxExpectedDelayAB:mMaxExpectedDelayNB);
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
if (rc > 0) {
type = (CorrType) rc;
@@ -700,13 +719,13 @@ void Transceiver::driveControl(size_t chan)
/* Verify a command signature */
if (strncmp(buffer, "CMD ", 4)) {
LOG(WARNING) << "bogus message on control interface";
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
return;
}
/* Set command pointer */
command = buffer + 4;
LOG(INFO) << "command is " << command;
LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
if (match_cmd(command, "POWEROFF", NULL)) {
stop();
@@ -785,7 +804,7 @@ void Transceiver::driveControl(size_t chan)
sscanf(params, "%d", &freqKhz);
mRxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
LOG(ALERT) << "RX failed to tune";
LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
}
else
@@ -796,7 +815,7 @@ void Transceiver::driveControl(size_t chan)
sscanf(params, "%d", &freqKhz);
mTxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
LOG(ALERT) << "TX failed to tune";
LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
}
else
@@ -808,7 +827,7 @@ void Transceiver::driveControl(size_t chan)
if (TSC > 7) {
sprintf(response, "RSP SETTSC 1 %d", TSC);
} else {
LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
mTSC = TSC;
sprintf(response,"RSP SETTSC 0 %d", TSC);
}
@@ -818,7 +837,7 @@ void Transceiver::driveControl(size_t chan)
int timeslot;
sscanf(params, "%d %d", &timeslot, &corrCode);
if ((timeslot < 0) || (timeslot > 7)) {
LOG(WARNING) << "bogus message on control interface";
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
return;
}
@@ -833,10 +852,11 @@ void Transceiver::driveControl(size_t chan)
mWriteBurstToDiskMask = mask;
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
} else {
LOG(WARNING) << "bogus command " << command << " on control interface.";
LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
sprintf(response,"RSP ERR 1");
}
LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
mCtrlSockets[chan]->write(response, strlen(response) + 1);
}
@@ -885,8 +905,12 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
void Transceiver::driveReceiveRadio()
{
if (!mRadioInterface->driveReceiveRadio()) {
int rc = mRadioInterface->driveReceiveRadio();
if (rc == 0) {
usleep(100000);
} else if (rc < 0) {
LOG(FATAL) << "radio Interface receive failed, requesting stop.";
osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this);
} else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
mForceClockInterface = false;
writeClockInterface();
@@ -982,7 +1006,8 @@ void Transceiver::driveTxFIFO()
// only update latency at the defined frame interval
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
LOG(INFO) << "new latency: " << mTransmitLatency;
LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
<< radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
mLatencyUpdateTime = radioClock->get();
}
}
@@ -1025,11 +1050,15 @@ void Transceiver::writeClockInterface()
void *RxUpperLoopAdapter(TransceiverChannel *chan)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
delete chan;
snprintf(thread_name, 16, "RxUpper%zu", num);
set_selfthread_name(thread_name);
trx->setPriority(0.42);
while (1) {
@@ -1041,6 +1070,8 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan)
void *RxLowerLoopAdapter(Transceiver *transceiver)
{
set_selfthread_name("RxLower");
transceiver->setPriority(0.45);
while (1) {
@@ -1052,6 +1083,8 @@ void *RxLowerLoopAdapter(Transceiver *transceiver)
void *TxLowerLoopAdapter(Transceiver *transceiver)
{
set_selfthread_name("TxLower");
transceiver->setPriority(0.44);
while (1) {
@@ -1063,11 +1096,15 @@ void *TxLowerLoopAdapter(Transceiver *transceiver)
void *ControlServiceLoopAdapter(TransceiverChannel *chan)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
delete chan;
snprintf(thread_name, 16, "CtrlService%zu", num);
set_selfthread_name(thread_name);
while (1) {
trx->driveControl(num);
pthread_testcancel();
@@ -1077,11 +1114,15 @@ void *ControlServiceLoopAdapter(TransceiverChannel *chan)
void *TxUpperLoopAdapter(TransceiverChannel *chan)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
delete chan;
snprintf(thread_name, 16, "TxUpper%zu", num);
set_selfthread_name(thread_name);
trx->setPriority(0.40);
while (1) {

View File

@@ -31,6 +31,7 @@
#include <sys/socket.h>
extern "C" {
#include <osmocom/core/signal.h>
#include "config_defs.h"
}
@@ -113,7 +114,8 @@ public:
~Transceiver();
/** Start the control loop */
bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge);
bool init(FillerType filler, size_t rtsc, unsigned rach_delay,
bool edge, bool ext_rach);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -128,6 +130,8 @@ public:
/** accessor for number of channels */
size_t numChans() const { return mChans; };
void setSignalHandler(osmo_signal_cbfn cbfn);
/** Codes for channel combinations */
typedef enum {
FILL, ///< Channel is transmitted, but unused
@@ -177,6 +181,8 @@ private:
double rssiOffset; ///< RSSI to dBm conversion offset
osmo_signal_cbfn *sig_cbfn; ///< Registered Signal Handler to announce events.
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
@@ -205,6 +211,7 @@ private:
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
bool mExtRACH;
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

View File

@@ -29,17 +29,15 @@
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 start, int len);
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 start, int len);
int bounds_check(int x_len, int h_len, int y_len,
int start, int len, int step);
int start, int len);
#ifdef HAVE_NEON
/* Calls into NEON assembler */
@@ -69,35 +67,32 @@ void convolve_init(void)
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)
int start, int len)
{
void (*conv_func)(float *, float *, float *, int) = NULL;
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
#ifdef HAVE_NEON
if (step <= 4) {
switch (h_len) {
case 4:
conv_func = neon_conv_real4;
break;
case 8:
conv_func = neon_conv_real8;
break;
case 12:
conv_func = neon_conv_real12;
break;
case 16:
conv_func = neon_conv_real16;
break;
case 20:
conv_func = neon_conv_real20;
break;
}
switch (h_len) {
case 4:
conv_func = neon_conv_real4;
break;
case 8:
conv_func = neon_conv_real8;
break;
case 12:
conv_func = neon_conv_real12;
break;
case 16:
conv_func = neon_conv_real16;
break;
case 20:
conv_func = neon_conv_real20;
break;
}
#endif
if (conv_func) {
@@ -107,7 +102,7 @@ int convolve_real(float *x, int x_len,
_base_convolve_real(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
start, len);
}
return len;
@@ -118,18 +113,17 @@ int convolve_real(float *x, int x_len,
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)
int start, int len)
{
void (*conv_func)(float *, float *, float *, int, int) = NULL;
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
#ifdef HAVE_NEON
if (step <= 4 && !(h_len % 4))
if (!(h_len % 4))
conv_func = neon_conv_cmplx_4n;
#endif
if (conv_func) {
@@ -139,7 +133,7 @@ int convolve_complex(float *x, int x_len,
_base_convolve_complex(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
start, len);
}
return len;

View File

@@ -92,8 +92,8 @@ neon_conv_real12:
vld2.32 {q8-q9}, [r4], r6
vld2.32 {q10-q11}, [r5], r6
#ifdef HAVE_NEON_FMA
vfma.f32 q1, q6, q0
vfma.f32 q3, q7, q0
vmul.f32 q1, q6, q0
vmul.f32 q3, q7, q0
vfma.f32 q1, q8, q2
vfma.f32 q3, q9, q2
vfma.f32 q1, q10, q4

View File

@@ -1,31 +1,27 @@
#ifndef _CONVOLVE_H_
#define _CONVOLVE_H_
void *convolve_h_alloc(int num);
void *convolve_h_alloc(size_t num);
int convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int start, int 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 start, int 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 start, int len);
int base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int start, int len);
void convolve_init(void);

View File

@@ -41,17 +41,17 @@ static void mac_cmplx(const float *x, const float *h, float *y)
/* Base vector complex-complex multiply and accumulate */
static void mac_real_vec_n(const float *x, const float *h, float *y,
int len, int step, int offset)
int len)
{
for (int i = offset; i < len; i += step)
for (int i=0; i<len; i++)
mac_real(&x[2 * i], &h[2 * i], y);
}
/* Base vector complex-complex multiply and accumulate */
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
int len, int step, int offset)
int len)
{
for (int i = offset; i < len; i += step)
for (int i=0; i<len; i++)
mac_cmplx(&x[2 * i], &h[2 * i], y);
}
@@ -59,14 +59,12 @@ static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
int _base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
int start, int len)
{
for (int i = 0; i < len; i++) {
mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
h,
&y[2 * i], h_len,
step, offset);
&y[2 * i], h_len);
}
return len;
@@ -76,14 +74,13 @@ int _base_convolve_real(const float *x, int x_len,
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
int start, int len)
{
for (int i = 0; i < len; i++) {
mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
h,
&y[2 * i],
h_len, step, offset);
h_len);
}
return len;
@@ -91,10 +88,10 @@ int _base_convolve_complex(const float *x, int x_len,
/* Buffer validity checks */
int bounds_check(int x_len, int h_len, int y_len,
int start, int len, int step)
int start, int len)
{
if ((x_len < 1) || (h_len < 1) ||
(y_len < 1) || (len < 1) || (step < 1)) {
(y_len < 1) || (len < 1)) {
fprintf(stderr, "Convolve: Invalid input\n");
return -1;
}
@@ -113,10 +110,9 @@ int bounds_check(int x_len, int h_len, int y_len,
int base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
int start, int len)
{
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
@@ -124,17 +120,16 @@ int base_convolve_real(const float *x, int x_len,
return _base_convolve_real(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
start, len);
}
/* API: Non-aligned (no SSE) complex-complex */
int base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
int start, int len)
{
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
@@ -142,11 +137,11 @@ int base_convolve_complex(const float *x, int x_len,
return _base_convolve_complex(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
start, len);
}
/* Aligned filter tap allocation */
void *convolve_h_alloc(int len)
void *convolve_h_alloc(size_t len)
{
#ifdef HAVE_SSE3
return memalign(16, len * 2 * sizeof(float));

View File

@@ -30,25 +30,25 @@
/* Architecture dependant function pointers */
struct convolve_cpu_context {
void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_cmplx_8n) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_cmplx) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_real4) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_real8) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_real12) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_real16) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_real20) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_real4n) (const float *, int, const float *, int, float *,
int, int, int, int, int);
int, int, int);
void (*conv_real) (const float *, int, const float *, int, float *, int,
int, int, int, int);
int, int);
};
static struct convolve_cpu_context c;
@@ -56,17 +56,15 @@ static struct convolve_cpu_context c;
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 start, int len);
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int start, int len);
int bounds_check(int x_len, int h_len, int y_len,
int start, int len, int step);
int start, int len);
/* API: Initalize convolve module */
void convolve_init(void)
@@ -99,46 +97,37 @@ void convolve_init(void)
/* 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)
float *y, int y_len, int start, int len)
{
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
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);
switch (h_len) {
case 4:
c.conv_real4(x, x_len, h, h_len, y, y_len, start, len);
break;
case 8:
c.conv_real8(x, x_len, h, h_len, y, y_len, start, len);
break;
case 12:
c.conv_real12(x, x_len, h, h_len, y, y_len, start, len);
break;
case 16:
c.conv_real16(x, x_len, h, h_len, y, y_len, start, len);
break;
case 20:
c.conv_real20(x, x_len, h, h_len, y, y_len, start, len);
break;
default:
if (!(h_len % 4))
c.conv_real4n(x, x_len, h, h_len, y, y_len,
start, len);
else
c.conv_real(x, x_len, h, h_len, y, y_len, start,
len);
}
return len;
}
@@ -147,26 +136,19 @@ int convolve_real(const float *x, int x_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 start, int len)
{
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
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);
if (!(h_len % 8))
c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start, len);
else if (!(h_len % 4))
c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start, len);
else
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len);
return len;
}

View File

@@ -34,12 +34,12 @@
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)
int start, int len)
{
/* NOTE: The parameter list of this function has to match the parameter
* list of _base_convolve_real() in convolve_base.c. This specific
* implementation, ignores some of the parameters of
* _base_convolve_complex(), which are: x_len, y_len, offset, step */
* _base_convolve_complex(), which are: x_len, y_len. */
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
@@ -75,7 +75,7 @@ void sse_conv_real4(const float *x, int x_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)
int start, int len)
{
/* See NOTE in sse_conv_real4() */
@@ -126,7 +126,7 @@ void sse_conv_real8(const float *x, int x_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)
int start, int len)
{
/* See NOTE in sse_conv_real4() */
@@ -192,7 +192,7 @@ void sse_conv_real12(const float *x, int x_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)
int start, int len)
{
/* See NOTE in sse_conv_real4() */
@@ -271,7 +271,7 @@ void sse_conv_real16(const float *x, int x_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)
int start, int len)
{
/* See NOTE in sse_conv_real4() */
@@ -361,7 +361,7 @@ void sse_conv_real20(const float *x, int x_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)
int start, int len)
{
/* See NOTE in sse_conv_real4() */
@@ -408,12 +408,12 @@ void sse_conv_real4n(const float *x, int x_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)
int start, int len)
{
/* NOTE: The parameter list of this function has to match the parameter
* list of _base_convolve_complex() in convolve_base.c. This specific
* implementation, ignores some of the parameters of
* _base_convolve_complex(), which are: x_len, y_len, offset, step. */
* _base_convolve_complex(), which are: x_len, y_len. */
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
@@ -466,7 +466,7 @@ void sse_conv_cmplx_4n(const float *x, int x_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)
int start, int len)
{
/* See NOTE in sse_conv_cmplx_4n() */

View File

@@ -23,46 +23,46 @@
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);
int start, int len);
/* 8-tap SSE complex-real convolution */
void sse_conv_real8(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
int start, int len);
/* 12-tap SSE complex-real convolution */
void sse_conv_real12(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
int start, int len);
/* 16-tap SSE complex-real convolution */
void sse_conv_real16(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
int start, int len);
/* 20-tap SSE complex-real convolution */
void sse_conv_real20(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
int start, int len);
/* 4*N-tap SSE complex-real convolution */
void sse_conv_real4n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
int start, int len);
/* 4*N-tap SSE complex-complex convolution */
void sse_conv_cmplx_4n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
int start, int len);
/* 8*N-tap SSE complex-complex convolution */
void sse_conv_cmplx_8n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len, int step, int offset);
int start, int len);

View File

@@ -1,8 +1,6 @@
include $(top_srcdir)/Makefile.common
noinst_HEADERS = radioDevice.h
SUBDIRS =
SUBDIRS = common
if DEVICE_USRP1
SUBDIRS += usrp1
@@ -11,3 +9,7 @@ endif
if DEVICE_UHD
SUBDIRS += uhd
endif
if DEVICE_LMS
SUBDIRS += lms
endif

View File

@@ -0,0 +1,12 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
noinst_HEADERS = radioDevice.h smpl_buf.h
noinst_LTLIBRARIES = libdevice_common.la
libdevice_common_la_SOURCES = \
smpl_buf.cpp

View File

@@ -19,6 +19,7 @@
#include <vector>
#include "GSMCommon.h"
#include "Logger.h"
extern "C" {
#include "config_defs.h"
@@ -39,7 +40,7 @@ class RadioDevice {
public:
/* Available transport bus types */
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED, TX_WINDOW_LMS1 };
/* Radio interface types */
enum InterfaceType {
@@ -160,8 +161,47 @@ class RadioDevice {
virtual double getTxFreq(size_t chan = 0) = 0;
virtual double getRxFreq(size_t chan = 0) = 0;
virtual double getSampleRate()=0;
virtual double numberRead()=0;
virtual double numberWritten()=0;
protected:
size_t tx_sps, rx_sps;
InterfaceType iface;
size_t chans;
double lo_offset;
std::vector<std::string> tx_paths, rx_paths;
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chans), lo_offset(offset),
tx_paths(tx_paths), rx_paths(rx_paths)
{ }
bool set_antennas() {
unsigned int i;
for (i = 0; i < tx_paths.size(); i++) {
if (tx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
if (!setTxAntenna(tx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
return false;
}
}
for (i = 0; i < rx_paths.size(); i++) {
if (rx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
if (!setRxAntenna(rx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
return false;
}
}
LOG(INFO) << "Antennas configured successfully";
return true;
}
};

View File

@@ -0,0 +1,169 @@
/*
* Sample Buffer - Allows reading and writing of timed samples
*
* Copyright 2010,2011 Free Software Foundation, Inc.
* Copyright (C) 2015 Ettus Research LLC
* Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include "smpl_buf.h"
#include <inttypes.h>
smpl_buf::smpl_buf(size_t len)
: buf_len(len), time_start(0), time_end(0),
data_start(0), data_end(0)
{
data = new uint32_t[len];
}
smpl_buf::~smpl_buf()
{
delete[] data;
}
ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
{
if (timestamp < time_start)
return ERROR_TIMESTAMP;
else if (timestamp >= time_end)
return 0;
else
return time_end - timestamp;
}
ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
{
int type_sz = 2 * sizeof(short);
// Check for valid read
if (timestamp < time_start)
return ERROR_TIMESTAMP;
if (timestamp >= time_end)
return 0;
if (len >= buf_len)
return ERROR_READ;
// How many samples should be copied
size_t num_smpls = time_end - timestamp;
if (num_smpls > len)
num_smpls = len;
// Starting index
size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
// Read it
if (read_start + num_smpls < buf_len) {
size_t numBytes = len * type_sz;
memcpy(buf, data + read_start, numBytes);
} else {
size_t first_cp = (buf_len - read_start) * type_sz;
size_t second_cp = len * type_sz - first_cp;
memcpy(buf, data + read_start, first_cp);
memcpy((char*) buf + first_cp, data, second_cp);
}
data_start = (read_start + len) % buf_len;
time_start = timestamp + len;
if (time_start > time_end)
return ERROR_READ;
else
return num_smpls;
}
ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
{
int type_sz = 2 * sizeof(short);
// Check for valid write
if ((len == 0) || (len >= buf_len))
return ERROR_WRITE;
if ((timestamp + len) <= time_end)
return ERROR_TIMESTAMP;
if (timestamp < time_end) {
LOGC(DDEV, ERR) << "Overwriting old buffer data: timestamp="
<< timestamp << " time_end=" << time_end;
// Do not return error here, because it's a rounding error and is not fatal
}
if (timestamp > time_end && time_end != 0) {
LOGC(DDEV, ERR) << "Skipping buffer data: timestamp="
<< timestamp << " time_end=" << time_end;
// Do not return error here, because it's a rounding error and is not fatal
}
// Starting index
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
// Write it
if ((write_start + len) < buf_len) {
size_t numBytes = len * type_sz;
memcpy(data + write_start, buf, numBytes);
} else {
size_t first_cp = (buf_len - write_start) * type_sz;
size_t second_cp = len * type_sz - first_cp;
memcpy(data + write_start, buf, first_cp);
memcpy(data, (char*) buf + first_cp, second_cp);
}
data_end = (write_start + len) % buf_len;
time_end = timestamp + len;
if (!data_start)
data_start = write_start;
if (((write_start + len) > buf_len) && (data_end > data_start))
return ERROR_OVERFLOW;
else if (time_end <= time_start)
return ERROR_WRITE;
else
return len;
}
std::string smpl_buf::str_status(TIMESTAMP timestamp) const
{
std::ostringstream ost("Sample buffer: ");
ost << "timestamp = " << timestamp;
ost << ", length = " << buf_len;
ost << ", time_start = " << time_start;
ost << ", time_end = " << time_end;
ost << ", data_start = " << data_start;
ost << ", data_end = " << data_end;
return ost.str();
}
std::string smpl_buf::str_code(ssize_t code)
{
switch (code) {
case ERROR_TIMESTAMP:
return "Sample buffer: Requested timestamp is not valid";
case ERROR_READ:
return "Sample buffer: Read error";
case ERROR_WRITE:
return "Sample buffer: Write error";
case ERROR_OVERFLOW:
return "Sample buffer: Overrun";
default:
return "Sample buffer: Unknown error";
}
}

View File

@@ -0,0 +1,87 @@
/*
* Sample Buffer - Allows reading and writing of timed samples
*
* Copyright 2010,2011 Free Software Foundation, Inc.
* Copyright (C) 2015 Ettus Research LLC
* Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#pragma once
#include <unistd.h>
#include "radioDevice.h"
/*
Sample Buffer - Allows reading and writing of timed samples using osmo-trx
timestamps. Time conversions are handled
internally or accessable through the static convert calls.
*/
class smpl_buf {
public:
/** Sample buffer constructor
@param len number of 32-bit samples the buffer should hold
@param timestamp
*/
smpl_buf(size_t len);
~smpl_buf();
/** Query number of samples available for reading
@param timestamp time of first sample
@return number of available samples or error
*/
ssize_t avail_smpls(TIMESTAMP timestamp) const;
/** Read and write
@param buf pointer to buffer
@param len number of samples desired to read or write
@param timestamp time of first stample
@return number of actual samples read or written or error
*/
ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
/** Buffer status string
@return a formatted string describing internal buffer state
*/
std::string str_status(TIMESTAMP timestamp) const;
/** Formatted error string
@param code an error code
@return a formatted error string
*/
static std::string str_code(ssize_t code);
enum err_code {
ERROR_TIMESTAMP = -1,
ERROR_READ = -2,
ERROR_WRITE = -3,
ERROR_OVERFLOW = -4
};
private:
uint32_t *data;
size_t buf_len;
TIMESTAMP time_start;
TIMESTAMP time_end;
size_t data_start;
size_t data_end;
};

View File

@@ -0,0 +1,773 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "Logger.h"
#include "Threads.h"
#include "LMSDevice.h"
#include "Utils.h"
#include <lime/LimeSuite.h>
#include <osmocom/core/utils.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
using namespace std;
constexpr double LMSDevice::masterClockRate;
#define MAX_ANTENNA_LIST_SIZE 10
#define LMS_SAMPLE_RATE GSMRATE*32
#define GSM_CARRIER_BW 270000.0 /* 270kHz */
#define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */
#define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED)
#define SAMPLE_BUF_SZ (1 << 20) /* Size of Rx timestamp based Ring buffer, in bytes */
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
m_lms_dev(NULL)
{
LOGC(DDEV, INFO) << "creating LMS device...";
m_lms_stream_rx.resize(chans);
m_lms_stream_tx.resize(chans);
m_last_rx_underruns.resize(chans, 0);
m_last_rx_overruns.resize(chans, 0);
m_last_rx_dropped.resize(chans, 0);
m_last_tx_underruns.resize(chans, 0);
rx_buffers.resize(chans);
}
LMSDevice::~LMSDevice()
{
unsigned int i;
LOGC(DDEV, INFO) << "Closing LMS device";
if (m_lms_dev) {
/* disable all channels */
for (i=0; i<chans; i++) {
LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, false);
LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, false);
}
LMS_Close(m_lms_dev);
m_lms_dev = NULL;
}
for (size_t i = 0; i < rx_buffers.size(); i++)
delete rx_buffers[i];
}
static void lms_log_callback(int lvl, const char *msg)
{
/* map lime specific log levels */
static const int lvl_map[5] = {
[0] = LOGL_FATAL,
[LMS_LOG_ERROR] = LOGL_ERROR,
[LMS_LOG_WARNING] = LOGL_NOTICE,
[LMS_LOG_INFO] = LOGL_INFO,
[LMS_LOG_DEBUG] = LOGL_DEBUG,
};
/* protect against future higher log level values (lower importance) */
if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map))
lvl = ARRAY_SIZE(lvl_map)-1;
LOGLV(DLMS, lvl_map[lvl]) << msg;
}
static void print_range(const char* name, lms_range_t *range)
{
LOGC(DDEV, INFO) << name << ": Min=" << range->min << " Max=" << range->max
<< " Step=" << range->step;
}
/*! Find the device string that matches all filters from \a args.
* \param[in] info_list device addresses found by LMS_GetDeviceList()
* \param[in] count length of info_list
* \param[in] args dev-args value from osmo-trx.cfg, containing comma separated key=value pairs
* \return index of first matching device or -1 (no match) */
int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args)
{
unsigned int i, j;
vector<string> filters;
filters = comma_delimited_to_vector(args.c_str());
/* iterate over device addresses */
for (i=0; i < count; i++) {
/* check if all filters match */
bool match = true;
for (j=0; j < filters.size(); j++) {
if (!strstr(info_list[i], filters[j].c_str())) {
match = false;
break;
}
}
if (match)
return i;
}
return -1;
}
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
{
lms_info_str_t* info_list;
const lms_dev_info_t* device_info;
lms_range_t range_sr;
float_type sr_host, sr_rf;
unsigned int i, n;
int rc, dev_id;
LOGC(DDEV, INFO) << "Opening LMS device..";
LMS_RegisterLogHandler(&lms_log_callback);
if ((n = LMS_GetDeviceList(NULL)) < 0)
LOGC(DDEV, ERROR) << "LMS_GetDeviceList(NULL) failed";
LOGC(DDEV, INFO) << "Devices found: " << n;
if (n < 1)
return -1;
info_list = new lms_info_str_t[n];
if (LMS_GetDeviceList(info_list) < 0)
LOGC(DDEV, ERROR) << "LMS_GetDeviceList(info_list) failed";
for (i = 0; i < n; i++)
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
dev_id = info_list_find(info_list, n, args);
if (dev_id == -1) {
LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
delete[] info_list;
return -1;
}
LOGC(DDEV, INFO) << "Using device[" << dev_id << "]";
rc = LMS_Open(&m_lms_dev, info_list[dev_id], NULL);
if (rc != 0) {
LOGC(DDEV, ERROR) << "LMS_GetDeviceList() failed)";
delete [] info_list;
return -1;
}
delete [] info_list;
device_info = LMS_GetDeviceInfo(m_lms_dev);
if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
LOGC(DDEV, ERROR) << "Invalid reference type";
goto out_close;
}
/* if reference clock is external setup must happen _before_ calling LMS_Init */
/* FIXME make external reference frequency configurable */
if (ref == REF_EXTERNAL) {
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
/* Assume an external 10 MHz reference clock */
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
goto out_close;
}
LOGC(DDEV, INFO) << "Init LMS device";
if (LMS_Init(m_lms_dev) != 0) {
LOGC(DDEV, ERROR) << "LMS_Init() failed";
goto out_close;
}
/* LimeSDR-Mini does not have switches but needs soldering to select external/internal clock */
/* LimeNET-Micro also does not like selecting internal clock*/
/* also set device specific maximum tx levels selected by phasenoise measurements*/
if (strncmp(device_info->deviceName,"LimeSDR-USB",11) == 0){
/* if reference clock is internal setup must happen _after_ calling LMS_Init */
/* according to lms using LMS_CLOCK_EXTREF with a frequency <= 0 is the correct way to set clock to internal reference*/
if (ref == REF_INTERNAL) {
LOGC(DDEV, INFO) << "Setting Internal clock reference";
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, -1) < 0)
goto out_close;
}
maxTxGainClamp = 73.0;
} else if (strncmp(device_info->deviceName,"LimeSDR-Mini",12) == 0)
maxTxGainClamp = 66.0;
else
maxTxGainClamp = 71.0; /* "LimeNET-Micro", etc FIXME pciE based LMS boards?*/
/* enable all used channels */
for (i=0; i<chans; i++) {
if (LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, true) < 0)
goto out_close;
if (LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, true) < 0)
goto out_close;
}
/* set samplerate */
if (LMS_GetSampleRateRange(m_lms_dev, LMS_CH_RX, &range_sr))
goto out_close;
print_range("Sample Rate", &range_sr);
LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps;
if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0)
goto out_close;
if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf))
goto out_close;
LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf;
/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */
ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */
/* configure antennas */
if (!set_antennas()) {
LOGC(DDEV, FATAL) << "LMS antenna setting failed";
goto out_close;
}
/* Set up per-channel Rx timestamp based Ring buffers */
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
started = false;
return NORMAL;
out_close:
LOGC(DDEV, FATAL) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
LMS_Close(m_lms_dev);
m_lms_dev = NULL;
return -1;
}
bool LMSDevice::start()
{
LOGC(DDEV, INFO) << "starting LMS...";
unsigned int i;
if (started) {
LOGC(DDEV, ERR) << "Device already started";
return false;
}
/* configure the channels/streams */
for (i=0; i<chans; i++) {
/* Set gains for calibration/filter setup */
/* TX gain to maximum */
setTxGain(maxTxGain(), i);
/* RX gain to midpoint */
setRxGain((minRxGain() + maxRxGain()) / 2, i);
/* set up Rx and Tx filters */
if (!do_filters(i))
return false;
/* Perform Rx and Tx calibration */
if (!do_calib(i))
return false;
/* configure Streams */
m_lms_stream_rx[i] = {};
m_lms_stream_rx[i].isTx = false;
m_lms_stream_rx[i].channel = i;
m_lms_stream_rx[i].fifoSize = 1024 * 1024;
m_lms_stream_rx[i].throughputVsLatency = 0.3;
m_lms_stream_rx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
m_lms_stream_tx[i] = {};
m_lms_stream_tx[i].isTx = true;
m_lms_stream_tx[i].channel = i;
m_lms_stream_tx[i].fifoSize = 1024 * 1024;
m_lms_stream_tx[i].throughputVsLatency = 0.3;
m_lms_stream_tx[i].dataFmt = lms_stream_t::LMS_FMT_I16;
if (LMS_SetupStream(m_lms_dev, &m_lms_stream_rx[i]) < 0)
return false;
if (LMS_SetupStream(m_lms_dev, &m_lms_stream_tx[i]) < 0)
return false;
}
/* now start the streams in a second loop, as we can no longer call
* LMS_SetupStream() after LMS_StartStream() of the first stream */
for (i = 0; i < chans; i++) {
if (LMS_StartStream(&m_lms_stream_rx[i]) < 0)
return false;
if (LMS_StartStream(&m_lms_stream_tx[i]) < 0)
return false;
}
flush_recv(10);
started = true;
return true;
}
bool LMSDevice::stop()
{
unsigned int i;
if (!started)
return true;
for (i=0; i<chans; i++) {
LMS_StopStream(&m_lms_stream_tx[i]);
LMS_StopStream(&m_lms_stream_rx[i]);
}
for (i=0; i<chans; i++) {
LMS_DestroyStream(m_lms_dev, &m_lms_stream_tx[i]);
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
}
started = false;
return true;
}
/* do rx/tx calibration - depends on gain, freq and bw */
bool LMSDevice::do_calib(size_t chan)
{
LOGCHAN(chan, DDEV, INFO) << "Calibrating";
if (LMS_Calibrate(m_lms_dev, LMS_CH_RX, chan, LMS_CALIBRATE_BW_HZ, 0) < 0)
return false;
if (LMS_Calibrate(m_lms_dev, LMS_CH_TX, chan, LMS_CALIBRATE_BW_HZ, 0) < 0)
return false;
return true;
}
/* do rx/tx filter config - depends on bw only? */
bool LMSDevice::do_filters(size_t chan)
{
lms_range_t range_lpfbw_rx, range_lpfbw_tx;
float_type lpfbw_rx, lpfbw_tx;
LOGCHAN(chan, DDEV, INFO) << "Setting filters";
if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_rx))
return false;
print_range("LPFBWRange Rx", &range_lpfbw_rx);
if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_tx))
return false;
print_range("LPFBWRange Tx", &range_lpfbw_tx);
lpfbw_rx = OSMO_MIN(OSMO_MAX(1.4001e6, range_lpfbw_rx.min), range_lpfbw_rx.max);
lpfbw_tx = OSMO_MIN(OSMO_MAX(5.2e6, range_lpfbw_tx.min), range_lpfbw_tx.max);
LOGCHAN(chan, DDEV, INFO) << "LPFBW: Rx=" << lpfbw_rx << " Tx=" << lpfbw_tx;
LOGCHAN(chan, DDEV, INFO) << "Setting LPFBW";
if (LMS_SetLPFBW(m_lms_dev, LMS_CH_RX, chan, lpfbw_rx) < 0)
return false;
if (LMS_SetLPFBW(m_lms_dev, LMS_CH_TX, chan, lpfbw_tx) < 0)
return false;
return true;
}
double LMSDevice::maxTxGain()
{
return maxTxGainClamp;
}
double LMSDevice::minTxGain()
{
return 0.0;
}
double LMSDevice::maxRxGain()
{
return 73.0;
}
double LMSDevice::minRxGain()
{
return 0.0;
}
double LMSDevice::setTxGain(double dB, size_t chan)
{
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX gain to " << dB << " dB";
if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
LOGCHAN(chan, DDEV, ERR) << "Error setting TX gain to " << dB << " dB";
return dB;
}
double LMSDevice::setRxGain(double dB, size_t chan)
{
if (dB > maxRxGain())
dB = maxRxGain();
if (dB < minRxGain())
dB = minRxGain();
LOGCHAN(chan, DDEV, NOTICE) << "Setting RX gain to " << dB << " dB";
if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0)
LOGCHAN(chan, DDEV, ERR) << "Error setting RX gain to " << dB << " dB";
return dB;
}
int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
const char* c_name = name.c_str();
int num_names;
int i;
num_names = LMS_GetAntennaList(m_lms_dev, dir_tx, chan, name_list);
for (i = 0; i < num_names; i++) {
if (!strcmp(c_name, name_list[i]))
return i;
}
return -1;
}
bool LMSDevice::flush_recv(size_t num_pkts)
{
#define CHUNK 625
int len = CHUNK * tx_sps;
short *buffer = (short*) alloca(sizeof(short) * len * 2);
int rc;
lms_stream_meta_t rx_metadata = {};
rx_metadata.flushPartialPacket = false;
rx_metadata.waitForTimestamp = false;
ts_initial = 0;
while (!ts_initial || (num_pkts-- > 0)) {
rc = LMS_RecvStream(&m_lms_stream_rx[0], &buffer[0], len, &rx_metadata, 100);
LOGC(DDEV, DEBUG) << "Flush: Recv buffer of len " << rc << " at " << std::hex << rx_metadata.timestamp;
if (rc != len) {
LOGC(DDEV, ERROR) << "Flush: Device receive timed out";
return false;
}
ts_initial = rx_metadata.timestamp + len;
}
LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
return true;
}
bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan)
{
int idx;
if (chan >= rx_paths.size()) {
LOGC(DDEV, ERROR) << "Requested non-existent channel " << chan;
return false;
}
idx = get_ant_idx(ant, LMS_CH_RX, chan);
if (idx < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna";
return false;
}
if (LMS_SetAntenna(m_lms_dev, LMS_CH_RX, chan, idx) < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Unable to set Rx Antenna";
}
return true;
}
std::string LMSDevice::getRxAntenna(size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
int idx;
if (chan >= rx_paths.size()) {
LOGC(DDEV, ERROR) << "Requested non-existent channel " << chan;
return "";
}
idx = LMS_GetAntenna(m_lms_dev, LMS_CH_RX, chan);
if (idx < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Error getting Rx Antenna";
return "";
}
if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, name_list) < idx) {
LOGCHAN(chan, DDEV, ERROR) << "Error getting Rx Antenna List";
return "";
}
return name_list[idx];
}
bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan)
{
int idx;
if (chan >= tx_paths.size()) {
LOGC(DDEV, ERROR) << "Requested non-existent channel " << chan;
return false;
}
idx = get_ant_idx(ant, LMS_CH_TX, chan);
if (idx < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna";
return false;
}
if (LMS_SetAntenna(m_lms_dev, LMS_CH_TX, chan, idx) < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Unable to set Rx Antenna";
}
return true;
}
std::string LMSDevice::getTxAntenna(size_t chan)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
int idx;
if (chan >= tx_paths.size()) {
LOGC(DDEV, ERROR) << "Requested non-existent channel " << chan;
return "";
}
idx = LMS_GetAntenna(m_lms_dev, LMS_CH_TX, chan);
if (idx < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Error getting Tx Antenna";
return "";
}
if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, name_list) < idx) {
LOGCHAN(chan, DDEV, ERROR) << "Error getting Tx Antenna List";
return "";
}
return name_list[idx];
}
bool LMSDevice::requiresRadioAlign()
{
return false;
}
GSM::Time LMSDevice::minLatency() {
/* UNUSED on limesdr (only used on usrp1/2) */
return GSM::Time(0,0);
}
void LMSDevice::update_stream_stats(size_t chan, bool * underrun, bool * overrun)
{
lms_stream_status_t status;
if (LMS_GetStreamStatus(&m_lms_stream_rx[chan], &status) == 0) {
if (status.underrun > m_last_rx_underruns[chan]) {
*underrun = true;
LOGCHAN(chan, DDEV, ERROR) << "recv Underrun! ("
<< m_last_rx_underruns[chan] << " -> "
<< status.underrun << ")";
}
m_last_rx_underruns[chan] = status.underrun;
if (status.overrun > m_last_rx_overruns[chan]) {
*overrun = true;
LOGCHAN(chan, DDEV, ERROR) << "recv Overrun! ("
<< m_last_rx_overruns[chan] << " -> "
<< status.overrun << ")";
}
m_last_rx_overruns[chan] = status.overrun;
if (status.droppedPackets > m_last_rx_dropped[chan]) {
LOGCHAN(chan, DDEV, ERROR) << "recv Dropped packets by HW! ("
<< m_last_rx_dropped[chan] << " -> "
<< status.droppedPackets << ")";
}
m_last_rx_dropped[chan] = m_last_rx_overruns[chan];
}
}
// NOTE: Assumes sequential reads
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
{
int rc, num_smpls, expect_smpls;
ssize_t avail_smpls;
TIMESTAMP expect_timestamp;
unsigned int i;
lms_stream_meta_t rx_metadata = {};
rx_metadata.flushPartialPacket = false;
rx_metadata.waitForTimestamp = false;
rx_metadata.timestamp = 0;
if (bufs.size() != chans) {
LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size();
return -1;
}
*overrun = false;
*underrun = false;
/* Check that timestamp is valid */
rc = rx_buffers[0]->avail_smpls(timestamp);
if (rc < 0) {
LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
return 0;
}
for (i = 0; i<chans; i++) {
/* Receive samples from HW until we have enough */
while ((avail_smpls = rx_buffers[i]->avail_smpls(timestamp)) < len) {
thread_enable_cancel(false);
num_smpls = LMS_RecvStream(&m_lms_stream_rx[i], bufs[i], len - avail_smpls, &rx_metadata, 100);
update_stream_stats(i, underrun, overrun);
thread_enable_cancel(true);
if (num_smpls <= 0) {
LOGCHAN(i, DDEV, ERROR) << "Device receive timed out (" << rc << " vs exp " << len << ").";
return -1;
}
LOGCHAN(i, DDEV, DEBUG) "Received timestamp = " << (TIMESTAMP)rx_metadata.timestamp << " (" << num_smpls << ")";
expect_smpls = len - avail_smpls;
if (expect_smpls != num_smpls)
LOGCHAN(i, DDEV, NOTICE) << "Unexpected recv buffer len: expect "
<< expect_smpls << " got " << num_smpls
<< ", diff=" << expect_smpls - num_smpls;
expect_timestamp = timestamp + avail_smpls;
if (expect_timestamp != (TIMESTAMP)rx_metadata.timestamp)
LOGCHAN(i, DDEV, ERROR) << "Unexpected recv buffer timestamp: expect "
<< expect_timestamp << " got " << (TIMESTAMP)rx_metadata.timestamp
<< ", diff=" << rx_metadata.timestamp - expect_timestamp;
rc = rx_buffers[i]->write(bufs[i], num_smpls, (TIMESTAMP)rx_metadata.timestamp);
if (rc < 0) {
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc);
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
if (rc != smpl_buf::ERROR_OVERFLOW)
return 0;
}
}
}
/* We have enough samples */
for (size_t i = 0; i < rx_buffers.size(); i++) {
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
if ((rc < 0) || (rc != len)) {
LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
return 0;
}
}
return len;
}
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
bool * underrun, unsigned long long timestamp,
bool isControl)
{
int rc = 0;
unsigned int i;
lms_stream_status_t status;
lms_stream_meta_t tx_metadata = {};
tx_metadata.flushPartialPacket = false;
tx_metadata.waitForTimestamp = true;
tx_metadata.timestamp = timestamp - ts_offset; /* Shift Tx time by offset */
if (isControl) {
LOGC(DDEV, ERROR) << "Control packets not supported";
return 0;
}
if (bufs.size() != chans) {
LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size();
return -1;
}
*underrun = false;
for (i = 0; i<chans; i++) {
LOGCHAN(i, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << tx_metadata.timestamp;
thread_enable_cancel(false);
rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100);
if (rc != len) {
LOGCHAN(i, DDEV, ERROR) << "LMS: Device send timed out";
}
if (LMS_GetStreamStatus(&m_lms_stream_tx[i], &status) == 0) {
if (status.underrun > m_last_tx_underruns[i])
*underrun = true;
m_last_tx_underruns[i] = status.underrun;
}
thread_enable_cancel(true);
}
return rc;
}
bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
{
return true;
}
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
{
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Error setting Tx Freq to " << wFreq << " Hz";
return false;
}
return true;
}
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
{
LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Error setting Rx Freq to " << wFreq << " Hz";
return false;
}
return true;
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector < std::string > &tx_paths,
const std::vector < std::string > &rx_paths)
{
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
return NULL;
}
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
return NULL;
}
return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef _LMS_DEVICE_H_
#define _LMS_DEVICE_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include "smpl_buf.h"
#include <sys/time.h>
#include <math.h>
#include <limits.h>
#include <string>
#include <iostream>
#include <lime/LimeSuite.h>
/* Definition of LIMESDR_TX_AMPL limits maximum amplitude of I and Q
* channels separately. Hence LIMESDR_TX_AMPL value must be 1/sqrt(2) =
* 0.7071.... to get an amplitude of 1 of the complex signal:
* A^2 = I^2 + Q^2
* A^2 = (1/sqrt(2))^2 + (1/sqrt(2))^2
* A^2 = 1/2 + 1/2
* A^2 = 1 */
#define LIMESDR_TX_AMPL 0.707
/** A class to handle a LimeSuite supported device */
class LMSDevice:public RadioDevice {
private:
static constexpr double masterClockRate = 52.0e6;
lms_device_t *m_lms_dev;
std::vector<lms_stream_t> m_lms_stream_rx;
std::vector<lms_stream_t> m_lms_stream_tx;
std::vector<uint32_t> m_last_rx_underruns;
std::vector<uint32_t> m_last_rx_overruns;
std::vector<uint32_t> m_last_rx_dropped;
std::vector<uint32_t> m_last_tx_underruns;
std::vector<smpl_buf *> rx_buffers;
double actualSampleRate; ///< the actual USRP sampling rate
bool started; ///< flag indicates LMS has started
bool skipRx; ///< set if LMS is transmit-only.
TIMESTAMP ts_initial, ts_offset;
double rxGain;
double maxTxGainClamp;
bool do_calib(size_t chan);
bool do_filters(size_t chan);
int get_ant_idx(const std::string & name, bool dir_tx, size_t chan);
bool flush_recv(size_t num_pkts);
void update_stream_stats(size_t chan, bool * underrun, bool * overrun);
public:
/** Object constructor */
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~LMSDevice();
/** Instantiate the LMS */
int open(const std::string &args, int ref, bool swap_channels);
/** Start the LMS */
bool start();
/** Stop the LMS */
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) {
}
enum TxWindowType getWindowType() {
return TX_WINDOW_LMS1;
}
/**
Read samples from the LMS.
@param buf preallocated buf to contain read result
@param len number of samples desired
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(std::vector < short *>&buf, int len, bool * overrun,
TIMESTAMP timestamp = 0xffffffff, bool * underrun =
NULL, unsigned *RSSI = NULL);
/**
Write samples to the LMS.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(std::vector < short *>&bufs, int len, bool * underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl =
false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
/** Set the transmitter frequency */
bool setTxFreq(double wFreq, size_t chan = 0);
/** Set the receiver frequency */
bool setRxFreq(double wFreq, size_t chan = 0);
/** Returns the starting write Timestamp*/
TIMESTAMP initialWriteTimestamp(void) {
return ts_initial;
}
/** Returns the starting read Timestamp*/
TIMESTAMP initialReadTimestamp(void) {
return ts_initial;
}
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue() {
return(double) SHRT_MAX * LIMESDR_TX_AMPL;
}
/** returns the full-scale receive amplitude **/
double fullScaleOutputValue() {
return (double) SHRT_MAX;
}
/** sets the receive chan gain, returns the gain setting **/
double setRxGain(double dB, size_t chan = 0);
/** get the current receive gain */
double getRxGain(size_t chan = 0) {
return rxGain;
}
/** return maximum Rx Gain **/
double maxRxGain(void);
/** return minimum Rx Gain **/
double minRxGain(void);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
/** return maximum Tx Gain **/
double maxTxGain(void);
/** return minimum Rx Gain **/
double minTxGain(void);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setRxAntenna(const std::string & ant, size_t chan = 0);
/* return the used RX path */
std::string getRxAntenna(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setTxAntenna(const std::string & ant, size_t chan = 0);
/* return the used RX path */
std::string getTxAntenna(size_t chan = 0);
/** return whether user drives synchronization of Tx/Rx of USRP */
bool requiresRadioAlign();
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency();
/** Return internal status values */
inline double getTxFreq(size_t chan = 0) {
return 0;
}
inline double getRxFreq(size_t chan = 0) {
return 0;
}
inline double getSampleRate() {
return actualSampleRate;
}
};
#endif // _LMS_DEVICE_H_

View File

@@ -0,0 +1,11 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
noinst_HEADERS = LMSDevice.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = LMSDevice.cpp
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la

View File

@@ -1,8 +1,11 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
noinst_HEADERS = UHDDevice.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = UHDDevice.cpp
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,173 @@
/*
* Device support for Ettus Research UHD driver
*
* Copyright 2010,2011 Free Software Foundation, Inc.
* Copyright (C) 2015 Ettus Research LLC
* Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#pragma once
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include "smpl_buf.h"
#include <uhd/version.hpp>
#include <uhd/property_tree.hpp>
#include <uhd/usrp/multi_usrp.hpp>
enum uhd_dev_type {
USRP1,
USRP2,
B100,
B200,
B210,
B2XX_MCBTS,
E1XX,
E3XX,
X3XX,
UMTRX,
LIMESDR,
};
/*
uhd_device - UHD implementation of the Device interface. Timestamped samples
are sent to and received from the device. An intermediate buffer
on the receive side collects and aligns packets of samples.
Events and errors such as underruns are reported asynchronously
by the device and received in a separate thread.
*/
class uhd_device : public RadioDevice {
public:
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chans, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~uhd_device();
int open(const std::string &args, int ref, bool swap_channels);
bool start();
bool stop();
bool restart();
void setPriority(float prio);
enum TxWindowType getWindowType() { return tx_window; }
int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp, bool isControl);
bool updateAlignment(TIMESTAMP timestamp);
bool setTxFreq(double wFreq, size_t chan);
bool setRxFreq(double wFreq, size_t chan);
TIMESTAMP initialWriteTimestamp();
TIMESTAMP initialReadTimestamp();
double fullScaleInputValue();
double fullScaleOutputValue();
double setRxGain(double db, size_t chan);
double getRxGain(size_t chan);
double maxRxGain(void) { return rx_gain_max; }
double minRxGain(void) { return rx_gain_min; }
double setTxGain(double db, size_t chan);
double maxTxGain(void) { return tx_gain_max; }
double minTxGain(void) { return tx_gain_min; }
double getTxFreq(size_t chan);
double getRxFreq(size_t chan);
double getRxFreq();
bool setRxAntenna(const std::string &ant, size_t chan);
std::string getRxAntenna(size_t chan);
bool setTxAntenna(const std::string &ant, size_t chan);
std::string getTxAntenna(size_t chan);
bool requiresRadioAlign();
GSM::Time minLatency();
inline double getSampleRate() { return tx_rate; }
/** Receive and process asynchronous message
@return true if message received or false on timeout or error
*/
bool recv_async_msg();
enum err_code {
ERROR_TIMING = -1,
ERROR_TIMEOUT = -2,
ERROR_UNRECOVERABLE = -3,
ERROR_UNHANDLED = -4,
};
private:
uhd::usrp::multi_usrp::sptr usrp_dev;
uhd::tx_streamer::sptr tx_stream;
uhd::rx_streamer::sptr rx_stream;
enum TxWindowType tx_window;
enum uhd_dev_type dev_type;
double tx_rate, rx_rate;
double tx_gain_min, tx_gain_max;
double rx_gain_min, rx_gain_max;
std::vector<double> tx_gains, rx_gains;
std::vector<double> tx_freqs, rx_freqs;
size_t tx_spp, rx_spp;
bool started;
bool aligned;
size_t drop_cnt;
uhd::time_spec_t prev_ts;
TIMESTAMP ts_initial, ts_offset;
std::vector<smpl_buf *> rx_buffers;
/* Sample buffers used to receive samples from UHD: */
std::vector<std::vector<short> > pkt_bufs;
/* Used to call UHD API: Buffer pointer of each elem in pkt_ptrs will
point to corresponding buffer of vector pkt_bufs. */
std::vector<short *> pkt_ptrs;
void init_gains();
void set_channels(bool swap);
void set_rates();
bool parse_dev_type();
bool flush_recv(size_t num_pkts);
int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
std::string str_code(uhd::rx_metadata_t metadata);
std::string str_code(uhd::async_metadata_t metadata);
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
bool set_freq(double freq, size_t chan, bool tx);
Thread *async_event_thrd;
Mutex tune_lock;
};

View File

@@ -1,6 +1,6 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS)
noinst_HEADERS = USRPDevice.h

View File

@@ -58,12 +58,15 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t sps)
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths)
{
LOG(INFO) << "creating USRP device...";
LOGC(DDEV, INFO) << "creating USRP device...";
this->sps = sps;
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
actualSampleRate = masterClockRate/decimRate;
rxGain = 0;
@@ -73,9 +76,9 @@ USRPDevice::USRPDevice(size_t sps)
* split sample rate Tx/Rx - 4/1 sps we need to need to
* compensate for advance rather than delay.
*/
if (sps == 1)
if (tx_sps == 1)
pingOffset = 272;
else if (sps == 4)
else if (tx_sps == 4)
pingOffset = 269 - 7500;
else
pingOffset = 0;
@@ -92,34 +95,32 @@ int USRPDevice::open(const std::string &, int, bool)
{
writeLock.unlock();
LOG(INFO) << "opening USRP device..";
LOGC(DDEV, INFO) << "opening USRP device..";
#ifndef SWLOOPBACK
string rbf = "std_inband.rbf";
//string rbf = "inband_1rxhb_1tx.rbf";
m_uRx.reset();
if (!skipRx) {
try {
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
0, decimRate * sps, 1, -1,
0, decimRate * tx_sps, 1, -1,
usrp_standard_rx::FPGA_MODE_NORMAL,
1024, 16 * 8, rbf));
m_uRx->set_fpga_master_clock_freq(masterClockRate);
}
catch(...) {
LOG(ALERT) << "make failed on Rx";
LOGC(DDEV, ALERT) << "make failed on Rx";
m_uRx.reset();
return -1;
}
if (m_uRx->fpga_master_clock_freq() != masterClockRate)
{
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
<< ", desired clock freq = " << masterClockRate;
m_uRx.reset();
return -1;
}
}
try {
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
@@ -129,20 +130,20 @@ int USRPDevice::open(const std::string &, int, bool)
}
catch(...) {
LOG(ALERT) << "make failed on Tx";
LOGC(DDEV, ALERT) << "make failed on Tx";
m_uTx.reset();
return -1;
}
if (m_uTx->fpga_master_clock_freq() != masterClockRate)
{
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
<< ", desired clock freq = " << masterClockRate;
m_uTx.reset();
return -1;
}
if (!skipRx) m_uRx->stop();
m_uRx->stop();
m_uTx->stop();
#endif
@@ -172,8 +173,6 @@ int USRPDevice::open(const std::string &, int, bool)
m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
samplesRead = 0;
samplesWritten = 0;
started = false;
return NORMAL;
@@ -183,12 +182,12 @@ int USRPDevice::open(const std::string &, int, bool)
bool USRPDevice::start()
{
LOG(INFO) << "starting USRP...";
LOGC(DDEV, INFO) << "starting USRP...";
#ifndef SWLOOPBACK
if (!m_uRx && !skipRx) return false;
if (!m_uRx) return false;
if (!m_uTx) return false;
if (!skipRx) m_uRx->stop();
m_uRx->stop();
m_uTx->stop();
writeLock.lock();
@@ -218,10 +217,7 @@ bool USRPDevice::start()
isAligned = false;
if (!skipRx)
started = (m_uRx->start() && m_uTx->start());
else
started = m_uTx->start();
return started;
#else
gettimeofday(&lastReadTime,NULL);
@@ -267,7 +263,7 @@ double USRPDevice::minRxGain()
double USRPDevice::setTxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
return 0.0;
}
@@ -277,10 +273,10 @@ double USRPDevice::setTxGain(double dB, size_t chan)
if (dB < minTxGain())
dB = minTxGain();
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
if (!m_dbTx->set_gain(dB))
LOG(ERR) << "Error setting TX gain";
LOGC(DDEV, ERR) << "Error setting TX gain";
writeLock.unlock();
@@ -291,7 +287,7 @@ double USRPDevice::setTxGain(double dB, size_t chan)
double USRPDevice::setRxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
return 0.0;
}
@@ -303,10 +299,10 @@ double USRPDevice::setRxGain(double dB, size_t chan)
if (dB < minRxGain())
dB = minRxGain();
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB.";
if (!m_dbRx->set_gain(dB))
LOG(ERR) << "Error setting RX gain";
LOGC(DDEV, ERR) << "Error setting RX gain";
writeLock.unlock();
@@ -316,40 +312,40 @@ double USRPDevice::setRxGain(double dB, size_t chan)
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
{
if (chan >= rx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
LOG(ALERT) << "Not implemented";
LOGC(DDEV, ALERT) << "Not implemented";
return true;
}
std::string USRPDevice::getRxAntenna(size_t chan)
{
if (chan >= rx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return "";
}
LOG(ALERT) << "Not implemented";
LOGC(DDEV, 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;
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
LOG(ALERT) << "Not implemented";
LOGC(DDEV, ALERT) << "Not implemented";
return true;
}
std::string USRPDevice::getTxAntenna(size_t chan)
{
if (chan >= tx_paths.size()) {
LOG(ALERT) << "Requested non-existent channel " << chan;
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return "";
}
LOG(ALERT) << "Not implemented";
LOGC(DDEV, ALERT) << "Not implemented";
return "";
}
@@ -395,18 +391,18 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
// read USRP packets, parse and save A/D data as needed
readLen = m_uRx->read((void *)readBuf,readLen,overrun);
for(int pktNum = 0; pktNum < (readLen/512); pktNum++) {
for (int pktNum = 0; pktNum < (readLen/512); pktNum++) {
// tmpBuf points to start of a USB packet
uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
uint32_t chan = (word0 >> 16) & 0x1f;
unsigned payloadSz = word0 & 0x1ff;
LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
if (incrementHi32 && (timeStart!=0)) {
LOG(DEBUG) << "high 32 increment!!!";
LOGC(DDEV, DEBUG) << "high 32 increment!!!";
hi32Timestamp++;
}
pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
@@ -418,19 +414,19 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
timestamp -= timestampOffset;
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset;
timestamp += timestampOffset;
isAligned = true;
}
continue;
}
if (chan != 0) {
LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
continue;
}
if ((word0 >> 28) & 0x04) {
if (underrun) *underrun = true;
LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
}
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
@@ -451,7 +447,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
}
}
@@ -459,14 +455,14 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
// copy desired data to buf
unsigned bufStart = dataStart+(timestamp-timeStart);
if (bufStart + len < currDataSize/2) {
LOG(DEBUG) << "bufStart: " << bufStart;
LOGC(DDEV, DEBUG) << "bufStart: " << bufStart;
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
memset(data+bufStart*2,0,len*2*sizeof(short));
}
else {
LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
unsigned firstLength = (currDataSize/2-bufStart);
LOG(DEBUG) << "firstLength: " << firstLength;
LOGC(DDEV, DEBUG) << "firstLength: " << firstLength;
memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
memset(data+bufStart*2,0,firstLength*2*sizeof(short));
memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
@@ -507,7 +503,6 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
gettimeofday(&lastReadTime,NULL);
firstRead = true;
}
samplesRead += numSamples;
return numSamples;
#endif
@@ -557,14 +552,12 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
}
m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
samplesWritten += len/2/sizeof(short);
writeLock.unlock();
return len/2/sizeof(short);
#else
int retVal = len;
memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
samplesWritten += retVal;
loopbackBufferSize += retVal*2;
return retVal;
@@ -596,19 +589,19 @@ bool USRPDevice::setTxFreq(double wFreq, size_t chan)
usrp_tune_result result;
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
return false;
}
if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
LOG(INFO) << "set TX: " << wFreq << std::endl
LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
return true;
}
else {
LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl
LOGC(DDEV, ALERT) << "set TX: " << wFreq << " failed" << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
@@ -621,19 +614,19 @@ bool USRPDevice::setRxFreq(double wFreq, size_t chan)
usrp_tune_result result;
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
return false;
}
if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
LOG(INFO) << "set RX: " << wFreq << std::endl
LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
return true;
}
else {
LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl
LOGC(DDEV, ALERT) << "set RX: " << wFreq << " failed" << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
@@ -648,9 +641,21 @@ bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double offset,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
{
return new USRPDevice(tx_sps);
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
return NULL;
}
if (chans != 1) {
LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
return NULL;
}
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
return NULL;
}
return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -48,15 +48,10 @@ private:
usrp_subdev_spec rxSubdevSpec;
usrp_subdev_spec txSubdevSpec;
int sps;
double actualSampleRate; ///< the actual USRP sampling rate
unsigned int decimRate; ///< the USRP decimation rate
unsigned long long samplesRead; ///< number of samples read from USRP
unsigned long long samplesWritten; ///< number of samples sent to USRP
bool started; ///< flag indicates USRP has started
bool skipRx; ///< set if USRP is transmit-only.
static const unsigned int currDataSize_log2 = 21;
static const unsigned long currDataSize = (1 << currDataSize_log2);
@@ -96,7 +91,9 @@ private:
public:
/** Object constructor */
USRPDevice(size_t sps);
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
/** Instantiate the USRP */
int open(const std::string &, int, bool);
@@ -201,10 +198,6 @@ private:
inline double getTxFreq(size_t chan = 0) { return 0; }
inline double getRxFreq(size_t chan = 0) { return 0; }
inline double getSampleRate() { return actualSampleRate; }
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten; }
std::vector<std::string> tx_paths, rx_paths;
};
#endif // _USRP_DEVICE_H_

View File

@@ -22,16 +22,19 @@
#include "Transceiver.h"
#include "radioDevice.h"
#include "Utils.h"
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <sched.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <sys/signalfd.h>
#include <GSMCommon.h>
#include <Logger.h>
@@ -49,10 +52,13 @@ extern "C" {
#include <osmocom/ctrl/ports.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/command.h>
#include "convolve.h"
#include "convert.h"
#include "trx_vty.h"
#include "debug.h"
#include "osmo_signal.h"
}
#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
@@ -61,6 +67,7 @@ extern "C" {
static char* config_file = (char*)DEFAULT_CONFIG_FILE;
struct osmo_fd signal_ofd;
volatile bool gshutdown = false;
static void *tall_trx_ctx;
@@ -110,6 +117,20 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
return radio;
}
/* Callback function to be called every time we receive a signal from TRANSC */
static int transc_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
switch (signal) {
case S_TRANSC_STOP_REQUIRED:
gshutdown = true;
break;
default:
break;
}
return 0;
}
/* Create transceiver core
* The multi-threaded modem core operates at multiples of the GSM rate of
* 270.8333 ksps and consists of GSM specific modulation, demodulation,
@@ -125,11 +146,13 @@ int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
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)) {
trx->cfg.rach_delay, trx->cfg.egprs, trx->cfg.ext_rach)) {
LOG(ALERT) << "Failed to initialize transceiver";
return -1;
}
transceiver->setSignalHandler(transc_sig_cb);
for (size_t i = 0; i < trx->cfg.num_chans; i++) {
fifo = radio->receiveFIFO(i);
if (fifo && transceiver->receiveFIFO(fifo, i))
@@ -143,6 +166,12 @@ int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
static void sig_handler(int signo)
{
if (gshutdown)
/* We are in the middle of shutdown process, avoid any kind of extra
action like printing */
return;
fprintf(stdout, "signal %d received\n", signo);
switch (signo) {
case SIGINT:
@@ -158,42 +187,68 @@ static void sig_handler(int signo)
case SIGUSR2:
talloc_report_full(tall_trx_ctx, stderr);
break;
case SIGHUP:
log_targets_reopen();
default:
break;
}
}
static int signalfd_callback(struct osmo_fd *ofd, unsigned int what)
{
struct signalfd_siginfo fdsi;
ssize_t s;
s = read(ofd->fd, &fdsi, sizeof(struct signalfd_siginfo));
if (s < 0) {
LOG(FATAL) << "Failed to read from signalfd ("<< ofd->fd << "): " << errno;
gshutdown = true;
return 0;
}
sig_handler(fdsi.ssi_signo);
return 0;
}
static void setup_signal_handlers()
{
/* Handle keyboard interrupt SIGINT */
signal(SIGINT, &sig_handler);
signal(SIGTERM, &sig_handler);
sigset_t set;
int sfd;
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);
/* Other threads created by this thread (main) will inherit a copy of the
signal mask. */
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGUSR2);
sigaddset(&set, SIGHUP);
if (pthread_sigmask(SIG_BLOCK, &set, NULL)) {
fprintf(stderr, "pthread_sigmask() failed.\n");
exit(EXIT_FAILURE);
}
if ((sfd = signalfd(-1, &set, 0)) == -1) {
fprintf(stderr, "signalfd() failed (%d).\n", errno);
exit(EXIT_FAILURE);
}
osmo_fd_setup(&signal_ofd, sfd, BSC_FD_READ, signalfd_callback, NULL, 0);
if (osmo_fd_register(&signal_ofd) < 0) {
fprintf(stderr, "osmo_fd_register() failed.\n");
exit(EXIT_FAILURE);
}
return result;
}
static void print_help()
{
fprintf(stdout, "Options:\n"
" -h This text\n"
" -C Filename The config file to use\n"
" -h, --help This text\n"
" -C, --config Filename The config file to use\n"
" -V, --version Print the version of OsmoTRX\n"
);
}
@@ -210,8 +265,15 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
bool rx_paths_set = false, tx_paths_set = false;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config", 1, 0, 'C'},
{"version", 0, 0, 'V'},
{NULL, 0, 0, 0}
};
while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:")) != -1) {
while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options,
NULL)) != -1) {
switch (option) {
case 'h':
print_help();
@@ -311,6 +373,10 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
case 'C':
config_file = optarg;
break;
case 'V':
print_version(1);
exit(0);
break;
default:
goto bad_config;
}
@@ -345,14 +411,14 @@ bad_config:
int trx_validate_config(struct trx_ctx *trx)
{
if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) {
if (trx->cfg.multi_arfcn && trx->cfg.num_chans > TRX_MCHAN_MAX) {
LOG(ERROR) << "Unsupported number of channels";
return -1;
}
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
if ((trx->cfg.egprs || trx->cfg.multi_arfcn) &&
(trx->cfg.tx_sps!=4 || trx->cfg.tx_sps!=4)) {
(trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) {
LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config.";
return -1;
}
@@ -366,7 +432,7 @@ static int set_sched_rr(unsigned int prio)
int rc;
memset(&param, 0, sizeof(param));
param.sched_priority = prio;
printf("Setting SCHED_RR priority(%d)\n", param.sched_priority);
LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority;
rc = sched_setscheduler(getpid(), SCHED_RR, &param);
if (rc != 0) {
LOG(ERROR) << "Config: Setting SCHED_RR failed";
@@ -390,6 +456,7 @@ static void print_config(struct trx_ctx *trx)
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 << " Extended RACH support... " << trx->cfg.ext_rach << 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;
@@ -409,12 +476,12 @@ static void print_config(struct trx_ctx *trx)
}
ost << std::endl;
std::cout << ost << std::endl;
LOG(INFO) << ost << std::endl;
}
static void trx_stop()
{
std::cout << "Shutting down transceiver..." << std::endl;
LOG(NOTICE) << "Shutting down transceiver..." << std::endl;
delete transceiver;
delete radio;
@@ -457,7 +524,7 @@ static int trx_start(struct trx_ctx *trx)
goto shutdown;
chans = transceiver->numChans();
std::cout << "-- Transceiver active with "
LOG(NOTICE) << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl;
return 0;
@@ -532,7 +599,7 @@ int main(int argc, char *argv[])
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
fprintf(stderr, "Failed to create CTRL interface.\n");
LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1);
}
@@ -569,5 +636,7 @@ int main(int argc, char *argv[])
trx_stop();
osmo_fd_unregister(&signal_ofd);
osmo_fd_close(&signal_ofd);
return 0;
}

View File

@@ -75,6 +75,14 @@ bool RadioInterface::init(int type)
void RadioInterface::close()
{
for (std::vector<RadioBuffer*>::iterator it = sendBuffer.begin(); it != sendBuffer.end(); ++it)
delete *it;
for (std::vector<RadioBuffer*>::iterator it = recvBuffer.begin(); it != recvBuffer.end(); ++it)
delete *it;
for (std::vector<short*>::iterator it = convertSendBuffer.begin(); it != convertSendBuffer.end(); ++it)
delete[] *it;
for (std::vector<short*>::iterator it = convertRecvBuffer.begin(); it != convertRecvBuffer.end(); ++it)
delete[] *it;
sendBuffer.resize(0);
recvBuffer.resize(0);
convertSendBuffer.resize(0);
@@ -148,6 +156,7 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
/** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
set_selfthread_name("AlignRadio");
while (1) {
sleep(60);
radioInterface->alignRadio();
@@ -218,14 +227,15 @@ void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
while (pushBuffer());
}
bool RadioInterface::driveReceiveRadio()
int RadioInterface::driveReceiveRadio()
{
radioVector *burst = NULL;
if (!mOn)
return false;
return 0;
pullBuffer();
if (pullBuffer() < 0)
return -1;
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
@@ -239,11 +249,11 @@ bool RadioInterface::driveReceiveRadio()
else
burstSize = symbolsPerSlot + (tN % 4 == 0);
/*
/*
* Pre-allocate head room for the largest correlation size
* so we can later avoid a re-allocation and copy
* */
size_t head = GSM::gRACHSynchSequence.size();
size_t head = GSM::gRACHSynchSequenceTS0.size();
/*
* Form receive bursts and pass up to transceiver. Use repeating
@@ -270,7 +280,7 @@ bool RadioInterface::driveReceiveRadio()
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
}
return true;
return 1;
}
bool RadioInterface::isUnderrun()
@@ -300,13 +310,14 @@ double RadioInterface::getRxGain(size_t chan)
}
/* Receive a timestamped chunk from the device */
void RadioInterface::pullBuffer()
int RadioInterface::pullBuffer()
{
bool local_underrun;
size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
int numRecv;
size_t segmentLen = recvBuffer[0]->getSegmentLen();
if (recvBuffer[0]->getFreeSegments() <= 0)
return;
return -1;
/* Outer buffer access size is fixed */
numRecv = mRadio->readSamples(convertRecvBuffer,
@@ -315,9 +326,9 @@ void RadioInterface::pullBuffer()
readTimestamp,
&local_underrun);
if (numRecv != segmentLen) {
if ((size_t) numRecv != segmentLen) {
LOG(ALERT) << "Receive error " << numRecv;
return;
return -1;
}
for (size_t i = 0; i < mChans; i++) {
@@ -328,6 +339,7 @@ void RadioInterface::pullBuffer()
underrun |= local_underrun;
readTimestamp += numRecv;
return 0;
}
/* Send timestamped chunk to the device with arbitrary size */

View File

@@ -14,7 +14,7 @@
#include "sigProcLib.h"
#include "sigProcLib.h"
#include "GSMCommon.h"
#include "LinkedLists.h"
#include "radioDevice.h"
@@ -71,7 +71,7 @@ private:
virtual bool pushBuffer(void);
/** pull GSM bursts from the receive buffer */
virtual void pullBuffer(void);
virtual int pullBuffer(void);
public:
@@ -116,8 +116,8 @@ public:
void driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros);
/** drive reception of GSM bursts */
bool driveReceiveRadio();
/** drive reception of GSM bursts. -1: Error. 0: Radio off. 1: Received something. */
int driveReceiveRadio();
int setPowerAttenuation(int atten, size_t chan = 0);
@@ -130,7 +130,7 @@ public:
/** set thread priority on current thread */
void setPriority(float prio = 0.5) { mRadio->setPriority(prio); }
/** get transport window type of attached device */
/** get transport window type of attached device */
enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
/** Minimum latency that the device can achieve */
@@ -149,7 +149,7 @@ private:
signalVector *outerRecvBuffer;
bool pushBuffer();
void pullBuffer();
int pullBuffer();
public:
RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
@@ -162,7 +162,7 @@ public:
class RadioInterfaceMulti : public RadioInterface {
private:
bool pushBuffer();
void pullBuffer();
int pullBuffer();
signalVector *outerSendBuffer;
signalVector *outerRecvBuffer;

View File

@@ -1,7 +1,7 @@
/*
* Multi-carrier radio interface
*
* Copyright (C) 2016 Ettus Research LLC
* Copyright (C) 2016 Ettus Research LLC
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
@@ -225,14 +225,15 @@ bool RadioInterfaceMulti::init(int type)
}
/* Receive a timestamped chunk from the device */
void RadioInterfaceMulti::pullBuffer()
int RadioInterfaceMulti::pullBuffer()
{
bool local_underrun;
size_t num;
float *buf;
unsigned int i;
if (recvBuffer[0]->getFreeSegments() <= 0)
return;
return -1;
/* Outer buffer access size is fixed */
num = mRadio->readSamples(convertRecvBuffer,
@@ -242,7 +243,7 @@ void RadioInterfaceMulti::pullBuffer()
&local_underrun);
if (num != channelizer->inputLen()) {
LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
return;
return -1;
}
convert_short_float((float *) outerRecvBuffer->begin(),
@@ -273,10 +274,22 @@ void RadioInterfaceMulti::pullBuffer()
buf = channelizer->outputBuffer(pchan);
size_t cLen = channelizer->outputLen();
size_t hLen = dnsampler->len();
size_t hSize = 2 * hLen * sizeof(float);
memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
float *fdst = &buf[2 * -hLen];
complex *src = history[lchan]->begin();
for (i = 0; i < hLen; i++) {
fdst[0] = src->real();
fdst[1] = src->imag();
src++;
fdst += 2;
}
complex *dst = history[lchan]->begin();
float *fsrc = &buf[2 * (cLen - hLen)];
for (i = 0; i < hLen; i++) {
*dst = complex(fdst[0], fdst[1]);
fsrc += 2;
dst++;
}
float *wr_segment = recvBuffer[lchan]->getWriteSegment();
@@ -288,6 +301,7 @@ void RadioInterfaceMulti::pullBuffer()
LOG(ALERT) << "Sample rate upsampling error";
}
}
return 0;
}
/* Send a timestamped chunk to the device */

View File

@@ -160,13 +160,13 @@ bool RadioInterfaceResamp::init(int type)
}
/* Receive a timestamped chunk from the device */
void RadioInterfaceResamp::pullBuffer()
int RadioInterfaceResamp::pullBuffer()
{
bool local_underrun;
int rc, num_recv;
if (recvBuffer[0]->getFreeSegments() <= 0)
return;
return -1;
/* Outer buffer access size is fixed */
num_recv = mRadio->readSamples(convertRecvBuffer,
@@ -176,7 +176,7 @@ void RadioInterfaceResamp::pullBuffer()
&local_underrun);
if (num_recv != (int) resamp_outchunk) {
LOG(ALERT) << "Receive error " << num_recv;
return;
return -1;
}
convert_short_float((float *) outerRecvBuffer->begin(),
@@ -196,6 +196,7 @@ void RadioInterfaceResamp::pullBuffer()
/* Set history for the next chunk */
outerRecvBuffer->updateHistory();
return 0;
}
/* Send a timestamped chunk to the device */

View File

@@ -84,14 +84,13 @@ static Resampler *dnsampler = NULL;
* perform 16-byte memory alignment required by many SSE instructions.
*/
struct CorrelationSequence {
CorrelationSequence() : sequence(NULL), buffer(NULL)
CorrelationSequence() : sequence(NULL)
{
}
~CorrelationSequence()
{
delete sequence;
free(buffer);
}
signalVector *sequence;
@@ -106,8 +105,7 @@ struct CorrelationSequence {
* for SSE instructions.
*/
struct PulseSequence {
PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL),
c0_buffer(NULL), c1_buffer(NULL), c0_inv_buffer(NULL)
PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL)
{
}
@@ -117,22 +115,17 @@ struct PulseSequence {
delete c1;
delete c0_inv;
delete empty;
free(c0_buffer);
free(c1_buffer);
}
signalVector *c0;
signalVector *c1;
signalVector *c0_inv;
signalVector *empty;
void *c0_buffer;
void *c1_buffer;
void *c0_inv_buffer;
};
static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static CorrelationSequence *gRACHSequence = NULL;
static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
static PulseSequence *GSMPulse1 = NULL;
static PulseSequence *GSMPulse4 = NULL;
@@ -150,11 +143,15 @@ void sigProcLibDestroy()
delayFilters[i] = NULL;
}
for (int i = 0; i < 3; i++) {
delete gRACHSequences[i];
gRACHSequences[i] = NULL;
}
delete GMSKRotation1;
delete GMSKReverseRotation1;
delete GMSKRotation4;
delete GMSKReverseRotation4;
delete gRACHSequence;
delete GSMPulse1;
delete GSMPulse4;
delete dnsampler;
@@ -163,7 +160,6 @@ void sigProcLibDestroy()
GMSKRotation4 = NULL;
GMSKReverseRotation4 = NULL;
GMSKReverseRotation1 = NULL;
gRACHSequence = NULL;
GSMPulse1 = NULL;
GSMPulse4 = NULL;
}
@@ -289,8 +285,7 @@ enum ConvType {
static signalVector *convolve(const signalVector *x, const signalVector *h,
signalVector *y, ConvType spanType,
size_t start = 0, size_t len = 0,
size_t step = 1, int offset = 0)
size_t start = 0, size_t len = 0)
{
int rc;
size_t head = 0, tail = 0;
@@ -337,7 +332,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
if (y && (len > y->size()))
return NULL;
if (!y) {
y = new signalVector(len);
y = new signalVector(len, convolve_h_alloc, free);
alloc = true;
}
@@ -358,22 +353,22 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
rc = convolve_real((float *) _x->begin(), _x->size(),
(float *) h->begin(), h->size(),
(float *) y->begin(), y->size(),
start, len, step, offset);
start, len);
} else if (!h->isReal() && h->isAligned()) {
rc = convolve_complex((float *) _x->begin(), _x->size(),
(float *) h->begin(), h->size(),
(float *) y->begin(), y->size(),
start, len, step, offset);
start, len);
} else if (h->isReal() && !h->isAligned()) {
rc = base_convolve_real((float *) _x->begin(), _x->size(),
(float *) h->begin(), h->size(),
(float *) y->begin(), y->size(),
start, len, step, offset);
start, len);
} else if (!h->isReal() && !h->isAligned()) {
rc = base_convolve_complex((float *) _x->begin(), _x->size(),
(float *) h->begin(), h->size(),
(float *) y->begin(), y->size(),
start, len, step, offset);
start, len);
} else {
rc = -1;
}
@@ -400,8 +395,7 @@ static bool generateInvertC0Pulse(PulseSequence *pulse)
if (!pulse)
return false;
pulse->c0_inv_buffer = convolve_h_alloc(5);
pulse->c0_inv = new signalVector((complex *) pulse->c0_inv_buffer, 0, 5);
pulse->c0_inv = new signalVector((complex *) convolve_h_alloc(5), 0, 5, convolve_h_alloc, free);
pulse->c0_inv->isReal(true);
pulse->c0_inv->setAligned(false);
@@ -430,9 +424,7 @@ static bool generateC1Pulse(int sps, PulseSequence *pulse)
return false;
}
pulse->c1_buffer = convolve_h_alloc(len);
pulse->c1 = new signalVector((complex *)
pulse->c1_buffer, 0, len);
pulse->c1 = new signalVector((complex *) convolve_h_alloc(len), 0, len, convolve_h_alloc, free);
pulse->c1->isReal(true);
/* Enable alignment for SSE usage */
@@ -486,8 +478,7 @@ static PulseSequence *generateGSMPulse(int sps)
len = 4;
}
pulse->c0_buffer = convolve_h_alloc(len);
pulse->c0 = new signalVector((complex *) pulse->c0_buffer, 0, len);
pulse->c0 = new signalVector((complex *) convolve_h_alloc(len), 0, len, convolve_h_alloc, free);
pulse->c0->isReal(true);
/* Enable alingnment for SSE usage */
@@ -1016,7 +1007,7 @@ static void generateDelayFilters()
for (int i = 0; i < DELAYFILTS; i++) {
data = (complex *) convolve_h_alloc(h_len);
h = new signalVector(data, 0, h_len);
h = new signalVector(data, 0, h_len, convolve_h_alloc, free);
h->setAligned(true);
h->isReal(true);
@@ -1102,17 +1093,17 @@ static complex interpolatePoint(const signalVector &inSig, float ix)
if (start < 0) start = 0;
int end = (int) (floor(ix) + 11);
if ((unsigned) end > inSig.size()-1) end = inSig.size()-1;
complex pVal = 0.0;
if (!inSig.isReal()) {
for (int i = start; i < end; i++)
for (int i = start; i < end; i++)
pVal += inSig[i] * sinc(M_PI_F*(i-ix));
}
else {
for (int i = start; i < end; i++)
for (int i = start; i < end; i++)
pVal += inSig[i].real() * sinc(M_PI_F*(i-ix));
}
return pVal;
}
@@ -1157,12 +1148,12 @@ static complex peakDetect(const signalVector &rxBurst,
// to save computation, we'll use early-late balancing
float earlyIndex = maxIndex-1;
float lateIndex = maxIndex+1;
float incr = 0.5;
while (incr > 1.0/1024.0) {
complex earlyP = interpolatePoint(rxBurst,earlyIndex);
complex lateP = interpolatePoint(rxBurst,lateIndex);
if (earlyP < lateP)
if (earlyP < lateP)
earlyIndex += incr;
else if (earlyP > lateP)
earlyIndex -= incr;
@@ -1250,7 +1241,7 @@ static bool generateMidamble(int sps, int tsc)
// NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst,
// the ideal TSC has an + 180 degree phase shift,
// due to the pi/2 frequency shift, that
// due to the pi/2 frequency shift, that
// needs to be accounted for.
// 26-midamble is 61 symbols into burst, has +90 degree phase shift.
scaleVector(*midMidamble, complex(-1.0, 0.0));
@@ -1260,10 +1251,9 @@ static bool generateMidamble(int sps, int tsc)
/* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
data = (complex *) convolve_h_alloc(midMidamble->size());
_midMidamble = new signalVector(data, 0, midMidamble->size());
_midMidamble = new signalVector(data, 0, midMidamble->size(), convolve_h_alloc, free);
_midMidamble->setAligned(true);
memcpy(_midMidamble->begin(), midMidamble->begin(),
midMidamble->size() * sizeof(complex));
midMidamble->copyTo(*_midMidamble);
autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY);
if (!autocorr) {
@@ -1272,7 +1262,6 @@ static bool generateMidamble(int sps, int tsc)
}
gMidambles[tsc] = new CorrelationSequence;
gMidambles[tsc]->buffer = data;
gMidambles[tsc]->sequence = _midMidamble;
gMidambles[tsc]->gain = peakDetect(*autocorr, &toa, NULL);
@@ -1317,14 +1306,12 @@ static CorrelationSequence *generateEdgeMidamble(int tsc)
conjugateVector(*midamble);
data = (complex *) convolve_h_alloc(midamble->size());
_midamble = new signalVector(data, 0, midamble->size());
_midamble = new signalVector(data, 0, midamble->size(), convolve_h_alloc, free);
_midamble->setAligned(true);
memcpy(_midamble->begin(), midamble->begin(),
midamble->size() * sizeof(complex));
midamble->copyTo(*_midamble);
/* Channel gain is an empirically measured value */
seq = new CorrelationSequence;
seq->buffer = data;
seq->sequence = _midamble;
seq->gain = Complex<float>(-19.6432, 19.5006) / 1.18;
seq->toa = 0;
@@ -1334,7 +1321,7 @@ static CorrelationSequence *generateEdgeMidamble(int tsc)
return seq;
}
static bool generateRACHSequence(int sps)
static bool generateRACHSequence(CorrelationSequence **seq, const BitVector &bv, int sps)
{
bool status = true;
float toa;
@@ -1342,13 +1329,14 @@ static bool generateRACHSequence(int sps)
signalVector *autocorr = NULL;
signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
delete gRACHSequence;
if (*seq != NULL)
delete *seq;
seq0 = modulateBurst(gRACHSynchSequence, 0, sps, false);
seq0 = modulateBurst(bv, 0, sps, false);
if (!seq0)
return false;
seq1 = modulateBurst(gRACHSynchSequence.segment(0, 40), 0, sps, true);
seq1 = modulateBurst(bv.segment(0, 40), 0, sps, true);
if (!seq1) {
status = false;
goto release;
@@ -1358,9 +1346,9 @@ static bool generateRACHSequence(int sps)
/* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
data = (complex *) convolve_h_alloc(seq1->size());
_seq1 = new signalVector(data, 0, seq1->size());
_seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free);
_seq1->setAligned(true);
memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex));
seq1->copyTo(*_seq1);
autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
if (!autocorr) {
@@ -1368,19 +1356,18 @@ static bool generateRACHSequence(int sps)
goto release;
}
gRACHSequence = new CorrelationSequence;
gRACHSequence->sequence = _seq1;
gRACHSequence->buffer = data;
gRACHSequence->gain = peakDetect(*autocorr, &toa, NULL);
*seq = new CorrelationSequence;
(*seq)->sequence = _seq1;
(*seq)->gain = peakDetect(*autocorr, &toa, NULL);
/* For 1 sps only
* (Half of correlation length - 1) + midpoint of pulse shaping filer
* 20.5 = (40 / 2 - 1) + 1.5
*/
if (sps == 1)
gRACHSequence->toa = toa - 20.5;
(*seq)->toa = toa - 20.5;
else
gRACHSequence->toa = 0.0;
(*seq)->toa = 0.0;
release:
delete autocorr;
@@ -1390,7 +1377,7 @@ release:
if (!status) {
delete _seq1;
free(data);
gRACHSequence = NULL;
*seq = NULL;
}
return status;
@@ -1457,7 +1444,7 @@ static signalVector *downsampleBurst(const signalVector &burst)
{
signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len());
signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN);
memcpy(in.begin(), burst.begin(), DOWNSAMPLE_IN_LEN * 2 * sizeof(float));
burst.copyToSegment(in, 0, DOWNSAMPLE_IN_LEN);
if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
(float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
@@ -1468,6 +1455,28 @@ static signalVector *downsampleBurst(const signalVector &burst)
return out;
};
static float computeCI(const signalVector *burst, CorrelationSequence *sync,
float toa, int start, complex xcorr)
{
float S, C;
int ps;
/* Integer position where the sequence starts */
ps = start + 1 - sync->sequence->size() + (int)roundf(toa);
/* Estimate Signal power */
S = 0.0f;
for (int i=0, j=ps; i<(int)sync->sequence->size(); i++,j++)
S += (*burst)[j].norm2();
S /= sync->sequence->size();
/* Esimate Carrier power */
C = xcorr.norm2() / ((sync->sequence->size() - 1) * sync->gain.abs());
/* Interference = Signal - Carrier, so C/I = C / (S - C) */
return 3.0103f * log2f(C / (S - C));
}
/*
* Detect a burst based on correlation and peak-to-average ratio
*
@@ -1483,6 +1492,7 @@ static int detectBurst(const signalVector &burst,
{
const signalVector *corr_in;
signalVector *dec = NULL;
complex xcorr;
if (sps == 4) {
dec = downsampleBurst(burst);
@@ -1494,7 +1504,7 @@ static int detectBurst(const signalVector &burst,
/* Correlate */
if (!convolve(corr_in, sync->sequence, &corr,
CUSTOM, start, len, 1, 0)) {
CUSTOM, start, len)) {
delete dec;
return -1;
}
@@ -1510,15 +1520,18 @@ static int detectBurst(const signalVector &burst,
if ((*toa < 3 * sps) || (*toa > len - 3 * sps))
return 0;
/* Peak -to-average ratio */
/* Peak-to-average ratio */
if (computePeakRatio(&corr, sps, *toa, *amp) < thresh)
return 0;
/* Compute peak-to-average ratio. Reject if we don't have enough values */
*amp = peakDetect(corr, toa, NULL);
/* Refine TOA and correlation value */
xcorr = peakDetect(corr, toa, NULL);
/* Compute C/I */
float CI = computeCI(corr_in, sync, *toa, start, xcorr);
/* Normalize our channel gain */
*amp = *amp / sync->gain;
*amp = xcorr / sync->gain;
/* Compensate for residuate time lag */
*toa = *toa - sync->toa;
@@ -1591,7 +1604,7 @@ static int detectGeneralBurst(const signalVector &rxBurst,
}
/*
/*
* RACH burst detection
*
* Correlation window parameters:
@@ -1600,23 +1613,27 @@ static int detectGeneralBurst(const signalVector &rxBurst,
* tail: Search 8 symbols + maximum expected delay
*/
static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
complex &amplitude, float &toa, unsigned max_toa)
complex &amplitude, float &toa, unsigned max_toa, bool ext)
{
int rc, target, head, tail;
CorrelationSequence *sync;
int i, num_seq;
target = 8 + 40;
head = 8;
tail = 8 + max_toa;
sync = gRACHSequence;
num_seq = ext ? 3 : 1;
rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
target, head, tail, sync);
for (i = 0; i < num_seq; i++) {
rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
target, head, tail, gRACHSequences[i]);
if (rc > 0)
break;
}
return rc;
}
/*
/*
* Normal burst detection
*
* Correlation window parameters:
@@ -1680,9 +1697,10 @@ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
rc = analyzeTrafficBurst(burst, tsc, threshold, sps,
amp, toa, max_toa);
break;
case EXT_RACH:
case RACH:
rc = detectRACHBurst(burst, threshold, sps, amp, toa,
max_toa);
max_toa, type == EXT_RACH);
break;
default:
LOG(ERR) << "Invalid correlation type";
@@ -1860,7 +1878,10 @@ bool sigProcLibSetup()
GSMPulse1 = generateGSMPulse(1);
GSMPulse4 = generateGSMPulse(4);
generateRACHSequence(1);
generateRACHSequence(&gRACHSequences[0], gRACHSynchSequenceTS0, 1);
generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
for (int tsc = 0; tsc < 8; tsc++) {
generateMidamble(1, tsc);
gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);

View File

@@ -29,6 +29,7 @@
enum CorrType{
OFF, ///< timeslot is off
TSC, ///< timeslot should contain a normal burst
EXT_RACH, ///< timeslot should contain an extended access burst
RACH, ///< timeslot should contain an access burst
EDGE, ///< timeslot should contain an EDGE burst
IDLE ///< timeslot is an idle (or dummy) burst

View File

@@ -1,20 +1,20 @@
#include "signalVector.h"
signalVector::signalVector(size_t size)
: Vector<complex>(size),
signalVector::signalVector(size_t size, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)
: Vector<complex>(size, wAllocFunc, wFreeFunc),
real(false), aligned(false), symmetry(NONE)
{
}
signalVector::signalVector(size_t size, size_t start)
: Vector<complex>(size + start),
signalVector::signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)
: Vector<complex>(size + start, wAllocFunc, wFreeFunc),
real(false), aligned(false), symmetry(NONE)
{
mStart = mData + start;
}
signalVector::signalVector(complex *data, size_t start, size_t span)
: Vector<complex>(NULL, data + start, data + start + span),
signalVector::signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)
: Vector<complex>(data, data + start, data + start + span, wAllocFunc, wFreeFunc),
real(false), aligned(false), symmetry(NONE)
{
}
@@ -41,7 +41,14 @@ signalVector::signalVector(const signalVector &vector,
void signalVector::operator=(const signalVector& vector)
{
resize(vector.size() + vector.getStart());
memcpy(mData, vector.mData, bytes());
unsigned int i;
complex *dst = mData;
complex *src = vector.mData;
for (i = 0; i < size(); i++, src++, dst++)
*dst = *src;
/* TODO: optimize for non non-trivially copyable types: */
/*memcpy(mData, vector.mData, bytes()); */
mStart = mData + vector.getStart();
}
@@ -58,8 +65,13 @@ size_t signalVector::getStart() const
size_t signalVector::updateHistory()
{
size_t num = getStart();
memmove(mData, mStart + this->size() - num, num * sizeof(complex));
unsigned int i;
complex *dst = mData;
complex *src = mStart + this->size() - num;
for (i = 0; i < num; i++, src++, dst++)
*dst = *src;
/* TODO: optimize for non non-trivially copyable types: */
/*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */
return num;
}

View File

@@ -13,13 +13,13 @@ enum Symmetry {
class signalVector: public Vector<complex> {
public:
/** Default constructor */
signalVector(size_t size = 0);
signalVector(size_t size = 0, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
/** Construct with head room */
signalVector(size_t size, size_t start);
signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
/** Construct from existing buffer data (buffer not managed) */
signalVector(complex *data, size_t start, size_t span);
signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);
/** Construct by from existing vector */
signalVector(const signalVector &vector);

View File

@@ -49,6 +49,7 @@ AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_INSTALL
AC_PATH_PROG([RM_PROG], [rm])
AC_LANG([C++])
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
@@ -90,6 +91,25 @@ then
LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined"
fi
AC_ARG_ENABLE(werror,
[AS_HELP_STRING(
[--enable-werror],
[Turn all compiler warnings into errors, with exceptions:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
]
)],
[werror=$enableval], [werror="no"])
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
fi
AC_ARG_WITH(uhd, [
AS_HELP_STRING([--with-uhd],
[enable UHD based transceiver])
@@ -100,6 +120,11 @@ AC_ARG_WITH(usrp1, [
[enable USRP1 gnuradio based transceiver])
])
AC_ARG_WITH(lms, [
AS_HELP_STRING([--with-lms],
[enable LimeSuite based transceiver])
])
AC_ARG_WITH(singledb, [
AS_HELP_STRING([--with-singledb],
[enable single daughterboard use on USRP1])
@@ -130,9 +155,15 @@ AS_IF([test "x$with_neon_vfpv4" = "xyes"], [
])
AS_IF([test "x$with_usrp1" = "xyes"], [
AC_CHECK_HEADER([boost/config.hpp],[],
[AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])])
PKG_CHECK_MODULES(USRP, usrp >= 3.3)
])
AS_IF([test "x$with_lms" = "xyes"], [
PKG_CHECK_MODULES(LMS, LimeSuite)
])
AS_IF([test "x$with_uhd" != "xno"],[
PKG_CHECK_MODULES(UHD, uhd >= 003.011,
[AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)],
@@ -184,14 +215,70 @@ CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" != "xno"])
AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
PKG_CHECK_MODULES(FFTWF, fftw3f)
AC_CHECK_HEADER([boost/config.hpp],[],
[AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])])
# Generate manuals
AC_ARG_ENABLE(manuals,
[AS_HELP_STRING(
[--enable-manuals],
[Generate manual PDFs [default=no]],
)],
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
fallback])
if test x"$osmo_ac_build_manuals" = x"yes"
then
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
else
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
else
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
fi
fi
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
fi
# Find and run check-depends
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
if ! test -x "$CHECK_DEPENDS"; then
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
fi
if ! $CHECK_DEPENDS; then
AC_MSG_ERROR("missing dependencies for --enable-manuals")
fi
# Put in Makefile with absolute path
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
AC_SUBST([OSMO_GSM_MANUALS_DIR])
fi
# https://www.freedesktop.org/software/systemd/man/daemon.html
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
[with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
with_systemdsystemunitdir=no],
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
@@ -209,11 +296,18 @@ AC_CONFIG_FILES([\
Transceiver52M/arch/arm/Makefile \
Transceiver52M/arch/x86/Makefile \
Transceiver52M/device/Makefile \
Transceiver52M/device/common/Makefile \
Transceiver52M/device/uhd/Makefile \
Transceiver52M/device/usrp1/Makefile \
Transceiver52M/device/lms/Makefile \
tests/Makefile \
tests/CommonLibs/Makefile \
tests/Transceiver52M/Makefile \
doc/Makefile \
doc/examples/Makefile \
contrib/Makefile \
contrib/systemd/Makefile \
])
AC_OUTPUT
AC_OUTPUT(
doc/manuals/Makefile)

1
contrib/Makefile.am Normal file
View File

@@ -0,0 +1 @@
SUBDIRS = systemd

View File

@@ -1,4 +1,12 @@
#!/bin/sh
# jenkins build helper script for osmo-trx. This is how we build on jenkins.osmocom.org
#
# environment variables:
# * INSTR: configure the CPU instruction set ("--with-sse", "--with-neon" or "--with-neon-vfpv4")
# * WITH_MANUALS: build manual PDFs if set to "1"
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
# * INSIDE_CHROOT: (used internally) set to "1" when the script runs with QEMU in an ARM chroot
#
set -ex
substr() { [ -z "${2##*$1*}" ]; }
@@ -68,6 +76,15 @@ osmo-build-dep.sh libusrp
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms $INSTR"
# Additional configure options and depends
if [ "$WITH_MANUALS" = "1" ]; then
osmo-build-dep.sh osmo-gsm-manuals
CONFIG="$CONFIG --enable-manuals"
fi
set +x
echo
@@ -79,9 +96,15 @@ set -x
cd "$base"
autoreconf --install --force
./configure --enable-sanitize --with-uhd --with-usrp1 $INSTR
./configure $CONFIG
$MAKE $PARALLEL_MAKE
$MAKE check \
|| cat-testlogs.sh
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck \
|| cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
osmo-clean-workspace.sh

View File

@@ -0,0 +1,22 @@
EXTRA_DIST = \
osmo-trx-lms.service \
osmo-trx-uhd.service \
osmo-trx-usrp1.service
if HAVE_SYSTEMD
SYSTEMD_SERVICES =
if DEVICE_UHD
SYSTEMD_SERVICES += osmo-trx-uhd.service
endif
if DEVICE_USRP1
SYSTEMD_SERVICES += osmo-trx-usrp1.service
endif
if DEVICE_LMS
SYSTEMD_SERVICES += osmo-trx-lms.service
endif
systemdsystemunit_DATA = $(SYSTEMD_SERVICES)
endif # HAVE_SYSTEMD

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg
RestartSec=2
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
RestartSec=2
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg
RestartSec=2
[Install]
WantedBy=multi-user.target

146
debian/changelog vendored
View File

@@ -1,3 +1,149 @@
osmo-trx (1.0.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* doc: examples: Add umtrx sample config
* UHDDevice: Fix setup failure with LimeSuite > 18.04.1
* examples: Set rt-prio 18 and print file basename
* lms: Several improvements and compilation/runtime fixes
* build: Add support for LimeSuite device backend
* LMSDevice: Set correct values for Max{Tx,Rx}Gain
* LMSDevice: Fix setup failure with LimeSuite > 18.04.1
* lms: Makefile.am: Reorder params to fix link issue
* lms: Check LPBFW to set is within supported range
* debian: Add package osmo-trx-lms
* contrib: Add systemd services for all backends
* debian: Add cfg file examples for osmo-trx-{lms,uhd}
* Add -V param to print version
* lms: Allow values diff than 34dB to be set by setRxGain()
* Use correct paths when installing example files
* debian: Enable build of osmo-trx-lms
* debian: Explicitly enable osmo-trx-uhd build
* configure.ac: Fix typo in with-lms help string
* vty: Fix typo in gpsdo clock reference type
* configure.ac: Add --enable-werror option
* Logger: Disable pthread cancel point inside Logger destructor
* cosmetic: Fix trailing whitespace
* radioInterface: forward errors from RadioDevice to Transceiver in recv path
* lms: Return error on device read timeout
* osmo-trx: Add osmo_signal to stop whole transceiver chain correctly on error
* radioInterface: Fix variable storing integer return value
* configure.ac: Specify default language as C++
* UHHDDevice: Replace deprecated header uhd/utils/thread_priority.hpp
* SigProcLib: Use available copyTo Vector API instead of memcopy
* cosmetic: Fix trailing whitespace in several files
* radioInterfaceMulti:pullBuffer: Sanely convert float array to complex array
* Vector: Copy arrays in a sane way for non-trivially copyable types
* jenkins.sh: Add --enable-werror flag to osmo-trx configure step
* Install systemd services with autotools
* Install sample cfg file to /etc/osmocom
* cosmetic: Use proper whitespace in several for loops
* Use pthread_setname_np to name threads
* CommonLibs/Makefile.am: Specify libcommon_la_LIBADD
* Transciever: Log values causing Tx underrun
* examples: Use logging level 'set-all' instead of 'all'
* jenkins.sh: Enable build of osmo-trx-lms
* ChannelizerBase: Fix ASan alloc-dealloc-mismatch
* UHDDevice: setRxGain on chan 0 when using multi-arfcn
* lms: Use LimeSuite.h log level defines instead of hardcoded values
* lms: Apply LMS->OSMO log level conversion
* Introduce OsmoTRX manual
* Introduce chapter trx_if.adoc and add it to OsmoTRX and OsmoBTS
* trx: Add reference to project wiki page in overfiew section
* trx: Add Hardware architecture support section
* trx: Add Hardware device support section
* osmotrx: Split Device specific section from backend one
* osmotrx: Write initial documentation for several supported devices
* osmotrx: configuration: Add section to document multi-arfcn feature
* osmotrx: Create a common chapter for section documenting backends
* osmotrx: Introduce code architecture chapter
* lms: Fix start after stop of device
* lms: Destroy streams on device stop
* radioInterface: Fix memleak during close()
* PointerFIFO: Fix memleak of ListNode
* lms: Make sure LMS_Close is called when Device is torn down
* osmo-trx: Change some lines to use libosmocore logging instead of cout
* lms: Close device on LMS_Init failure
* SigProcLib: Improve Vector buffer allocation mess
* lms: Allow setting Tx/RxGain for chan!=0
* lms: Allow setting Tx/RxFreq for lchan!=0
* lms: Improve Set{Rx,Tx}{Gain,Freq} logging
* transceiver: log chan on CTRL command received
* Add TRXCTRL log category
* transceiver: Log TRXCTRL iface responses towards osmo-bts-trx
* lms: Move {under,over}run checks into separate method
* lms: Do {under,over}run checks even if LMS_RecvStream fails
* Timeval: passed() returns true if time is equal
* Timeval: Move implementation to use clock_gettime and timespec
* Timeval: Move to osmo_clock_gettime
* TimevalTest: Make test deterministic with fake time
* lms: Fix build against LimeSuite > 18.10
* configure.ac: check boost only if USRP1 support is enabled
[ Vadim Yanitskiy ]
* trx_vty.c: fix: use CONFIG_NODE as parent by default
* device/lms/LMSDevice.cpp: fix compilation warning
* sigProcLib: introduce both TS1 and TS2 RACH synch. sequences
* sigProcLib: add a CorrType for extended (11-bit) RACH
[ Harald Welte ]
* Initial work towards direct LimeSuite support in OsmoTRX
* update .gitignore to include osmo-trx-lms
* LMSDevice: Call LMS_Init() before setting sample rate
* LMSDevice: Print sample rate range + actual sample rate after setting it
* LMSDevice: Typo fix: s/Internal/External
* LMSDevice: Set low-pass filters to smallest possible option
* LMSDevice: Fix initial timestamp offset of 2500
* LMS_Device: Set ts_offset to 0.
* LMSDevice: Reduce Rx logging verbosity: Only log unexpected timestamps
* move set_antennas() from UHD to generic radioDevice base class
* lms: Fix support for rx_paths / tx_paths
* lms: Call set_antennas() during open() method
* radioDevice: Move tx_sps from derived into base class
* radioDevice: better encapsulation in base class
* lms: Fix coding style
* lms: Fail in case of unsupported configuration
* usrp1: Fail in case of unsupported configuration
* Fix config file saving of {tx,rx}-path VTY config strings
* logging: Introduce new "DDEV" category for device-specific code
* update git-version-gen to generate proper version numbers
* ensure well-formed example config files
* SocketsTest.testReaderIP(): Zero terminate received buffer
* trx_validate_config(): Fix validation of rx_sps
* vty-ref: Update URI of docbook 5.0 schema
* lms: User correct scale factor for transmit samples
* lms: Set Rx gain to midpoint, as comment suggests.
* usrp1: Remove uninitialized skipRx logic
* usrp1: Fix formatting of log message (missing space)
* cosmetic: Don't call the SDR "USRP" in error message
[ Zydrunas Tamosevicius ]
* lms: Use same timestamp offset like when using LimeSDR via UHD
* lms: Reduce log level of "send buffer of len ..."
* lms: fix LMS_StartStream() handling for multiple channels
* lms: Reduce Rx gain from 47 to 34 dB
[ Alexander Couzens ]
* debian: add patches for debian8
[ Oliver Smith ]
* Add long parameters (--help, --version, ...)
* build manuals moved here from osmo-gsm-manuals.git
* Fix DISTCHECK_CONFIGURE_FLAGS override
* contrib/jenkins.sh: build and publish manuals
* jenkins.sh: run "make distcheck"
* contrib: fix makedistcheck with disabled systemd
* osmo-trx.cpp: move comma_delimited_to_vector() to Utils.cpp
* LMSDevice: make use of dev-args in osmo-trx.cfg
* LMSDeviceTest: fix link errors on OBS
[ Neels Hofmeyr ]
* Importing history from osmo-gsm-manuals.git
[ d0gtail ]
* UHDDevice: log exception information on device open failure
-- Harald Welte <laforge@gnumonks.org> Sun, 20 Jan 2019 19:35:04 +0100
osmo-trx (0.4.0) unstable; urgency=medium
[ Neels Hofmeyr ]

22
debian/control vendored
View File

@@ -13,6 +13,7 @@ Build-Depends: debhelper (>= 9),
libfftw3-dev,
libtalloc-dev,
libusrp-dev,
liblimesuite-dev,
libosmocore-dev (>= 0.10.0)
Standards-Version: 3.9.6
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
@@ -28,7 +29,7 @@ Package: osmo-trx-dbg
Architecture: any
Section: debug
Priority: extra
Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), ${misc:Depends}
Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), ${misc:Depends}
Description: Debug symbols for the osmo-trx-*
Make debugging possible
@@ -69,3 +70,22 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (USRP1)
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
Package: osmo-trx-lms
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
.
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
.
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)

4
debian/osmo-trx-lms.install vendored Normal file
View File

@@ -0,0 +1,4 @@
etc/osmocom/osmo-trx-lms.cfg
lib/systemd/system/osmo-trx-lms.service
/usr/bin/osmo-trx-lms
/usr/share/doc/osmo-trx/examples/osmo-trx-lms/osmo-trx-limesdr.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-lms/

View File

@@ -1 +1,6 @@
etc/osmocom/osmo-trx-uhd.cfg
lib/systemd/system/osmo-trx-uhd.service
/usr/bin/osmo-trx-uhd
/usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/
/usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/
/usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/

View File

@@ -1,3 +1,4 @@
lib/systemd/system/osmo-trx-usrp1.service
/usr/bin/osmo-trx-usrp1
/usr/share/usrp/rev2/std_inband.rbf
/usr/share/usrp/rev4/std_inband.rbf

57
debian/patches/build-for-debian8.patch vendored Normal file
View File

@@ -0,0 +1,57 @@
Index: osmo-trx/debian/control
===================================================================
--- osmo-trx.orig/debian/control
+++ osmo-trx/debian/control
@@ -13,7 +13,6 @@ Build-Depends: debhelper (>= 9),
libfftw3-dev,
libtalloc-dev,
libusrp-dev,
- liblimesuite-dev,
libosmocore-dev (>= 0.10.0)
Standards-Version: 3.9.6
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
@@ -29,7 +28,7 @@ Package: osmo-trx-dbg
Architecture: any
Section: debug
Priority: extra
-Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), ${misc:Depends}
+Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), ${misc:Depends}
Description: Debug symbols for the osmo-trx-*
Make debugging possible
@@ -70,22 +70,3 @@ Description: SDR transceiver that implem
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
-
-Package: osmo-trx-lms
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
-Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
- OsmoTRX is a software-defined radio transceiver that implements the Layer 1
- physical layer of a BTS comprising the following 3GPP specifications:
- .
- TS 05.01 "Physical layer on the radio path"
- TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
- TS 05.04 "Modulation"
- TS 05.10 "Radio subsystem synchronization"
- .
- In this context, BTS is "Base transceiver station". It's the stations that
- connect mobile phones to the mobile network.
- .
- 3GPP is the "3rd Generation Partnership Project" which is the collaboration
- between different telecommunication associations for developing new
- generations of mobile phone networks. (post-2G/GSM)
Index: osmo-trx/debian/rules
===================================================================
--- osmo-trx.orig/debian/rules
+++ osmo-trx/debian/rules
@@ -9,7 +9,7 @@ override_dh_shlibdeps:
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
override_dh_auto_configure:
- dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system
+ dh_auto_configure -- --with-uhd --with-usrp1 --with-systemdsystemunitdir=/lib/systemd/system
override_dh_strip:
dh_strip --dbg-package=osmo-trx-dbg

1
debian/patches/series vendored Normal file
View File

@@ -0,0 +1 @@
# build-for-debian8.patch

2
debian/rules vendored
View File

@@ -9,7 +9,7 @@ override_dh_shlibdeps:
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
override_dh_auto_configure:
dh_auto_configure -- --with-usrp1
dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system
override_dh_strip:
dh_strip --dbg-package=osmo-trx-dbg

4
doc/Makefile.am Normal file
View File

@@ -0,0 +1,4 @@
SUBDIRS = \
examples \
manuals \
$(NULL)

41
doc/examples/Makefile.am Normal file
View File

@@ -0,0 +1,41 @@
OSMOCONF_FILES =
osmoconfdir = $(sysconfdir)/osmocom
if DEVICE_UHD
OSMOCONF_FILES += osmo-trx-uhd/osmo-trx-uhd.cfg
endif
# if DEVICE_USRP1
# TODO: no usrp1 sample file yet
# OSMOCONF_FILES += osmo-trx-usrp1/osmo-trx-usrp1.cfg
# endif
if DEVICE_LMS
OSMOCONF_FILES += osmo-trx-lms/osmo-trx-lms.cfg
endif
osmoconf_DATA = $(OSMOCONF_FILES)
EXTRA_DIST = $(OSMOCONF_FILES)
CFG_FILES = find $(srcdir) -type f -name '*.cfg*' | sed -e 's,^$(srcdir),,'
dist-hook:
for f in $$($(CFG_FILES)); do \
j="$(distdir)/$$f" && \
mkdir -p "$$(dirname $$j)" && \
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
done
install-data-hook:
for f in $$($(CFG_FILES)); do \
j="$(DESTDIR)$(docdir)/examples/$$f" && \
mkdir -p "$$(dirname $$j)" && \
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
done
uninstall-hook:
@$(PRE_UNINSTALL)
for f in $$($(CFG_FILES)); do \
j="$(DESTDIR)$(docdir)/examples/$$f" && \
$(RM) $$j; \
done

View File

@@ -1,9 +1,10 @@
log stderr
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging level all info
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all info
!
line vty
no login
@@ -15,6 +16,7 @@ trx
egprs disable
tx-sps 4
rx-sps 4
rt-prio 18
chan 0
tx-path BAND1
rx-path LNAW

View File

@@ -0,0 +1 @@
osmo-trx-limesdr.cfg

View File

@@ -0,0 +1,22 @@
log stderr
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all info
!
line vty
no login
!
trx
bind-ip 127.0.0.1
remote-ip 127.0.0.1
base-port 5700
egprs disable
tx-sps 4
rx-sps 4
rt-prio 18
chan 0
tx-path BAND1
rx-path LNAW

View File

@@ -0,0 +1 @@
osmo-trx-usrp_b200.cfg

View File

@@ -0,0 +1,23 @@
log stderr
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all info
!
line vty
no login
!
trx
bind-ip 127.0.0.1
remote-ip 127.0.0.1
base-port 5700
dev-args addr=192.168.10.2,pa=NONE,pa_power_max_dbm=23,fifo_ctrl_window=0,status_port=12345
egprs disable
tx-sps 4
rx-sps 4
rssi-offset 38
rt-prio 18
chan 0
chan 1

View File

@@ -1,9 +1,10 @@
log stderr
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging level all info
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all info
!
line vty
no login
@@ -16,5 +17,6 @@ trx
tx-sps 4
rx-sps 4
clock-ref external
rt-prio 18
chan 0

16
doc/manuals/Makefile.am Normal file
View File

@@ -0,0 +1,16 @@
EXTRA_DIST = osmotrx-usermanual.adoc \
osmotrx-usermanual-docinfo.xml \
osmotrx-vty-reference.xml \
chapters \
vty
if BUILD_MANUALS
ASCIIDOC = osmotrx-usermanual.adoc
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
VTY_REFERENCE = osmotrx-vty-reference.xml
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
endif

View File

@@ -0,0 +1,141 @@
[[code_architecture]]
== Code Architecture
[[fig-code-architecture-general]]
.General overview of main OsmoTRX components
[graphviz]
----
digraph hierarchy {
node[shape=record,style=filled,fillcolor=gray95]
edge[dir=back, arrowtail=empty]
2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()}"]
3[label = "{RadioInterface|...}"]
4[label = "{RadioInterfaceResamp|...}"]
5[label = "{RadioInterfaceMulti|...}"]
6[label = "{RadioDevice|...}"]
7[label = "{UHDDevice|...}"]
8[label = "{LMSDevice|...}"]
9[label = "{USRPDevice|...}"]
2->3[arrowtail=odiamond]
3->4[constraint=false]
3->5[constraint=false]
3->6[arrowtail=odiamond]
6->7
6->8
6->9
}
----
[[fig-code-architecture-threads]]
.Example of thread architecture with OsmoTRX configured to use 2 logical RF channels (Trx=Transceiver, RI=RadioIface)
[graphviz]
----
digraph hierarchy {
node[shape=record,style=filled,fillcolor=gray95]
trans [label="Transceiver"];
radioiface [label="RadioInterface"];
radiodev [label="RadioDevice"];
trans:nw->trans:ne [label="Trx.ControlServiceLoop_0"];
trans:nw->trans:ne [label="Trx.ControlServiceLoop_1"];
trans:w->radioiface:w [label="Trx.TxPriorityQueueServiceLoop_0"];
trans:w->radioiface:w [label="Trx.TxPriorityQueueServiceLoop_1"];
radioiface:e->trans:e [label="Trx.RxServiceLoop_0"];
radioiface:e->trans:e [label="Trx.RxServiceLoop_1"];
radioiface->radiodev[label="RI.AlignRadioServiceLoop"];
radioiface:sw->radiodev:nw [label="Trx.TxLowerLoop"];
radiodev:ne->radioiface:se [label="Trx.RxLowerLoop"];
}
----
[[code_component_transceiver]]
=== Transceiver
The Transceiver is the main component managing the other components running in
the OsmoTRX process. There's a unique instance per process.
This class is quite complex from code point of view, as it starts lots of
different threads and hence the interaction with this class from the outside is
quite limited. Only interaction possible is to:
* `Transceiver()`: Create an instance through its constructor, at this time most
configuration is handed to it.
* `init()`: Start running all the threads.
* `receiveFIFO()`: Attach a `radioInterface` channel FIFO in order to use it.
* `setSignalHandler()`: Used to set up a callback to receive certain events
asynchronously from the Transceiver. No assumptions can be made about from
which thread is the callback being called, which means multi-thread locking
precautions may be required in certain cases, similar to usual signal handler
processing. One important event received through this path is for instance
when the Transceiver detected a fatal error which requires it to stop. Since
it cannot stop itself (see destructor below), stopping procedure must be
delegated to the user who created the instance.
* `~Transceiver()`: The destructor, which stops all running threads created at
`init()` time. Destroying the object is the only way to stop the `Transceiver`
completely, and must be called from a thread not managed by the
`Transceiver`, otherwise it will deadlock. Usually it is stopped from the main
thread, the one that called the constructor during startup.
During `init()` time, `Transceiver` will create a noticeable amount of threads,
which may vary depending on the amount of RF channels requested.
Static amount of Threads (1 per `Transceiver` instance):
* `RxLowerLoop`: This thread is responsible for reading bursts from the
`RadioInterface`, storing them into its FIFO and sending Clock Indications
(<<trx_if_clock_ind>>) to _osmo-bts_trx_.
* `TxLowerLoop`: Manages pushing bursts from buffers in the FIFO into the
`RadioInterface` at expected correct time based on the Transceiver clock.
Dynamic amount of Threads (1 per RF logical channel on the `Transceiver` instance):
* `ControlServiceLoop`: Handles commands from the Per-ARFCN Control Interface
socket (<<trx_if_control>>). Each thread is responsible for managing one
socket related to one ARFCN or which is the same, to one RF logical channel.
These are the only threads expected to use the private `start()` and `stop()`
methods of the `Transceiver()` class, since those methods don't stop any of
the `ControlServiceLoop` threads as they must keep running to handle new
commands (for instance, to re-start processing samples with the _POWERON_
command).
* `RxServiceLoop`: Each thread of this type pulls bursts from the
`RadioInterface` FIFO for one specific logical RF channel and handles it
according to the slot and burst correlation type, finally sending proper data
over the TRX Manager UDP socket (<<trx_if>>).
* `TxPriorityQueueServiceLoop`: Blocks reading from one ARFCN specific TRX
Manager UDP socket (<<trx_if>>), and fills the `RadioInterface` with it
setting clock related information.
[[code_component_radioiface]]
=== RadioInterface
The `RadioInterface` sits between the `Transceiver` and the `RadioDevice`, and
provides extra features to the pipe like channelizers, resamplers, Tx/Rx
synchronization on some devices, etc.
If the `RadioDevice` it drives requires it (only _USRP1_ so far), the
`RadioIntercace` will start and manage a thread internally called
`AlignRadioServiceLoop` which will align current RX and TX timestamps.
Different features are offered through different `RadioInterface` subclasses
which are selected based on configuration and device detected at runtime. Using
these features may impact on the amount of CPU required to run the entire pipe.
==== RadioInterfaceResamp
This subclass of `RadioInterface` is automatically selected when some known
specific UHD are to be used, since they require resampling to work properly.
Some of this devices are for instance Ettus B100, USRP2 and X3XX models.
==== RadioInterfaceMulti
This subclass of `RadioInterface` is used when <<multiarfcn_mode>> is requested.
[[code_component_radiodev]]
=== RadioDevice
The `RadioDevice` class is responsible for driving the actual Hardware device.
It is actually only an interface, and it is implemented in each backend which in
turn becomes a specific OsmoTRX binary, see <<trx_backends>>.

View File

@@ -0,0 +1,85 @@
== Configuring OsmTRX
OsmoTRX will read the configuration at startup time and configure the
transceiver accordingly after validating the configuration.
OsmoTRX can handle several TRX channels, but at least one must be configured in
order to be able to start it successfully. Channels must be present in the
configuration file in incremental order, starting from 0 and be consecutive.
Example configuration files for different devices and setups can be found in
`doc/examples/` in 'osmo-trx' git repository.
=== Documented example
.Example: Static GGSN/APN configuration (single catch-all GGSN)
----
trx
bind-ip 127.0.0.1 <1>
remote-ip 127.0.0.1 <2>
base-port 5700 <3>
egprs disable <4>
tx-sps 4 <5>
rx-sps 4 <6>
chan 0 <7>
tx-path BAND1 <8>
rx-path LNAW <9>
----
<1> Configure the local IP address at the TRX used for the connection against `osmo-bts-trx`.
<2> Specify the IP address of `osmo-bts-trx` to connect to.
<3> Specify the reference base UDP port to use for communication.
<4> Don't enable EDGE support.
<5> Use 4 TX samples per symbol. This is device specific.
<6> Use 4 RX samples per symbol. This is device specific.
<7> Configure the first channel. As no other channels are specified, `osmo-trx` assumes it is using only one channel.
<8> Configure the device to use `BAND1` Tx antenna path from all the available ones (device specific).
<9> Configure the device to use `LNAW` Rx antenna path from all the available ones (device specific).
[[multiarfcn_mode]]
=== Multi-ARFCN mode
The Multi-ARFCN feature allows to have a multi-carrier approach multiplexed on a
single physical RF channel, which can introduce several benefits, such as lower
cost and higher capacity support.
Multi-ARFCN support is available since osmo-trx release `0.2.0`, and it was
added specifically in commit `76764278169d252980853251daeb9f1ba0c246e1`.
This feature is useful for instance if you want to run more than 1 TRX with an
Ettus B200 device, or 2 TRX with an Ettus B210 device, since they support only 1
and 2 physical RF channels respectively. No device from other providers or even
other devices than B200 and B210 from Ettus are known to support this feature.
With multi-ARFCN enabled, ARFCN spacing is fixed at 800 kHz or 4 GSM channels.
So if TRX-0 is set to ARFCN 51, TRX-1 _must_ be set to 55, and so on. Up to
three ARFCN's is supported for multi-TRX.
From BTS and BSC point of view, supporting multiple TRX through multi-ARFCN
feature in OsmoTRX doesn't make any difference from a regular multi-TRX setup,
leaving apart of course the mentioned ARFCN limitations explained above and as a
consequence physical installation and operational differences.
.Example: osmo-bts-trx.cfg using 2 TRX against an osmo-trx driven device
----
phy 0
osmotrx ip local 127.0.0.1
osmotrx ip remote 127.0.0.1
instance 0
instance 1
bts 0
...
band GSM-1800
trx 0
phy 0 instance 0
trx 1
phy 0 instance 1
----
.Example: osmo-trx.cfg using Multi-ARFCN mode to run 2 TRX
----
trx
...
multi-arfcn enable
chan 0
chan 1
----

View File

@@ -0,0 +1,12 @@
[[control]]
== Control interface
The actual protocol is described in <<common-control-if>>, the variables
common to all programs using it are described in <<ctrl_common_vars>>. Here we
describe variables specific to OsmoTRX.
.Variables available over control interface
[options="header",width="100%",cols="20%,5%,5%,50%,20%"]
|===
|Name|Access|Trap|Value|Comment
|===

View File

@@ -0,0 +1,4 @@
[[counters]]
== Counters
include::./counters_generated.adoc[]

View File

@@ -0,0 +1,7 @@
// autogenerated by show asciidoc counters
These counters and their description based on OsmoTRX 0.2.0.61-408f (OsmoTRX).
// generating tables for rate_ctr_group
// generating tables for osmo_stat_items
// generating tables for osmo_counters
// there are no ungrouped osmo_counters

View File

@@ -0,0 +1,62 @@
[[chapter_introduction]]
== Overview
[[intro_overview]]
=== About OsmoTRX
OsmoTRX is a C/C++ language implementation of the GSM radio modem,
originally developed as the 'Transceiver' part of OpenBTS. This radio
modem offers an interface based on top of UDP streams.
The OsmoBTS bts_model code for OsmoTRX is called
`osmo-bts-trx`. It implements the UDP stream interface of
OsmoTRX, so both parts can be used together to implement a complete GSM
BTS based on general-purpose computing SDR.
As OsmoTRX is general-purpose software running on top of Linux, it is
thus not tied to any specific physical hardware. At the time of this
writing, OsmoTRX supports a variety of Lime Microsystems and Ettus USRP SDRs via
the UHD driver, as well as the Fairwaves UmTRX and derived products.
OsmoTRX is not a complete GSM PHY but 'just' the radio modem. This
means that all of the Layer 1 functionality such as scheduling,
convolutional coding, etc. is actually also implemented inside OsmoBTS.
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
* TS 05.01 "Physical layer on the radio path"
* TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
* TS 05.04 "Modulation"
* TS 05.10 "Radio subsystem synchronization
As such, the boundary between OsmoTRX and `osmo-bts-trx` is at
a much lower interface, which is an internal interface of other more
traditional GSM PHY implementations.
Besides OsmoTRX, there are also other implementations (both Free
Software and proprietary) that implement the same UDP stream based radio
modem interface.
[[fig-gprs-pcubts]]
.GSM network architecture with OsmoTRX and OsmoBTS
[graphviz]
----
digraph G {
rankdir=LR;
MS0 [label="MS"];
MS1 [label="MS"];
MS0->SDR[label="Um"];
MS1->SDR [label="Um"];
SDR -> OsmoTRX [label="Raw Samples"];
OsmoTRX->BTS [label="bursts over UDP"];
BTS->BSC [label="Abis"];
BSC->MSC [label="A"];
BTS->PCU [label="pcu_sock"];
PCU->SGSN [label="Gb"];
OsmoTRX [color=red];
}
----
For more information see
https://osmocom.org/projects/osmotrx/wiki/OsmoTRX

View File

@@ -0,0 +1,19 @@
== Running OsmoTRX
The OsmoTRX executable (`osmo-trx`) offers the following command-line
options:
=== SYNOPSIS
*osmo-trx* [-h] [-C 'CONFIGFILE']
=== OPTIONS
*-h*::
Print a short help message about the supported options
*-C 'CONFIGFILE'*::
Specify the file and path name of the configuration file to be
used. If none is specified, use `osmo_trx.cfg` in the current
working directory.

View File

@@ -0,0 +1,34 @@
[[osmotrx_arch_support]]
== OsmoTRX hardware architecture support
OsmoTRX comes out-of-the-box with several algorithms and operations
optimized for certain instruction-set architectures, as well as non-optimized
fall-back algorithms in case required instruction sets are not supported by the
compiler at compile time or by the executing machine at run-time. Support for
these optimized algorithms can be enabled and disabled by means of configure
flags. Accelerated operations include pulse shape filtering, resampling,
sequence correlation, and many other signal processing operations.
On Intel processors, OsmoTRX makes heavy use of the Streaming SIMD Extensions
(SSE) instruction set. SSE3 is the minimum requirement for accelerated use.
SSE3 is present in the majority of Intel processors since later versions of the
Pentium 4 architecture and is also present on low power Atom processors. Support
is automatically detected at build time. SSE4.1 instruction set is supported
too. This feature is enabled by default unless explicitly disabled by passing
the configure flag _--with-sse=no_. When enabled, the compiler will build an
extra version of each of the supported algorithms using each of the supported
mentioned instruction sets. Then, at run-time, OsmoTRX will auto-detect
capabilities of the executing machine and enable an optimized algorithm using
the most suitable available (previously compiled) instruction set.
On ARM processors, NEON and NEON FMA are supported. Different to the x86, there
is no auto-detection in this case, nor difference between compile and runtime.
NEON support is disabled by default and can be enabled by passing the flag
_--with-neon=yes_ to the configure script; the used compiler must support NEON
instruction set and the resulting binary will only run fine on an ARM board
supporting NEON extensions. Running OsmoTRX built with flag _--with-neon_ on a
board without NEON instruction set support, will most probably end up in the
process being killed with a _SIGILL_ Illegal Instruction signal by the operating
system. NEON FMA (Fused Multiply-Add) is an extension to the NEON instruction
set, and its use in OsmoTRX can be enabled by passing the _--with_neon_vfpv4_
flag, which will also implicitly enable NEON support (_--with_neon_).

View File

@@ -0,0 +1,46 @@
[[trx_backends]]
== OsmoTRX backend support
[[backend_uhd]]
=== `osmo-trx-uhd` for UHD based Transceivers
This OsmoTRX model uses _libuhd_ (UHD, USRP Hardware Driver) to drive the
device, that is configuring it and reading/writing samples from/to it.
So far, this backend has been mostly used to drive devices such as the Ettus
B200 family and Fairwaves UmTRX family, and used to be the default backend used
for legacy @osmo-trx@ binary when per-backend binaries didn't exist yet.
Any device providing generic support for UHD should theoretically be able to be
run through this backend without much effort, but pracitcal experience showed
that some devices don't play well with it, such as the LimeSDR family of
devices, which showed far better results when using its native interface.
Related code can be found in the _Transceiver52M/device/uhd/_ directory in
_osmo-trx.git_.
[[backend_lms]]
=== `osmo-trx-lms` for LimeSuite based Transceivers
This OsmoTRX model uses LimeSuite API and library to drive the device, that is
configuring it and reading/writing samples from/to it.
This backend was developed in order to be used together with LimeSDR-USB and
LimeSDR-mini devices, due to to the poor results obtained with the UHD backend,
and to simplify the stack.
Related code can be found in the _Transceiver52M/device/lms/_ directory in
_osmo-trx.git_.
[[backend_usrp1]]
=== `osmo-trx-usrp1` for libusrp based Transceivers
This OsmoTRX model uses the legacy libusrp driver provided in GNU Radio 3.4.2.
As this code was dropped from GNU Radio at some point and was found very
difficult to build, some work was done to create a standalone libusrp which can
be nowadays found as a separate git repository together with other osmocom git
repositories, in https://git.osmocom.org/libusrp/
Related code can be found in the _Transceiver52M/device/usrp1/_ directory in
_osmo-trx.git_.

View File

@@ -0,0 +1,90 @@
[[osmotrx_device_support]]
== OsmoTRX hardware device support
OsmoTRX consists of a _common_ part that applies to all TRX devices as well as
_hardware-specific_ parts for each TRX device. The hardware-specific parts are
usually provided by vendor-specific or device-specific libraries that are then
handled by some OsmoTRX glue code presenting a unified interface towards the
rest of the code by means of a _RadioDevice_ class.
The common part includes the core TRX architecture as well as code for
implementing the external interfaces such as the TRX Manager UDP socket,
control, and VTY interfaces.
The hardware-specific parts include support for driving one particular
implementation of a radio modem. Such a physical layer
implementation can come in many forms. Sometimes it runs on a general
purpose CPU, sometimes on a dedicated ARM core, a dedicated DSP, a
combination of DSP and FPGA.
Joining the common part with each of the available backends results in a
different binary with different suffix for each backend. For instance, when
OsmoTRX is built with UHD backend, an _osmo-trx-uhd_ binary is generated; when
OsmoTRX is built with LimeSuite backend, an _osmo-trx-lms_ binary is generated.
Build of different backend can be enabled and disabled by means of configure
flags, which can be found in each subsection relative to each backend below.
[[dev_ettus_usrp1]]
=== Ettus USRP1
The binary _osmo-trx-usrp1_ is used to drive this device, see <<backend_usrp1>>.
[[dev_ettus_b200]]
=== Ettus B200
The binary _osmo-trx-uhd_ is used to drive this device, see <<backend_uhd>>.
Comes only with 1 RF channel. It can still be used in a multi-TRX setup by using
the <<multiarfcn_mode>> feature. By using this feature, one can drive up to 3
TRX (with the restrictions explained there).
[[dev_ettus_b200]]
=== Ettus B210
The binary _osmo-trx-uhd_ is used to drive this device, see <<backend_uhd>>.
Comes with 2 RF channels, which can be used to set up a multi-TRX BTS. However,
due to a shared local oscillator for both RF channels, ARFCN separation can be
up about 25 MHz.
This device also supports the <<multiarfcn_mode>> feature. By using this
feature, one can drive up to 3 TRX (with the restrictions explained there).
Please note that the above configurations cannot be combined, which means
maximum number of TRX one can achieve is 2 by using separate physical RF
channels, or 3 by using multi-ARFCN method. You cannot support, for example, 6
ARFCN operation on B210 using 3 TRX on side A and another 3 TRX on side B.
[[dev_limesdr_usb]]
=== LimeSDR-USB
The binary _osmo-trx-lms_ is used to drive this device, see <<backend_lms>>.
This device comes with 2 RF channels, so it should theoretically be possible to
run a multi-TRX setup with it, but there are yet no records that this kind of
setup was tested with this device.
This device has 3 different Rx paths with different antenna connectors in the
PCB, each with a different frequency and bandwidth range. One should make sure
the physical antenna is connected to the correct connector matching the Rx path
you want to use. If one wants to be able to use the device in both 900 and 1800
MHz GSM bands and easily switch between them, then Rx Path `LNAW` should be used
,since it is the only one covering both bands, and the antenna physically plugged
accordingly. Following example shows how to then configure _osmo-trx-lms_ to use
that Rx path to read samples.
.Example: Configure osmo-trx-lms to use LNAW as Rx path and BAND1 as Tx Path
----
trx
...
chan 0
tx-path BAND1
rx-path LNAW
----
[[dev_limesdr_mini]]
=== LimeSDR-mini
The binary _osmo-trx-lms_ is used to drive this device, see <<backend_lms>>.
As a smaller brother of the [[dev_limesdr_usb]], this device comes only with 1
RF channel. As a result, it can only hold 1 TRX as of today.

View File

@@ -0,0 +1,46 @@
<revhistory>
<revision>
<revnumber>1</revnumber>
<date>March 6, 2019</date>
<authorinitials>PE</authorinitials>
<revremark>
Initial version.
</revremark>
</revision>
</revhistory>
<authorgroup>
<author>
<firstname>Pau</firstname>
<surname>Espin Pedrol</surname>
<email>pespin@sysmocom.de</email>
<authorinitials>PE</authorinitials>
<affiliation>
<shortaffil>sysmocom</shortaffil>
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
<jobtitle>Software Developer</jobtitle>
</affiliation>
</author>
</authorgroup>
<copyright>
<year>2018</year>
<holder>sysmocom - s.f.m.c. GmbH</holder>
</copyright>
<legalnotice>
<para>
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts,
and no Back-Cover Texts. A copy of the license is included in
the section entitled "GNU Free Documentation License".
</para>
<para>
The Asciidoc source code of this manual can be found at
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
http://git.osmocom.org/osmo-gsm-manuals/
</ulink>
</para>
</legalnotice>

View File

@@ -0,0 +1,42 @@
:gfdl-enabled:
OsmoTRX User Manual
====================
Pau Espin Pedrol <pespin@sysmocom.de>
include::./common/chapters/preface.adoc[]
include::{srcdir}/chapters/overview.adoc[]
include::{srcdir}/chapters/running.adoc[]
include::./common/chapters/control_if.adoc[]
include::{srcdir}/chapters/control.adoc[]
include::./common/chapters/vty.adoc[]
include::./common/chapters/logging.adoc[]
include::{srcdir}/chapters/counters.adoc[]
include::{srcdir}/chapters/configuration.adoc[]
include::{srcdir}/chapters/trx-architectures.adoc[]
include::{srcdir}/chapters/trx-devices.adoc[]
include::{srcdir}/chapters/trx-backends.adoc[]
include::{srcdir}/chapters/code-architecture.adoc[]
include::./common/chapters/trx_if.adoc[]
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
ex:ts=2:sw=42sts=2:et
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-->
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
]>
<book>
<info>
<revhistory>
<revision>
<revnumber>v1</revnumber>
<date>6th March 2018</date>
<authorinitials>pe</authorinitials>
<revremark>Initial</revremark>
</revision>
</revhistory>
<title>OsmoTRX VTY Reference</title>
<copyright>
<year>2018</year>
</copyright>
<legalnotice>
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
</para>
</legalnotice>
</info>
<!-- Main chapters-->
&chapter-vty;
</book>

View File

@@ -0,0 +1,2 @@
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
</vtydoc>

File diff suppressed because it is too large Load Diff

View File

@@ -93,7 +93,8 @@ if test -n "$v"
then
: # use $v
elif
v=`git tag -l --sort=v:refname | grep "^[0-9]*.[0-9]*.[0-9]*$" | tail -n 1 2>/dev/null` \
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|| git describe --abbrev=4 HEAD 2>/dev/null` \
&& case $v in
[0-9]*) ;;
v[0-9]*) ;;

View File

@@ -28,11 +28,11 @@ PRBSTest_SOURCES = PRBSTest.cpp
InterthreadTest_SOURCES = InterthreadTest.cpp
InterthreadTest_LDADD = $(COMMON_LA)
InterthreadTest_LDFLAGS = -lpthread
InterthreadTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
SocketsTest_SOURCES = SocketsTest.cpp
SocketsTest_LDADD = $(COMMON_LA)
SocketsTest_LDFLAGS = -lpthread
SocketsTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
TimevalTest_SOURCES = TimevalTest.cpp
TimevalTest_LDADD = $(COMMON_LA)

View File

@@ -47,9 +47,10 @@ void *testReaderIP(void *param)
readSocket->nonblocking();
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH] = { 0 };
char buf[MAX_UDP_LENGTH+1] = { 0 };
int count = readSocket->read(buf, MAX_UDP_LENGTH);
if (count>0) {
buf[count] = 0;
CERR("read: " << buf);
rc++;
} else {

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