Compare commits

...

132 Commits

Author SHA1 Message Date
Keith
3629017b0d Add 4/4 Tx/Rx SPS definition for OCR01 2020-03-23 14:22:36 -05:00
Keith
6d496c8e15 Add Device Definition for the OC Connect1 SDR
This patch forward ports the relevant parts of a patch
from OpenCellular for osmo-trx versions previous to commit
1fb0ce67d8
so that osmo-trx-uhd recognizes the hardware, somewhat documented here:

https://github.com/Telecominfraproject/OpenCellular/blob/master/electronics/radio/SDR/

Note: I'm not very familiar with the hardware. It requires a patch to the
Ettus UHD driver to load the correct FPGA fw in which one call to
check_fpga_compat() is commented.
Otherwise mostly changes identifiers and log messages to "OCR01".

The usb device is reported as Vid:2500 Pid:0020 (Ettus,B200) but
I'm not sure how compatible it is. The fpga FW is different.
2020-03-23 14:13:25 -05:00
Pau Espin Pedrol
dfc6e5ffc7 radioDevice: Drop unused isControl param from WriteSamples API
The out "isControl" parameter is only used by internal callers of
USRPDevice, and not used at all by any user of the generic API
(radioInterface*.cpp). Hence, we can get rid of it and keep it as a flag
for an internal API of USRPDevice.

Change-Id: I843384e24b76cdd28a95f9ee4e95e6157098e4a3
2020-03-12 19:35:47 +01:00
Pau Espin Pedrol
f8c0c464b8 radioDevice: Drop unused RSSI param from readSamples API
The out "RSSI" parameter is only filled by USRPDevice, and not used at
all by any user of the API (radioInterface*.cpp).

RSSI seems to be computed nowadays in the common path in
Transceiver::pullRadioVector().

Change-Id: I06c2ea5a9891d170bc468f952bbf2a7e64d95784
2020-03-12 19:34:28 +01:00
Pau Espin Pedrol
93707d0227 cosmetic: fix several typos found by codespell
Change-Id: Id1f6766572fd313463201e6d03964965f227db25
2020-02-25 17:03:00 +01:00
Pau Espin Pedrol
5291e8a654 debug.h: Fix print format of chan in CLOGCHAN
Under armv7l arch, size_t is actually an unsigned int and not a long
unsigned int, and compiler errors:

CommonLibs/debug.h:28:24: error: format ‘%lu’ expects argument of type
‘long unsigned int’, but argument 8 has type ‘size_t {aka unsigned int}’ [-Werror=format=]

Change-Id: I7f6ded5a984570b5267916d6c84eb7d019db73a8
2020-02-19 18:08:20 +01:00
Pau Espin Pedrol
7a07de1efd debug.h: Avoid printing pthread_t type
Using %lu for pthread_t was wrong on armv7l arch. Even worse, according
to pthread_self() man page, pthread_t cannot be assumed to be of a simple
type and hence printable:
"""
POSIX.1 allows an implementation wide freedom in choosing the type used
to represent a thread ID; for example, representation using either an
arithmetic  type  or a structure is permitted.
"""

Let's use gettid() instead. According to glibc documentation:
"""
The pid_t data type is a signed integer type which is capable of
representing a process ID. In the GNU C Library, this is an int.
"""
It may not be the same on other libc's though, so let's better cast to a
long int just in case.

Accordign to gettid() man, the libc function was only added recently
during glibc 2.30, however the system call has been around for quite
some time (linux 2.4.11). Let's accomodate use udner non-glibc or older
versions of it by having a direct syscall fallback.

Change-Id: I40265fd4c62e550014ba3ff3335ca053c5bc01f2
2020-02-19 18:08:20 +01:00
Pau Espin Pedrol
fd67262df8 contrib/jenkins.sh: Reorder sanity checks
Change-Id: Idfe12148aa7a8030bdaf56d11c1547ebc3f56d14
2020-02-19 11:53:30 +01:00
Pau Espin Pedrol
0569845a08 lms: Initial multi-arfcn support
With current state multi-arfcn can be used (eg. I can place a call
between 2 phones using TRX1 and sustain for as long as wanted), but from
time to time (around every 20seconds), a burst of Tx packed dropped
events from LimeSuite appears.

LimeNet-micro coefficients have yet not been tested.

Related: OS#4362
Change-Id: I7e67d90a8126546eeeeba376f816ec5d158d4712
2020-01-15 15:46:14 +01:00
Pau Espin Pedrol
c69b87f9bd lms: Make ts_offset and smpl rate coefs device-specific
Right now the values are the same for all devices, but they will differ
in forthcoming commits once multi-arfcn support is added.

Change-Id: I262d3a71848fc3070473e29e42820848e7591d02
2020-01-15 15:46:14 +01:00
Pau Espin Pedrol
a7bf6cd8a4 lms: Store device type specific parameters in one place
Add an enum containing each supported device type (LimeSDR-USB,
LimeSDR-Mini and LimeNet-Micro) plus "unknown", to leave some room for
yet-to-come devices to run with some generic parameters without
rebuilding osmo-trx.

Each device type is assigned a dev_desc structure, and all of them are
put in HashMap, similar to what's already done in UHDDevice.cpp.

Device type is infered from string provided by LMS_GetDeviceInfo(), as
it was already done before in several places. From now on, we only need
to parse the string once since we store the device type after first
during open time.

Later on, more fields will be moved to device-type specific structure,
such as Tx timing offset, clock rate, etc.

Change-Id: I7658615787c5bc41c365bab9c11733b701ac2ae5
2020-01-15 15:45:29 +01:00
Pau Espin Pedrol
e7f6a27ab6 lms: Move rx_buffers allocation to constructor
Release is done in destructor, so let's move allocation to constructor
since there's really no need to have them in open() which is already
quite complex and large.

Change-Id: I8a4fd973590c4c165abd8f2837b2da8fc14a2066
2020-01-15 15:35:01 +01:00
Pau Espin Pedrol
a979f5f32b lms: Make reference to std::vector unambiguous
Change-Id: Ieebdbd3d5082a02aea2441e6737783370511cbc1
2020-01-15 15:34:49 +01:00
Pau Espin Pedrol
b0e54265ad lms: Change radioDevice constructor arg name to avoid masking instance attr
channel number mangling based on multi-arfcn feature being enabled was
moved to generic radioDevice() to reuse code. Hence, the generic parent
constructor sets this->chans to 1 if multi-arfcn feature is requested.
However, LMSDevice constructor argument had same name as the class
instance attribute, taking preference. As a result, if multi-arfcn is
enabled in LMSDevice, the generic constructor first sets this->chans=1
but afterwards LMSDEvice constructor keeps calling .resize() with the
argument value "chans" instead of using this->chans.

Let's rename the argument in all radioDevice child class constructors to
avoid potential future bugs in all of them.

Change-Id: Id6c837e9133f22783dd92a81dfcc493e51bf2d21
2020-01-13 16:13:28 +01:00
Pau Espin Pedrol
62c9280590 lms: Improve smpl_buf error logging
Change-Id: I511abe2c333443b978a3767bd7b7e320e07c4930
2020-01-13 14:35:06 +01:00
Pau Espin Pedrol
1421adbc61 smpl_buf: Fix str_code() param and print unknown error val
Change-Id: I95fadac15b9ad337ebc7cfb44a20dcf803ff8a47
2020-01-13 14:34:19 +01:00
Pau Espin Pedrol
dccc82491c lms: Drop unused define
Change-Id: Iaf3361ed29dd552e5e52b62bc738fa20c6b583fe
2020-01-07 16:21:12 +01:00
Pau Espin Pedrol
bf58370675 lms: Move initialization of field started to constructor
Change-Id: I135a2ff4a419775169452be1128c7b30f7d638ad
2020-01-07 16:10:34 +01:00
Pau Espin Pedrol
7c84925ea4 doc: Update vty reference xml file
Change-Id: Ib2707204cbba6df813ffc08d7098093cf4393da0
2020-01-07 16:04:04 +01:00
Pau Espin Pedrol
fd99c6ce05 radioInterfaceMulti: Fail to tune on freq not following multi-arfcn restrictions
multi-arfcn feature uses a hardcoded disposition of logical channels on
a physical channel. Logical channels in the phisical channel are
separated by MCBTS_SPACING Hz, that is 4 GSM ARFCNs.

As a result, multi-arfcn restricts the TRX ARFCN setup to the following:
ARFCN(TRX0)=N, ARFCN(TRX1)=N+1*4, ARFCN(TRX2)=N+2*4, ...

Let's make sure radioInterfaceMulti verifies the requested Rx/Tx
frequencies for each logical channel over TRXC match the restriction
explained above. It will check freq going to be set is indeed separated
by MCBTS_SPACING from already set channels, making sure the ARFCN series
is consistent.

Otherwise, before this patch, one could set in osmo-bsc:
ARFCN(TRX0)=N, ARFCN(TRX1)=N+2

and osmo-trx would silently ack the related Rx/TxTUNE TRXC commands, but
actually still transmit on ARFCN N+4 instead. As a result, in this
scenario TRX!=0 were unusable with multi-arfcn.

Related: OS#4207
Change-Id: I2f3d66a611d3a489b3e4d9431994f4ec77b4460f
2020-01-07 16:04:04 +01:00
Pau Espin Pedrol
e947db8d98 doc: clarify number of channels on B210 with multi-arfcn enabled
Change-Id: I082d4d8c346f1be1569fe63baa856029e439cb2c
2020-01-07 16:04:04 +01:00
Pau Espin Pedrol
9279e0e123 uhd: Improve some logging lines printing UHD pretty-print output
Change-Id: If5aba28aaf8a3312d89b3e963184f9f20966d199
2020-01-07 16:04:04 +01:00
Pau Espin Pedrol
84231bd8b7 uhd: Use DEVDRV log category and support UHD >=3.11 logging framework
Change-Id: I36f1ff7d425a2144fb512ff393af02741eb4a3d4
2020-01-07 16:04:04 +01:00
Pau Espin Pedrol
aebbfe0ee7 Make logging category DLMS generic and reusable for other backends
Make sure old configs using "logging level lms <level>" are still accepted.
Initialization order of VTY componenets need to be resorted since newly
introduced command requires logging VTY node to be already setup
beforehand.

Change-Id: Ia195a74a62a8a3dd6267fb1359acaa5628208d8e
2020-01-07 16:04:04 +01:00
Pau Espin Pedrol
da7fee8ea8 Bump version: 1.1.1.38-9f2b-dirty → 1.2.0
Change-Id: I9009eb44e7d8100294da139300480fc3a2f6b616
2020-01-03 19:54:00 +01:00
Pau Espin Pedrol
9f2baf3e04 Transceiver.cpp: Introduce and use new logging categories
Take the chance to clean up logging lines in this file:
* Use LOGCHAN in more places where chan is useful
* Replace inherited (old osmo-trx) categories such as WARNING with
osmocom ones.

Change-Id: Ic8c218f050f35d48046ccf1561fb0bfc505d4f63
2019-12-20 14:19:35 +01:00
Pau Espin Pedrol
0a2a40f43c uhd: use value already cached in tmp variable
Change-Id: I4568eaed6db3da12f83f2f503a50032f7bfb482c
2019-12-19 13:52:57 +01:00
Harald Welte
501d053b70 trx: exit() on unsupported positional arguments on command line
Change-Id: Ifeea65a359e9ca307efb2c721fd0fb6f0959e976
2019-12-03 21:41:38 +01:00
Pau Espin Pedrol
0fafe03199 Transceiver: Fix wrong response upon CMD HANDOVER failure
Change-Id: I9d3f120b1696a9ce92c81097d04e81dbb717287d
2019-11-27 13:17:59 +01:00
Alexander Chemeris
9a87d90c1e vty: Simplify filler burst settings and improve help and readability.
In the command line options time, filler table/filer burts settings
were a bit difficult to undertand because the number of one-letter
settings was limited. Now, with VTY configuration, there is no reason
to keep it so difficult.

Also, after the previous commit it was no longer posible to enable
random 8-PSK filler bursts. With this patch you can configure all
supported filler bursts in a simple and logical way.

Change-Id: I752eb2c1162d084e8769181f2fcd6c0877663448
2019-10-21 08:41:07 +00:00
Martin Hauke
066fd04f47 Fix common misspellings and typos
Change-Id: I4ec7accb1912c052b446be7c399bed32a8c62253
2019-10-17 08:06:19 +00:00
Alexander Chemeris
aeaba02e02 vty: Don't enable random filler bursts automatically with EDGE.
The EGPRS switch in the VTY config enables 8-PSK burst detection on
uplink. Enabling it shouldn't turn on filler bursts.

Change-Id: I2786c768e038b769a80c8b78fe58cfa09eb322a9
2019-10-15 12:00:55 +00:00
Pau Espin Pedrol
b7e992703c Use new libosmocore logging lock API
Since libosmocore Id7711893b34263baacac6caf4d489467053131bb, a new API
log_enable_multithread() is available which takes care of protecting
logging infrastructure from us (and actually does it correctly since we
cannot protect internal libosmocore structures from osmo-trx).

Let's drop all mutex related code from osmo-trx logging infra and simply
rely on libosmocore's.

Related: OS#4088
Change-Id: I519d0f30bce871005ca26b90177ea4aa4839360a
2019-10-09 19:15:49 +02:00
Pau Espin Pedrol
928141b7d6 radioInterfaceMulti: write frequency offset direction (rx/tx) in log line
Change-Id: I7b89426e5d7d5e7570d4ef800a30c7b74bd09b82
2019-09-30 12:18:31 +02:00
Pau Espin Pedrol
e503c988d8 radioInterface: Atomically fetch and change underrun variable
Otherwise, it could happen that underrun events are lost:
TxLower (isUnderrun):	RxLower (pullBuffer):
read(underrun)
			read(underrun)
			write(underrun, |val) [maybe underrun becomes TRUE]
write(underrun, false)

Similary, it could happen the other direction if atomic was only applied
to isUnderrun:
TxLower (isUnderrun):	RxLower (pullBuffer):
			read(underrun) -> true
read(underrun)-> true
write(underrun, false)
			write(underrun, true|val) where val=false

So in here isUnderrun would return true twice while it should only
return one.

Change-Id: I684e0a5d2a9583a161d5a6593559b3a9e7cd57e3
2019-09-20 19:17:22 +02:00
Pau Espin Pedrol
ee2ba19cec Move multi-ARFCN chan amount modification from UHDDevice to parent class
This way switch is applied correctly to parent structures and features
can be used later by other children classes (other devices).

Change-Id: I24d6c66bb3195ba2513b4a67daa14cdfbacdce6d
2019-09-20 19:17:22 +02:00
Pau Espin Pedrol
80d053acb9 radioInterface: Mark setRxGain as virtual
Otherwise the parent function is always called even if the iface is
radioInterfaceMult.

Change-Id: Ie41efab1e60b88677bbd1ec333ea656794503a5a
2019-09-20 19:17:22 +02:00
Timo Jacobus
18a615176e Transceiver: Fixed copying of history into and from channelizer buffer.
In multi arfcn mode, osmo-trx would drop some bursts because it couldn't detect it
and would emit idle burst instead. Specificaly detection of peak in correlation
vector failed. Correcting copying of history in pullBuffer method fixes this issue.

[Re-worked by Pau Espin Pedrol <pespin@sysmocom.de>]

Fixes: 57df2362f0eca0a330aad3e18906046dfadb9c8b
Change-Id: I93e43f6868cd67e69fc59d2980a03550d2505bf8
2019-09-20 19:17:22 +02:00
Pau Espin Pedrol
25383a3610 radioInterface{Multi,Resamp}: Fix successful writeSamples() masking underrun from readSamples()
The only who should be setting class instance value "underrun" to false
is isUnderrun().
Similar fixes were already applied lately to radioInterface.cpp.

Change-Id: I3239b1df4536c080365106b3e4daa523b57f5dff
2019-09-13 18:53:29 +02:00
Pau Espin Pedrol
e2ac20a96e UHDDevice: Drop unneeded MULTI_ARFCN checks
After previous changes, radioInterfaceMulti is expected to handle channel
conversion correctly, so it will always use chan 0 for all these
functions. This simplifies code in radioDevice avoiding need to add
checks to all devices supporting multi-arfcn in the future.

Change-Id: Ib2cd50a6ceaeedc6aaf3e1bb51d33b52911b6eba
2019-09-13 17:54:27 +02:00
Pau Espin Pedrol
62845241a2 radioInterfaceMulti: Override setTxGain() to avoid chan!=0 calls
Change-Id: I7e67f660c3b0b009db59b405de603f6058021802
2019-09-13 17:50:10 +02:00
Pau Espin Pedrol
2ab921812e radioDevice: Introduce getTxGain() API
It will be used in later commits by radioInterfaceMulti.

Change-Id: Ie3caca8971ed1e5370dfed6fb60716a24e7d82a5
2019-09-13 17:34:46 +02:00
Pau Espin Pedrol
752055c7c1 radioInterface: Remove unusued getRxGain()
Only the radioDevice->getRxGain() is called from inside
radioInterfaceMulti, so the API in radioInterface is not used at all.

Change-Id: Icc4e9a7ebfdafe7c72c535752a5e379d12592c9a
2019-09-13 17:34:46 +02:00
Pau Espin Pedrol
705a348326 LMSDevice: Return previous txGain/rxGain if setting value failed
Change-Id: I11e853e11bec99fc88e81642f9b2cd87d5815398
2019-09-13 17:33:14 +02:00
Pau Espin Pedrol
331c88ae82 USRPDevice: Return previous txGain if setting value failed
Change-Id: I0d8fd51586ef01141d4e5896f0fc3029a22743f8
2019-09-13 17:33:14 +02:00
Pau Espin Pedrol
ca0892d822 USRPDevice: Fix setRxGain return on error and getRxGain() returning always 0
field rxGain is set to 0 during constructor and never set after that
point.

Change-Id: I7fae7a315e5ab98a15c27628a88a92226ef89469
2019-09-13 17:32:03 +02:00
Pau Espin Pedrol
17e6cd0394 radioInterfaceMulti: Check equals zero explicitly
It's not a pointer check or a boolean expression, in here we really
check chan index is 0, so it's more clear doing so this way like it's
done in all other places.

Change-Id: I83b14487d14ba8272f58796f640f58a88891e532
2019-09-13 16:20:46 +02:00
Pau Espin Pedrol
a801ae5d94 radioInterface: Rename mRadio to mDevice
Previous naming is ready confusing, because "Radio" is actually the
common term between radioInterface and radioDevice, and it looks like
it's referring to radioInterface rather than radioDevice. On the other
hand, mDevice cleary states it refers to the radioDevice item.

Change-Id: I708bb1992a156fb63334f5590f2c6648ca27495e
2019-09-13 15:59:31 +02:00
Pau Espin Pedrol
923b4bc9a2 Transceiver: Don't stop TRX if pulling from OFF timeslot
BTS may have any timeslot disabled, or may have not yet sent initial
SETSLOT cmd to properly configure the timeslot.

Change-Id: Icf62e5d1200c7a440f255bb46023cdbf61532b7f
2019-09-09 10:27:04 +02:00
Pau Espin Pedrol
0d56d75dbb Transceiver: pullRadioVector(): Fix use of uninitialized value bi->tn
Change-Id: Ia0f2b5a51040663d7e8219e6ed51e0513b876548
2019-09-09 10:20:58 +02:00
Pau Espin Pedrol
94c5241403 Transceiver: Log error condition no burst in pullRadioVector()
Change-Id: I4f180cc852582b131ba28a8139215335f7ba567d
2019-09-06 14:37:36 +02:00
Pau Espin Pedrol
e3a2516280 Transceiver: Use LOGCHAN in logRxBurst to unify log format
Change-Id: Iec33ab73a9bf90fd0bff9ba453c32ea11bf0670e
2019-09-06 14:37:36 +02:00
Pau Espin Pedrol
1fba10409b Transceiver: Fix logging TN and version
Since tn is declared as uint8_t, it's actually a char, and by default
c++'s ostream& operator<<(ostream&, unsigned char) tries to print chars
with its ASCII visible character instead of numeric value.

Change-Id: I534158e8e1719ad19a9cde7c747a8f8ad5a01a2b
2019-09-06 14:33:59 +02:00
Pau Espin Pedrol
77f3396d04 device: Use LOGCHAN in set_antennas()
Change-Id: I3099498e3a3f26b53d55a96a36cc056f7b25b27a
2019-08-26 17:13:23 +02:00
Pau Espin Pedrol
e0010fa425 lms: Log available antennas if requested antenna fails
Related: OS#3343
Change-Id: Icd328e85b0a75ef67f371a7ed72638053b1854f9
2019-08-26 17:13:17 +02:00
Pau Espin Pedrol
bfc1d0bed7 Transceiver: Enable EDGE detection only on PDCH timeslots
Related: OS#3664
Change-Id: Id16248a1c03f9bc9f323c707857cdf3bf34c4e3e
2019-08-26 16:34:49 +02:00
Pau Espin Pedrol
76ff96e210 Transceiver: exit process when BTS drops connection
We don't want to keep osmo-trx running in a started state once the BTS
controlling it becomes unavailable. If a socket towards the BTS fails,
it means the BTS is gone and the best thing to do is to stop the process
(alternatively we could go back to stopped state instead, and wait for
BTS to re-connect, fur so far this action is good enough).

Related: OS#4170
Change-Id: I2ccbe3c17b39fb792ea7810f840235c348054d66
2019-08-26 15:55:49 +02:00
Pau Espin Pedrol
4e6ec4554d Transceiver: Fix idle ul burst indications being dropped
pullRadioVector() should return true on idle frames because those
indications may be handled by upper layers (TRXDv1). Clarify return code
in function documentation.

Change-Id: If592ed1c04cf7e995f656b313f60edd4d40d1bfd
2019-08-26 15:45:56 +02:00
Pau Espin Pedrol
e4166be76f Transceiver: Clean up receival of downlink bursts
Use a packed structure to clearly indicate what is contained in the
received buffer.

Change-Id: I4d8c0e3c0c717699889f79e50c778d14b6058f2d
2019-08-26 14:34:09 +02:00
Pau Espin Pedrol
e7195ac7d0 Transceiver: Check return value when binding IP addr for clock socket
No need to continue further since anyway we'll end up exiting.

Change-Id: Id8b38b40df5744beb705d560defb06532cf0fd44
2019-08-26 10:55:08 +02:00
Pau Espin Pedrol
ca6a78e193 Transceiver: fix segfault during init if IP addr binding fails
If osmo-trx is started and IP addr binding fails (ie because the IP
address is not assigned in the system), it will try to access a
heap-allocated Thread instance which was not initialized (because init()
function returned earlier).

Fixes: OS#4147
Change-Id: I19f9745cd026c0ff6581895a66bf91b40113b07d
2019-08-26 10:50:20 +02:00
Pau Espin Pedrol
67aa91b2c0 Drop old setPriority related code
This code is not needed anymore since we are setting SCHED_RR scheduler
with a real time priority in main thread during startup, so all threads
will inherit same rt priority, which should be enough to keep the
process working reliably even on high system loads (from non rt
processes).

osmo-trx was tested to be reliable during test with stress-ng as
explained in related ticket below.

Related: OS#2344
Change-Id: I3a88946dd71e9aeeaac9d19d396e2236c302b608
2019-08-21 13:00:41 +02:00
Pau Espin Pedrol
3eed8ebb0d osmo-trx: log to stderr on signal received
Since osmo-trx it's a big multithreaded process and shutdown sequence
can be complex, better use stderr to log signal received events to make
sure log is outputted straigh away and not buffered. In general stdout
is usually line-buffered, but buffering strategy can be more
conservative if output is for instance redirected to a file.

Change-Id: I70ba86919d1f7df41ef3db4916317d27697a025c
2019-08-14 16:26:47 +02:00
Pau Espin Pedrol
d0cbb16a93 Bump version: 1.1.0-dirty → 1.1.1
Patch release to have debian/patches/build-for-debian8.patch applying
correctly again.

Change-Id: I27caf077abc400e9661549886bb8f7827dd299bb
2019-08-08 13:00:06 +02:00
Pau Espin Pedrol
e774d52504 Bump version: 1.0.0.104-72a7 → 1.1.0
Change-Id: Ifd5abfb03015e9233814eb9d843ce2e218987283
2019-08-07 21:12:56 +02:00
Pau Espin Pedrol
72a75f3e43 Require newer version of libosmocore to avoid build failure
Older commit started using osmo_timerd_* APIs which are only available
starting libosmocore 0.12.0, but configure.ac still states 0.11.0 is
accepted, which is incorrect.

Fixes: 4456b6f132
Change-Id: I71743b8a10edb10af51ad8e9289c53e432199b97
2019-08-07 21:11:47 +02:00
Pau Espin Pedrol
27424a39ee Transceiver: Add missing include netinet/in.h
Previous commit started using IPPROTO_UDP without including required
include. Newer versions of libosmocore's osmocom/core/socket.h include
that header so the define is present, but older versions of libosmocore
(such as 0.12.0) don't, so let's include it explicitly (the correct
thing to do).

Fixes: b9d2515704
Change-Id: I67ddf550f3a7fc6a650e1e1d9bde0bbb28785104
2019-08-07 21:10:47 +02:00
Pau Espin Pedrol
c7ac63afa5 Drop old README information, provide new updated README
Previous content in README file is actually a description of the TRXD
and TRXC protocols, and it has already been moved to the User Manual
some time ago. INSTALLATION contained README related information, but it
was really out of date.

So this commit basically drops those two files and provides a new
README.md with content taken from Osmocom's OsmoTRX project wiki page.

Change-Id: I3df00799ce80aa4af43225e69a408ba2cbc444db
2019-08-01 15:17:30 +00:00
Pau Espin Pedrol
cbc02086d5 Move std_inband.rbf under device/usrp1/ dir
This file is only used by USRP1, so let's move it there and avoid
processing it in Makefiles if build for USRP1 is not requested at
configure time.

Change-Id: Ibb40ba487581e76d2ae3e8a420d631670f876cf0
2019-08-01 13:46:00 +02:00
Pau Espin Pedrol
3d29b4d5b0 Move README.DFEsymbolspaced to utils/matlab
Change-Id: Icccc18688ee54e669cf3452b08747b3509042fa2
2019-08-01 13:46:00 +02:00
Pau Espin Pedrol
06d3ba0445 Move Transceiver52/README to UserManual
Change-Id: Ib5a56cfe0c27d027bc0c60abda89e646a80849de
2019-08-01 13:46:00 +02:00
Pau Espin Pedrol
8a784c7145 Move matlab files under utils/matlab
Change-Id: I15b687fbf436d662b264cb00f72b367ccd64b962
2019-08-01 13:46:00 +02:00
Pau Espin Pedrol
7758542087 Move inband-signaling-usb documentation to UserManual
Change-Id: I4d6ef1f54f3d6c5a73ce00dc4640bd698f96842b
2019-08-01 13:46:00 +02:00
Pau Espin Pedrol
68a78099a0 lms: Drop rx_underruns rate ctr, add tx_drop_* rate ctr
After discussion in [1] and further look at the code, it became obvios
rx_underrun events are not happening in general for any SDR (don't
exist), so let's drop that counter. Instead, add Tx Dropped Packet counters,
which were not accounted prior to this commit.

[1] bde55afd29

Change-Id: Iff1535c219a4695a511d383d7c4b06ef6eff959d
2019-08-01 13:45:55 +02:00
Pau Espin Pedrol
50c78dfe85 jenkins.sh: Workaround libusrp build race conditon
When submitting patches to osmo-trx in gerrit, sometimes the jenkins job
fails due to libusrp failing to build. I never have this issue in on my
workstation.

Let's disable parallel make and see if we can avoid it for now.

Related: OS#3970
Change-Id: I24bc54f5872e8edb9fab5b88055a00cebe1a6911
2019-07-30 15:06:24 +02:00
Pau Espin Pedrol
207911bcde usrp1: don't check for non-null underrun pointer
The pointer can't never be null, so avoid checking it.

Change-Id: I12e14641713a6494bc89570f02cecfc6f8fd4b5e
2019-07-29 20:42:56 +02:00
Pau Espin Pedrol
2c673e0f3e radioInterface: Clarify how underruns are handled driving a radioDevice
The underrun parameter in radioDevice's readSamples() is not a "Rx
Underrun" event, but rather it's used to retrieve a "Tx Underrun" which
on some SDRs (like USRP1) seems to be (so far) available only at
readSamples() times.

Thus, underrun parameter for both readSamples() and writeSamples() is
actually flagging the same event, and should be ORed in pushBuffer() as
it's already done in pullBuffer(). Otherwise if implementation is
setting the underrun pointer to false, it could erase the flag being
marked by the counterpart function before isUnderrun() is called (which
is the one responsible to clear the flag).

Change-Id: Id549489fc1485e0d762818c8e682aaddd5041f1c
2019-07-29 20:42:56 +02:00
Pau Espin Pedrol
b4c749b32b Remove unused autogen.sh
autoreconf is used instead, as done in all of the osmocom projects.

Change-Id: I87676cdf6818b4250f478962baf96ad5f28564d5
2019-07-29 15:50:59 +02:00
Pau Espin Pedrol
c641f781dc Remove empty ChangeLog fnd NEWS files
We already have debian/changelog for same purpose. This file is empty
and doesn't exist on other osmocom projects.

Change-Id: I5a90107476ca116bebc8569f4eb1db0fa25807a6
2019-07-29 15:50:46 +02:00
Vadim Yanitskiy
6b30ab0c34 manuals/configuration.adoc: fix copy-paste error in config example
Change-Id: If9de32eed8170038c8c177c8996c78846dddb624
2019-07-24 23:08:19 +07:00
Pau Espin Pedrol
720b912ba9 Transceiver: Clean up code passing parameters to threads
TransceiverChannel naming was misleading there. It's simply a data type
used to pass 2 parameters through the void* of the thread entry
function, so let's clearly specify is a storage for thread params.
Furthermore, we don't need a full C++ class for that, let's simply use a
struct.

Change-Id: I6e3898a8a66520cc5b2a7df9b9ae01b0b272387f
2019-07-23 09:06:01 +00:00
Pau Espin Pedrol
c3325b9aeb Transceiver: Store TRXD version per channel
The setting is negotiatied by osmo-bts-trx on each channel, so let's
keep and use state per channel instead of overwriting the state from
different channels.
Take the chance to change related log lines to also print the channel
number.

Change-Id: If9cf95e89d38d0155ab48b8c0977ca5f381c2aad
2019-07-23 09:06:01 +00:00
Pau Espin Pedrol
88f86a14ba Bind CTRL port to IP addr specified in VTY config
Before this commit, osmo-trx was always setting its CTRL socket to
listen on 127.0.0.1.

Change-Id: I61a06c1b9c20a906e7030f824a93370d041be7b9
2019-07-22 22:15:24 +00:00
Pau Espin Pedrol
21d03d3912 Add SPDX annotation
Related: OS#3515
Change-Id: I3719bd8dc015569ecd81928fc079e27593cdca09
2019-07-22 12:06:26 +02:00
Pau Espin Pedrol
bdb970e495 cosmetic: Fix trailing whitespace in several files
Change-Id: Ifafb68353960fc5046661854ccfb8d783b0efb14
2019-07-22 12:03:39 +02:00
Pau Espin Pedrol
e6319ed32a debian/copyright: Add missing file Utils.* to LGPL-2.1+ list
Change-Id: I36b8b8be48ae8676688786f39008d08b61011ede
2019-07-22 11:59:53 +02:00
Pau Espin Pedrol
1830705e67 debian/copyright: Update wrong paths in license list
Those paths were moved into a subdir a while ago, but this file was not
updated at the time.

Change-Id: I1857338b7a19e2a37f62386ceb4a1fad988272ba
2019-07-22 11:22:48 +02:00
Pau Espin Pedrol
46d0be06ab debian/copyright: Remove non existent radioInterfaceDiversity.cpp from list
Change-Id: I5cfbbafd411e580421f86df6817f91261aceda7e
2019-07-22 11:21:28 +02:00
Harald Welte
2896cecefb radioInterface.cpp: Fix missing member initialization of RadioInterface()
Change-Id: I7264ef35dbe6d3522858eae5b47d150aa1ffe334
Closes: CID#169594
2019-07-21 15:46:06 +00:00
Harald Welte
5c6ca1739f CorrelationSequence: fix initialization of class members
Change-Id: Ia72395f8805e9e2cd700ad1e559a8aa62124aaec
Closes: CID#149371
2019-07-21 15:46:04 +00:00
Harald Welte
80ca1de44a ChannelizerBase: fix initialization of class members
Change-Id: I8c047c8f98e928a62dca1d7b8c003502727c086e
Closes: CID#149374
2019-07-21 15:46:01 +00:00
Harald Welte
2a3d8ba71a Timeval: Restore output stream flags after changing them
Change-Id: I866505f29ed56d8f3ba3aaba70c0d82479987c64
Closes: CID#149361
2019-07-21 15:45:58 +00:00
Vadim Yanitskiy
6fa906c280 trxd_fill_common(): fix TRXD header version coding
The header version field is 4 bit long, so the mask 0x07 == 0b111
is wrong, it should actually be 0x0f == 0b1111.

Change-Id: I290931559ce01cf6e43470b18855c46808d6c2a5
2019-07-20 19:39:18 +07:00
Harald Welte
a7ba484fb2 proto_trxd.c: Use bit-wise AND, not boolean AND
Change-Id: I974c91be7cc119b44c2fb0c53d08009c87de7bf1
Related: CID#202038
2019-07-20 10:13:18 +02:00
Pau Espin Pedrol
758381bad4 Transceiver: Initialize mExtRACH in constructor
Doing so should make Coverity happy:
>>>     CID 200212:  Uninitialized members  (UNINIT_CTOR)
>>>     Non-static class member "mExtRACH" is not initialized in this constructor nor in any functions that it calls.

The current status is actually harmless since the field will be set
during init() time, and the variable is never used before init() is
called.

Fixes: Coverity CID#200212
Change-Id: I17286570a9a6db695a75147e5cbb18c9da7d0fe6
2019-07-19 14:58:32 +02:00
Vadim Yanitskiy
56c5f2959b driveTxPriorityQueue(): enrich logging message
Change-Id: If25c2171f7d1ab98d65f0dbf93d0d8a5a635caf7
2019-07-19 14:58:32 +02:00
Vadim Yanitskiy
dd571c6db1 driveTxPriorityQueue(): check if message header format is supported
Change-Id: I17abf95f5e23236abccc50476cd59931580f5cd3
2019-07-19 14:58:32 +02:00
Vadim Yanitskiy
b31232537a driveTxPriorityQueue(): use trxd_hdr_common for message parsing
Change-Id: If6a93e2b7fc9ada55edbdd16352cd4f7040e3d2a
2019-07-19 14:58:32 +02:00
Vadim Yanitskiy
8d771d24e7 driveTxPriorityQueue(): cosmetic: use proper type for TDMA TN
Change-Id: I8396004616754f84fb465c972fde9e91b18cc49b
2019-07-19 14:58:32 +02:00
Pau Espin Pedrol
cf6113b2fe Transceiver: Support TRXD v1
Related: OS#4006
Change-Id: I53db2678458a7377c87875b58b58b76a1b900517
2019-07-19 14:58:29 +02:00
Pau Espin Pedrol
13c81098f8 Transceiver: Support SETFORMAT command
Only old v0 is supported so far.

Related: OS#4006
Change-Id: If9fc22f9987238a5ff870df7718de4efc9e04289
2019-07-19 11:44:13 +00:00
Pau Espin Pedrol
15fa64bce4 Transceiver: Move out TRXD socket send code to prepare for TRXDv1
Only old v0 is supported so far. TRXD protocol related data/logic is
moved to its own file out of Transceiver class. Code is refactored so it
can be re-used later by TRXDv1.

Related: OS#4006
Change-Id: I5786dd44b076202c6f1a6e82405670e8605797ed
2019-07-19 11:44:13 +00:00
Pau Espin Pedrol
c9202ab0be Logger: global Log mutex is now available from C code
This way the C++ logging API can still be used while allowing for C
files to use the same mutex.

Change-Id: I473e57479f8ae98a84ad00b76ff338f79f732236
2019-07-19 11:44:13 +00:00
Sylvain Munaut
b49a42e70b sigProcLib: Add C/I (Carrier-to-Interference ratio) computation
Related: OS#4006
Change-Id: Ib4ceec553f2e5f77bf3f6777724968456a180f5e
2019-07-19 11:44:13 +00:00
Pau Espin Pedrol
c3d68c159f sigProcLib: detectAnyBurst(): make TSC used to detect burst available to caller
This value will be sent in TRXDv1 protocol.

Related: OS#4006
Change-Id: I603b7b52f957cf897b036dbaeb22c01a55de08c3
2019-07-19 11:44:13 +00:00
Pau Espin Pedrol
7ee2d10113 sigProcLib: detectAnyBurst() family: Use struct to gather all out params
Currently we have 2 out parameters, but in forthcoming commits will add
a third one. All those functions already have too many parameters, so
let's put together all the output params in a struct to pass them easily
and make it easier to understand they are the estimated output values.

Related: OS#4006
Change-Id: I05cfa0ceaa2e633a5e6e404e2eae497ff4442dea
2019-07-19 11:44:13 +00:00
Ruben Undheim
252564b50e Fix spelling discovered by lintian
Change-Id: I5ab9b9c7c47d0d6e674c1f5242e2b3a05006293e
2019-07-17 10:58:22 +00:00
Oliver Smith
2ded53c440 contrib/jenkins.sh: run "make maintainer-clean"
Related: OS#3047
Change-Id: I6d541b47e68f5a8a61ac139a3ea85a9cb33856c1
2019-07-10 13:26:21 +02:00
Pau Espin Pedrol
95c8318d5d Transceiver: Support pulling idle frames in pullRadioVector()
This logic will be used once we support TRXDv1, where idle indications
are sent through the socket.

Related: OS#4006
Change-Id: I46404f6e4055b6d3af3afffb0dfe4a19502917aa
2019-07-03 16:03:21 +02:00
Pau Espin Pedrol
9bb24a1103 Transceiver: pullRadioVector(): Move initialization of var to start of function
This will be needed upon forthcoming refactor to support idle frames,
which will add a goto return. Otherwise compiler complains:

error: jump to label ret_idle [-fpermissive]
note:   crosses initialization of unsigned int max_toa

Change-Id: Icd2793adc7b73a795184639b95fb5da336909b59
2019-07-03 15:49:38 +02:00
Pau Espin Pedrol
d6dbb1b987 Transceiver: Simplify code on early error return when calling detectAnyBurst
We get rid of one branch and simplify code logic.

Change-Id: I026e35262bfe42c3d23ebdc06d84e4908a8380e2
2019-07-03 15:49:38 +02:00
Pau Espin Pedrol
be9cd66020 Transceiver: Avoid noise calculation formula in 2 branches in pullRadioVector
Makes code easier to follow and will help in forthcoming refactoring
once idle frames are supported.

Change-Id: I56c84e9684ca460efd6c983d7e95d8e455bcac69
2019-07-03 15:49:38 +02:00
Pau Espin Pedrol
b9d2515704 Transceiver: replace UDPSocket with libosmocore socket API
We have a good socket API in libosmocore, let's drop osmo-trx socket API
and use libosmocore's one instead of maintaining the two of them.

Change-Id: Ib19856a3e0a7607f63436c4a80b1381a3f318764
2019-07-02 15:07:25 +02:00
Pau Espin Pedrol
7dc07b9425 Transceiver: Get rid of SoftVector in struct trx_ul_burst_ind
Make the interface using trx_ul_burst_ind more implementation agnostic
as well as easier to use. For instance, we don't care about SoftVector
size one returned from pullRadioVector(); we want to use nbits instead.
As a result, we no longer spend time normalizing guard periods. While at
it, change vectorSLicer to return void since it always returns true.

Change-Id: I726e5a98a43367a22c9a4ca5cbd9eb87e6765c7a
2019-07-02 15:05:17 +02:00
Pau Espin Pedrol
07ddce5c1f Transceiver: Drop use of GSM::Time from trx_ul_burst_ind
Use of that class is really not needed since we don't need to do any
calculation with those values, so we can simply store the final values
in the struct.

Related: OS#4006
Change-Id: Iadf2683d7f52138a2248598641f3b702252f325d
2019-07-02 15:05:17 +02:00
Pau Espin Pedrol
25ae190dc6 Transceiver: Move soft bits normalization to pullRadioVector()
That's where all the filling logic happens, while in driveReceiveFIFO we
mostly want to take the burst, generate a message and sent it over the
socket.

Related: OS#4006
Change-Id: Ibfb48877af4ff5ef0f56390901669c8353beaf48
2019-07-02 15:05:10 +02:00
Pau Espin Pedrol
607a414967 Transceiver: Move calculation of normalized values (rssiOffset) to pullRadioVector()
That's where all the filling logic happens, while in driveReceiveFIFO we
mostly want to take the burst, generate a message and sent it over the
socket.
In pullRadioVector this way we always provide normalized values based on
user configuration (VTY rssi-offset).

Related: OS#4006
Change-Id: I1ee28daf21dc287bec564d45d58086d63655c0f6
2019-07-02 15:04:40 +02:00
Pau Espin Pedrol
0e67cf24eb Transceiver: Move nbits burst size calculation to pullRadioVector()
That's where all the filling logic happens, while in driveReceiveFIFO we
mostly want to take the burst, generate a message and sent it over the
socket.

Related: OS#4006
Change-Id: Ib1df10c40d737954904290f57d58b1c77d65f82e
2019-07-02 15:04:29 +02:00
Pau Espin Pedrol
ff6aeb7f62 Transceiver: Drop unused rssi_valid struct field
That field is actually never used. Furthermore, if pullRadioVector()
returns false, then the caller should consider the 'trx_ul_burst_ind'
structure as uninitialized. Moreover, RSSI is mandatory - we cannot send
burst indications without it.

Related: OS#4006
Change-Id: Ia109298aebe8ba4750a39338ba7962555903cd82
2019-07-02 15:04:24 +02:00
Pau Espin Pedrol
ddd18a5e33 Transceiver: refactor: gather uplink burst parameters in struct
A new struct trx_ul_burst_ind is introduced, which will handle
information filled by lower layers upon decoding of uplink bursts.

Methods pullRadioVector() and logRxBurst() are adapted to use that
struct. This way it's easier to understand in/out parameters and it's
also easier to add further parameters to be filled in in the future.

Related: OS#4006
Change-Id: I7e590fb1c0901de627e782f183251c20f4f68d48
2019-07-01 16:13:21 +02:00
Pau Espin Pedrol
778b30a530 Introduce structs to encode TRXD packets
This will ease adding new protocol versions in the future.

Related: OS#4006
Change-Id: I67ffede171eddde436f9057191ed76015a8ea6eb
2019-07-01 16:13:10 +02:00
Pau Espin Pedrol
e9ce77b871 trx_{vty,rate_ctr}: Set proper license AGPLv3+
Take the chance to improve text with author, SPDX tag and fix incorrect
copyright dates.

Related: OS#3515
Change-Id: Ic745312ed07db205b1cdc0f2fa130000319354c5
2019-06-25 12:40:17 +02:00
Pau Espin Pedrol
c27d9f88df Remove AUTHORS file
License of this file doesn't match the license stated in COPYING and in
most source code files (GPLv3 vs AGPLv3). Furthermore, we don't really
maintain this file, so let's drop it to avoid non-up-to-date content
which may introduce confusion.

Related: OS#3515
Change-Id: I536a145cb7696af8e9dbd3065ee5e5f493217ac6
2019-06-25 12:40:17 +02:00
Eric Wild
ac0487eb66 Add option to set stack size in config file, default == 0 == OS default
Change-Id: Id752f6b5ce9a96a67cd1ff835687ce0e03d3a50d
2019-06-17 14:41:34 +02:00
Pau Espin Pedrol
6a305feb0f Add VTY commands to set error ctr thresholds
osmo-trx will validate over time that those thresholds are not reached.
If they are reached, osmo-trx will die. As a result, osmo-bts-trx will
notice and will end up notifying the BSC about it (for instance because
it will also restart its process).

For instance:
"""
ctr-error-threshold rx_drop_events 2 minute
ctr-error-threshold rx_underruns 10 second
"""

In those cases above, osmo-trx will die if rate_ctr rx_drop_events went
to a value higher than 2 per minute, or it will die to if rx_underruns
went higher than 10 per second.

Change-Id: I4bcf44dbf064e2e86dfc3b8a2ad18fea76fbd51a
2019-06-11 14:28:17 +00:00
Pau Espin Pedrol
bde55afd29 lms: Fix stream_stats checks with overrun/underrun
It was initially thought that underruns/overrun fields were
increasing-over-time values.
However, after reading LimeSuite code, it seems overrun and
underrun fields are actually reset upon every call to
LMS_GetStreamStatus().

Related: osmo-trx.git 9281771256
Related: https://github.com/myriadrf/LimeSuite/issues/265
Change-Id: I677232a7b12ee83d26aa34d92f76a91d4b5a63a6
2019-06-09 06:55:13 +00:00
Pau Espin Pedrol
4c50bf435b lms: Drop unusued variable masterClockRate
Change-Id: I19192925d008046f474615a0476b52ddee9a9d78
2019-06-06 19:44:22 +00:00
Pau Espin Pedrol
63e872a701 doc: vty: Update trx_vty_reference.xml
Change-Id: I2587cd47ee48e979de447f7a0f4d79bb5fac0592
2019-06-05 12:50:38 +02:00
Pau Espin Pedrol
b426e4abb4 Rename and move STOP signal from Transceiver to main
The callback actually belongs there, since it's the code/thread in main the one
actually in charge of stopping everything. It simplifies current code,
and more important, allows for new clients of this signal to use it.
This callback will also be used in forthcoming commits by code
controlling rate_ctr thresholds to stop the process if the VTY
configured threshold is used.

Change-Id: Id4159e64225c6606fef34a74b24f37c3a071aceb
2019-06-05 12:50:38 +02:00
Pau Espin Pedrol
4456b6f132 Add rate_ctr support to store/retrieve SDR errors through VTY
Introduce a unified implementation-agnostic interface for radioDevice to
signal SDR error counters to upper layers and manage them.
This patch only implements counters for osmo-trx-lms (other devices will
show all counters unchanged during time).

Sample use through VTY:
"""
OsmoTRX> show rate-counters
osmo-trx statistics 0:
   device:rx_underruns:          0 (0/s 0/m 0/h 0/d) Number of Rx underruns
    device:rx_overruns:          0 (0/s 0/m 0/h 0/d) Number of Rx overruns
   device:tx_underruns:          0 (0/s 0/m 0/h 0/d) Number of Tx underruns
 device:rx_drop_events:          4 (0/s 2/m 3/h 0/d) Number of times Rx samples were dropped by HW
device:rx_drop_samples:        513 (0/s 196/m 425/h 0/d) Number of Rx samples dropped by HW
"""

Change-Id: I78b158141697e5714d04db8b9ccc96f31f34f439
2019-06-05 12:50:38 +02:00
Oliver Smith
76a5013c91 debian: create -doc subpackage with pdf manuals
I have verified, that the resulting debian packages build in my own OBS
namespace (see the -doc packages):
https://download.opensuse.org/repositories/home:/osmith42/Debian_9.0/all/
https://build.opensuse.org/project/show/home:osmith42

Depends: Ib7251cca9116151e473798879375cd5eb48ff3ad (osmo-ci)
Related: OS#3899
Change-Id: I34858a18a34fc467f274ac164697a242f4cf0df8
2019-05-29 12:14:24 +02:00
Pau Espin Pedrol
9281771256 lms: Fix stream_stats checks with droppedPackets
Existing code had a typo (value was assigned from wrong variable).
Furthermore, it was experimentally found that:
while underrun/overrun are documented as "FIFO overrun
count" in LimeSuite.h, it seems droppedPackets ("Number of dropped
packets by HW") is not actually an incrementing counter like the others,
but simply a value set every time LMS_RecvStream() is called. Since we
are actually interested in keeping the count over time, adjust code to
achieve that.

Change-Id: Id93d33400e11360b9536f56a31904328549cfbbf
2019-05-24 17:01:13 +02:00
117 changed files with 3248 additions and 3730 deletions

127
AUTHORS
View File

@@ -1,127 +0,0 @@
#
# Copyright 2008, 2009 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
David A. Burgess, dburgess@kestrelsp.com:
CLI/CLI.cpp
CLI/CLI.h
CommonLibs/Assert.h
CommonLibs/BitVector.cpp
CommonLibs/Interthread.h
CommonLibs/LinkedLists.cpp
CommonLibs/LinkedLists.h
CommonLibs/Regexp.h
CommonLibs/Sockets.cpp
CommonLibs/Sockets.h
CommonLibs/Threads.cpp
CommonLibs/Threads.h
CommonLibs/Timeval.cpp
CommonLibs/Timeval.h
CommonLibs/Vector.h
GSM/GSM610Tables.cpp
GSM/GSM610Tables.h
GSM/GSMCommon.cpp
GSM/GSMCommon.h
GSM/GSMConfig.h
GSM/GSML1FEC.cpp
GSM/GSML1FEC.h
GSM/GSML2LAPDm.cpp
GSM/GSML2LAPDm.h
GSM/GSML3CCElements.cpp
GSM/GSML3CCElements.h
GSM/GSML3CCMessages.cpp
GSM/GSML3CCMessages.h
GSM/GSML3CommonElements.cpp
GSM/GSML3CommonElements.h
GSM/GSML3MMElements.cpp
GSM/GSML3MMElements.h
GSM/GSML3MMMessages.cpp
GSM/GSML3MMMessages.h
GSM/GSML3Message.cpp
GSM/GSML3Message.h
GSM/GSML3RRElements.cpp
GSM/GSML3RRElements.h
GSM/GSML3RRMessages.cpp
GSM/GSML3RRMessages.h
GSM/GSMLogicalChannel.h
GSM/GSMTDMA.cpp
GSM/GSMTDMA.h
GSM/GSMTransfer.cpp
GSM/GSMTransfer.h
LICENSEBLOCK
TRXManager/TRXManager.cpp
Transceiver/Complex.h
tests/CommonLibs/BitVectorTest.cpp
tests/CommonLibs/InterthreadTest.cpp
tests/CommonLibs/SocketsTest.cpp
tests/CommonLibs/TimevalTest.cpp
tests/CommonLibs/VectorTest.cpp
Harvind S. Samra, hssamra@kestrelsp.com:
GSM/GSMConfig.h
GSM/GSMTransfer.h
LICENSEBLOCK
Transceiver/ComplexTest.cpp
Transceiver/Transceiver.cpp
Transceiver/Transceiver.h
Transceiver/USRPDevice.cpp
Transceiver/USRPDevice.h
Transceiver/USRPping.cpp
Transceiver/radioInterface.cpp
Transceiver/radioInterface.h
Transceiver/rcvLPF_651.h
Transceiver/runTransceiver.cpp
Transceiver/sendLPF_961.h
Transceiver/sigProcLib.cpp
Transceiver/sigProcLib.h
Transceiver/sigProcLibTest.cpp
Transceiver/sweepGenerator.cpp
Transceiver/testRadio.cpp
Raffi Sevlian, raffisev@gmail.com:
GSM/GSMCommon.h
GSM/GSMConfig.h
GSM/GSML1FEC.h
GSM/GSML3CCElements.cpp
GSM/GSML3CCElements.h
GSM/GSML3CCMessages.cpp
GSM/GSML3CCMessages.h
GSM/GSML3CommonElements.cpp
GSM/GSML3CommonElements.h
GSM/GSML3MMElements.cpp
GSM/GSML3MMElements.h
GSM/GSML3MMMessages.cpp
GSM/GSML3MMMessages.h
GSM/GSML3Message.cpp
GSM/GSML3Message.h
GSM/GSML3RRElements.cpp
GSM/GSML3RRElements.h
GSM/GSML3RRMessages.cpp
GSM/GSML3RRMessages.h
GSM/GSMLogicalChannel.h
GSM/GSMSAPMux.cpp
GSM/GSMSAPMux.h
GSM/GSMTransfer.h
LICENSEBLOCK
TRXManager/TRXManager.h
Alon Levy, alonlevy1@gmail.com
RRLPMessages.cpp
RRLPMessages.h
RRLPTest.cpp

View File

@@ -666,7 +666,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
=========================================================================
This marks the end of the AGPLv3 text. The following text is appended to the
same file for convience but constituting a distinct document, not part of the
same file for convenience but constituting a distinct document, not part of the
actual AGPL text and not part of an attempt to create a deriviative work based
on the AGPLv3 text.

View File

View File

@@ -1,6 +1,7 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -36,7 +37,7 @@ using namespace std;
/**
Apply a Galois polymonial to a binary seqeunce.
Apply a Galois polymonial to a binary sequence.
@param val The input sequence.
@param poly The polynomial.
@param order The order of the polynomial.

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -45,7 +47,7 @@
// (pat) The elements in the queue are type T*, and
// the Fifo class implements the underlying queue.
// The default is class PointerFIFO, which does not place any restrictions on the type of T,
// and is implemented by allocating auxilliary structures for the queue,
// and is implemented by allocating auxiliary structures for the queue,
// or SingleLinkedList, which implements the queue using an internal pointer in type T,
// which must implement the functional interface of class SingleLinkListNode,
// namely: functions T*next() and void setNext(T*).
@@ -53,7 +55,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
protected:
Fifo mQ;
Fifo mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
@@ -160,7 +162,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
protected:
Fifo mQ;
Fifo mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
@@ -256,7 +258,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
// This recurs (and the InterthreadQueue fills up with data)
// until the read thread's accumulated temporary priority causes it to
// get a second pre-emptive activation over the writing thread,
// resulting in bursts of activity by the read thread.
// resulting in bursts of activity by the read thread.
{ ScopedLock lock(mLock);
mQ.put(val);
}
@@ -281,7 +283,7 @@ template <class T> class InterthreadQueueWithWait {
protected:
PointerFIFO mQ;
PointerFIFO mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
mutable Signal mReadSignal;

View File

@@ -1,6 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.

View File

@@ -1,7 +1,10 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.

View File

@@ -1,6 +1,7 @@
/*
* Copyright (C) 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -35,8 +36,6 @@
using namespace std;
Mutex gLogToLock;
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
{
return os << ss.str();
@@ -45,13 +44,13 @@ 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";
ScopedLock lock(gLogToLock);
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
/* print related function called inside a C++ destructor, use pthread_setcancelstate() APIs.
See osmo-trx commit 86be40b4eb762d5c12e8e3f7388ca9f254e77b36 for more information */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
pthread_setcancelstate(old_state, NULL);
}

View File

@@ -2,6 +2,8 @@
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -56,6 +58,9 @@ extern "C" {
#define LOGLV(category, level) \
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
#define LOGSRC(category, level, file, line) \
Log(category, level, file, line).get() << "[tid=" << pthread_self() << "] "
#define LOGCHAN(chan, category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "][chan=" << chan << "] "

View File

@@ -30,11 +30,11 @@ noinst_LTLIBRARIES = libcommon.la
libcommon_la_SOURCES = \
BitVector.cpp \
LinkedLists.cpp \
Sockets.cpp \
Threads.cpp \
Timeval.cpp \
Logger.cpp \
Utils.cpp \
trx_rate_ctr.cpp \
trx_vty.c \
debug.c
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
@@ -44,12 +44,12 @@ noinst_HEADERS = \
PRBS.h \
Interthread.h \
LinkedLists.h \
Sockets.h \
Threads.h \
Timeval.h \
Vector.h \
Logger.h \
Utils.h \
trx_rate_ctr.h \
trx_vty.h \
debug.h \
osmo_signal.h \

View File

@@ -1,6 +1,8 @@
/*
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -1,287 +0,0 @@
/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <sys/select.h>
#include "Threads.h"
#include "Sockets.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort)
{
assert(address);
assert(hostAndPort);
char *copy = strdup(hostAndPort);
char *colon = strchr(copy,':');
if (!colon) return false;
*colon = '\0';
char *host = copy;
unsigned port = strtol(colon+1,NULL,10);
bool retVal = resolveAddress(address,host,port);
free(copy);
return retVal;
}
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port)
{
assert(address);
assert(host);
// FIXME -- Need to ignore leading/trailing spaces in hostname.
struct hostent *hp;
int h_errno_local;
#ifdef HAVE_GETHOSTBYNAME2_R
struct hostent hostData;
char tmpBuffer[2048];
// There are different flavors of gethostbyname_r(), but
// latest Linux use the following form:
if (gethostbyname2_r(host, AF_INET, &hostData, tmpBuffer, sizeof(tmpBuffer), &hp, &h_errno_local)!=0) {
CERR("WARNING -- gethostbyname2_r() failed for " << host << ", " << hstrerror(h_errno_local));
return false;
}
#else
static Mutex sGethostbynameMutex;
// gethostbyname() is NOT thread-safe, so we should use a mutex here.
// Ideally it should be a global mutex for all non thread-safe socket
// operations and it should protect access to variables such as
// global h_errno.
sGethostbynameMutex.lock();
hp = gethostbyname(host);
h_errno_local = h_errno;
sGethostbynameMutex.unlock();
#endif
if (hp==NULL) {
CERR("WARNING -- gethostbyname() failed for " << host << ", " << hstrerror(h_errno_local));
return false;
}
if (hp->h_addrtype != AF_INET) {
CERR("WARNING -- gethostbyname() resolved " << host << " to something other then AF_INET");
return false;
}
address->sin_family = hp->h_addrtype;
assert(sizeof(address->sin_addr) == hp->h_length);
memcpy(&(address->sin_addr), hp->h_addr_list[0], hp->h_length);
address->sin_port = htons(port);
return true;
}
DatagramSocket::DatagramSocket()
{
memset(mDestination, 0, sizeof(mDestination));
}
void DatagramSocket::nonblocking()
{
fcntl(mSocketFD,F_SETFL,O_NONBLOCK);
}
void DatagramSocket::blocking()
{
fcntl(mSocketFD,F_SETFL,0);
}
void DatagramSocket::close()
{
::close(mSocketFD);
}
DatagramSocket::~DatagramSocket()
{
close();
}
int DatagramSocket::write( const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0,
(struct sockaddr *)mDestination, addressSize());
if (retVal == -1 ) perror("DatagramSocket::write() failed");
return retVal;
}
int DatagramSocket::writeBack( const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0,
(struct sockaddr *)mSource, addressSize());
if (retVal == -1 ) perror("DatagramSocket::write() failed");
return retVal;
}
int DatagramSocket::write( const char * message)
{
size_t length=strlen(message)+1;
return write(message,length);
}
int DatagramSocket::writeBack( const char * message)
{
size_t length=strlen(message)+1;
return writeBack(message,length);
}
int DatagramSocket::send(const struct sockaddr* dest, const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0, dest, addressSize());
if (retVal == -1 ) perror("DatagramSocket::send() failed");
return retVal;
}
int DatagramSocket::send(const struct sockaddr* dest, const char * message)
{
size_t length=strlen(message)+1;
return send(dest,message,length);
}
int DatagramSocket::read(char* buffer, size_t length)
{
socklen_t addr_len = sizeof(mSource);
int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
(struct sockaddr*) &mSource, &addr_len);
if ((rd_length==-1) && (errno!=EAGAIN)) {
perror("DatagramSocket::read() failed");
throw SocketError();
}
return rd_length;
}
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(mSocketFD,&fds);
struct timeval tv;
tv.tv_sec = timeout/1000;
tv.tv_usec = (timeout%1000)*1000;
int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv);
if (sel<0) {
perror("DatagramSocket::read() select() failed");
throw SocketError();
}
if (sel==0) return -1;
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
return -1;
}
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
:DatagramSocket()
{
open(wSrcPort, wSrcIP);
}
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
const char *wDestIP, unsigned short wDestPort)
:DatagramSocket()
{
open(wSrcPort, wSrcIP);
destination(wDestPort, wDestIP);
}
void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
{
resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort );
}
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
{
// create
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
if (mSocketFD<0) {
perror("socket() failed");
throw SocketError();
}
// pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes.
int on = 1;
setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
// bind
struct sockaddr_in address;
size_t length = sizeof(address);
bzero(&address,length);
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(wlocalIP);
address.sin_port = htons(localPort);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");
throw SocketError();
}
}
unsigned short UDPSocket::port() const
{
struct sockaddr_in name;
socklen_t nameSize = sizeof(name);
int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize);
if (retVal==-1) throw SocketError();
return ntohs(name.sin_port);
}
// vim:ts=4:sw=4

View File

@@ -1,173 +0,0 @@
/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SOCKETS_H
#define SOCKETS_H
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <errno.h>
#include <list>
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#define MAX_UDP_LENGTH 1500
/** A function to resolve IP host names. */
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port);
/** Resolve an address of the form "<host>:<port>". */
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort);
/** An exception to throw when a critical socket operation fails. */
class SocketError {};
#define SOCKET_ERROR {throw SocketError(); }
/** Abstract class for connectionless sockets. */
class DatagramSocket {
protected:
int mSocketFD; ///< underlying file descriptor
char mDestination[256]; ///< address to which packets are sent
char mSource[256]; ///< return address of most recent received packet
public:
/** An almost-does-nothing constructor. */
DatagramSocket();
virtual ~DatagramSocket();
/** Return the address structure size for this socket type. */
virtual size_t addressSize() const = 0;
/**
Send a binary packet.
@param buffer The data bytes to send to mDestination.
@param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
@return number of bytes written, or -1 on error.
*/
int write( const char * buffer, size_t length);
/**
Send a C-style string packet.
@param buffer The data bytes to send to mDestination.
@return number of bytes written, or -1 on error.
*/
int write( const char * buffer);
/**
Send a binary packet.
@param buffer The data bytes to send to mSource.
@param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
@return number of bytes written, or -1 on error.
*/
int writeBack(const char * buffer, size_t length);
/**
Send a C-style string packet.
@param buffer The data bytes to send to mSource.
@return number of bytes written, or -1 on error.
*/
int writeBack(const char * buffer);
/**
Receive a packet.
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
@return The number of bytes received or -1 on non-blocking pass.
*/
int read(char* buffer, size_t length);
/**
Receive a packet with a timeout.
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
@param maximum wait time in milliseconds
@return The number of bytes received or -1 on timeout.
*/
int read(char* buffer, size_t length, unsigned timeout);
/** Send a packet to a given destination, other than the default. */
int send(const struct sockaddr *dest, const char * buffer, size_t length);
/** Send a C-style string to a given destination, other than the default. */
int send(const struct sockaddr *dest, const char * buffer);
/** Make the socket non-blocking. */
void nonblocking();
/** Make the socket blocking (the default). */
void blocking();
/** Close the socket. */
void close();
};
/** UDP/IP User Datagram Socket */
class UDPSocket : public DatagramSocket {
public:
/** Open a USP socket with an OS-assigned port and no default destination. */
UDPSocket(const char *localIP, unsigned short localPort);
/** Given a full specification, open the socket and set the dest address. */
UDPSocket(const char *localIP, unsigned short localPort,
const char *remoteIP, unsigned short remotePort);
/** Set the destination port. */
void destination( unsigned short wDestPort, const char * wDestIP );
/** Return the actual port number in use. */
unsigned short port() const;
/** Open and bind the UDP socket to a local port. */
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
/** Give the return address of the most recently received packet. */
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
size_t addressSize() const { return sizeof(struct sockaddr_in); }
};
#endif
// vim:ts=4:sw=4

View File

@@ -1,6 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -39,7 +40,9 @@
using namespace std;
#ifndef HAVE_ATOMIC_OPS
pthread_mutex_t atomic_ops_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
@@ -135,8 +138,10 @@ void Thread::start(void *(*task)(void*), void *arg)
// (pat) Moved initialization to constructor to avoid crash in destructor.
//res = pthread_attr_init(&mAttrib);
//assert(!res);
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
assert(!res);
if (mStackSize != 0) {
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
assert(!res);
}
res = pthread_create(&mThread, &mAttrib, task, arg);
assert(!res);
}

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -26,6 +28,8 @@
#ifndef THREADS_H
#define THREADS_H
#include "config.h"
#include <pthread.h>
#include <iostream>
#include <assert.h>
@@ -158,7 +162,7 @@ class Thread {
public:
/** Create a thread in a non-running state. */
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
Thread(size_t wStackSize = 0):mThread((pthread_t)0) {
pthread_attr_init(&mAttrib); // (pat) moved this here.
mStackSize=wStackSize;
}
@@ -182,12 +186,34 @@ class Thread {
}
}
/** Send cancelation to thread */
/** Send cancellation to thread */
void cancel() { pthread_cancel(mThread); }
};
#ifdef HAVE_ATOMIC_OPS
#define osmo_trx_sync_fetch_and_and(ptr, value) __sync_fetch_and_and((ptr), (value))
#define osmo_trx_sync_or_and_fetch(ptr, value) __sync_or_and_fetch((ptr), (value))
#else
extern pthread_mutex_t atomic_ops_mutex;
static inline int osmo_trx_sync_fetch_and_and(int *ptr, int value)
{
pthread_mutex_lock(&atomic_ops_mutex);
int tmp = *ptr;
*ptr &= value;
pthread_mutex_unlock(&atomic_ops_mutex);
return tmp;
}
static inline int osmo_trx_sync_or_and_fetch(int *ptr, int value)
{
int tmp;
pthread_mutex_lock(&atomic_ops_mutex);
*ptr |= value;
tmp = *ptr;
pthread_mutex_unlock(&atomic_ops_mutex);
return tmp;
}
#endif
#endif
// vim: ts=4 sw=4

View File

@@ -1,6 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -81,14 +82,15 @@ long Timeval::delta(const Timeval& other) const
int32_t deltaNs = other.nsec() - nsec();
return 1000*deltaS + deltaNs/1000000;
}
ostream& operator<<(ostream& os, const Timeval& tv)
{
os.setf( ios::fixed, ios::floatfield );
ios_base::fmtflags flags_backup = os.setf( ios::fixed, ios::floatfield );
os << tv.seconds();
os.flags( flags_backup );
return os;
}

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -82,7 +84,7 @@ class Timeval {
uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
uint32_t nsec() const { return mTimespec.tv_nsec; }
/** Return differnce from other (other-self), in ms. */
/** Return difference from other (other-self), in ms. */
long delta(const Timeval& other) const;
/** Elapsed time in ms. */

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,6 +2,8 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -34,7 +36,7 @@
#include <assert.h>
#include <stdlib.h>
// We cant use Logger.h in this file...
// We can't use Logger.h in this file...
extern int gVectorDebug;
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
@@ -230,7 +232,7 @@ template <class T> class Vector {
assert(mStart+span<=mEnd);
for (i = 0; i < span; i++, src++, dst++)
*dst = *src;
/*TODO if not non-trivially copyable type class, optimize:
/*TODO if not non-trivially copiable type class, optimize:
memcpy(dst,mStart,span*sizeof(T)); */
}

View File

@@ -1,3 +1,38 @@
/*
* Copyright (C) 2018-2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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 "config.h"
/* If HAVE_GETTID, then "_GNU_SOURCE" may need to be defined to use gettid() */
#if HAVE_GETTID
#define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "config.h"
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include "debug.h"
@@ -10,21 +45,39 @@ static const struct log_info_cat default_categories[] = {
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXCLK] = {
.name = "DTRXCLK",
.description = "TRX Master Clock",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXCTRL] = {
.name = "DTRXCTRL",
.description = "TRX CTRL interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXDDL] = {
.name = "DTRXDDL",
.description = "TRX Data interface Downlink",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXDUL] = {
.name = "DTRXDUL",
.description = "TRX CTRL interface Uplink",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DDEV] = {
.name = "DDEV",
.description = "Device/Driver specific code",
.color = NULL,
.enabled = 1, .loglevel = LOGL_INFO,
},
[DLMS] = {
.name = "DLMS",
.description = "Logging from within LimeSuite itself",
[DDEVDRV] = {
.name = "DDEVDRV",
.description = "Logging from external device driver library implementing lower level specifics",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
@@ -34,3 +87,15 @@ const struct log_info log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
pid_t my_gettid(void)
{
#if HAVE_GETTID
return gettid();
#elif defined(LINUX) && defined(__NR_gettid)
return (pid_t) syscall(__NR_gettid);
#else
#pragma message ("use pid as tid")
return getpid();
#endif
}

View File

@@ -1,11 +1,29 @@
#pragma once
#include <stdbool.h>
#include <sys/types.h>
#include <osmocom/core/logging.h>
extern const struct log_info log_info;
/* Debug Areas of the code */
enum {
DMAIN,
DTRXCLK,
DTRXCTRL,
DTRXDDL,
DTRXDUL,
DDEV,
DLMS,
DDEVDRV,
};
pid_t my_gettid(void);
#define CLOGC(category, level, fmt, args...) do { \
LOGP(category, level, "[tid=%ld] " fmt, (long int) my_gettid(), ##args); \
} while(0)
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
LOGP(category, level, "[tid=%ld][chan=%zu] " fmt, (long int) my_gettid(), chan, ##args); \
} while(0)

View File

@@ -5,6 +5,8 @@
*
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -26,10 +28,30 @@
/* Signalling subsystems */
enum signal_subsystems {
SS_TRANSC,
SS_MAIN,
SS_DEVICE,
};
/* SS_TRANSC signals */
enum SS_TRANSC {
S_TRANSC_STOP_REQUIRED, /* Transceiver fatal error, it should be stopped */
/* SS_MAIN signals */
enum SS_MAIN {
S_MAIN_STOP_REQUIRED, /* TRX fatal error, it should be stopped */
};
/* SS_DEVICE signals */
enum SS_DEVICE {
/* Device internal counters changed. Counters are provided as cb data
(struct device_counters). Must be sent with PTHREAD_CANCEL_DISABLE
to avoid deadlocks in case osmo-trx process is asked to exit. */
S_DEVICE_COUNTER_CHANGE,
};
/* signal cb for signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> */
struct device_counters {
size_t chan;
unsigned int rx_overruns;
unsigned int tx_underruns;
unsigned int rx_dropped_events;
unsigned int rx_dropped_samples;
unsigned int tx_dropped_events;
unsigned int tx_dropped_samples;
};

336
CommonLibs/trx_rate_ctr.cpp Normal file
View File

@@ -0,0 +1,336 @@
/*
* Copyright (C) 2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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.
*/
/*
* rate_ctr API uses several osmocom select loop features, and as a result,
* calls to it must be done through the main thread (the one running the osmocom
* loop in osmo-trx).
* Since read/write from/to SDR is done in separate threads (even read and write
* each use a different thread), we must use some sort of message passing system
* between main thread feeding rate_ctr structures and the Rx/Tx threads
* generating the events.
* The idea is that upon read/write issues, lower layers (SDR APIs) provide us with
* underrun/overrun/droppedPackets information, and in that case we pass that up
* the stack through signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> with signal_cb
* being a pointer to a "struct device_counters" structure, which contains
* device (implementation agnostic) statful counters for different kind of
* statistics.
* That signal is processed here in device_sig_cb, where a copy of the "struct
* device_counters" structure is held and the main thread is instructed through
* a timerfd to update rate_ctr APIs against this copy. All this is done inside
* a mutex to avoid different race conditions (between Rx andTx threads, and
* between Rx/Tx and main thread). For the same reason, callers of signal
* <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> (device_sig_cb), that is Rx/Tx threads,
* must do so with PTHREAD_CANCEL_DISABLE, in order to avoid possible deadlocks
* in case the main thread decides to cancel other threads due to a shutdown
* operation (fi SIGKILL received)
*/
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <arpa/inet.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/select.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/timer.h>
#include "osmo_signal.h"
#include "trx_vty.h"
#include "trx_rate_ctr.h"
}
#include "Threads.h"
#include "Logger.h"
/* Used in ctrs_pending, when set it means that channel slot contains unused
(non-pending) counter data */
#define PENDING_CHAN_NONE SIZE_MAX
static void *trx_rate_ctr_ctx;
static struct rate_ctr_group** rate_ctrs;
static struct device_counters* ctrs_pending;
static size_t chan_len;
static struct osmo_fd rate_ctr_timerfd;
static Mutex rate_ctr_mutex;
struct osmo_timer_list threshold_timer;
static LLIST_HEAD(threshold_list);
static int threshold_timer_sched_secs;
static bool threshold_initied;
const struct value_string rate_ctr_intv[] = {
{ RATE_CTR_INTV_SEC, "per-second" },
{ RATE_CTR_INTV_MIN, "per-minute" },
{ RATE_CTR_INTV_HOUR, "per-hour" },
{ RATE_CTR_INTV_DAY, "per-day" },
{ 0, NULL }
};
const struct value_string trx_chan_ctr_names[] = {
{ TRX_CTR_RX_OVERRUNS, "rx_overruns" },
{ TRX_CTR_TX_UNDERRUNS, "tx_underruns" },
{ TRX_CTR_RX_DROP_EV, "rx_drop_events" },
{ TRX_CTR_RX_DROP_SMPL, "rx_drop_samples" },
{ TRX_CTR_TX_DROP_EV, "tx_drop_events" },
{ TRX_CTR_TX_DROP_SMPL, "tx_drop_samples" },
{ 0, NULL }
};
static const struct rate_ctr_desc trx_chan_ctr_desc[] = {
[TRX_CTR_RX_OVERRUNS] = { "device:rx_overruns", "Number of Rx overruns in FIFO queue" },
[TRX_CTR_TX_UNDERRUNS] = { "device:tx_underruns", "Number of Tx underruns in FIFO queue" },
[TRX_CTR_RX_DROP_EV] = { "device:rx_drop_events", "Number of times Rx samples were dropped by HW" },
[TRX_CTR_RX_DROP_SMPL] = { "device:rx_drop_samples", "Number of Rx samples dropped by HW" },
[TRX_CTR_TX_DROP_EV] = { "device:tx_drop_events", "Number of times Tx samples were dropped by HW" },
[TRX_CTR_TX_DROP_SMPL] = { "device:tx_drop_samples", "Number of Tx samples dropped by HW" }
};
static const struct rate_ctr_group_desc trx_chan_ctr_group_desc = {
.group_name_prefix = "trx:chan",
.group_description = "osmo-trx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(trx_chan_ctr_desc),
.ctr_desc = trx_chan_ctr_desc,
};
static int rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
size_t chan;
struct rate_ctr *ctr;
LOGC(DMAIN, NOTICE) << "Main thread is updating counters";
rate_ctr_mutex.lock();
for (chan = 0; chan < chan_len; chan++) {
if (ctrs_pending[chan].chan == PENDING_CHAN_NONE)
continue;
LOGCHAN(chan, DMAIN, INFO) << "rate_ctr update";
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_OVERRUNS];
rate_ctr_add(ctr, ctrs_pending[chan].rx_overruns - ctr->current);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_UNDERRUNS];
rate_ctr_add(ctr, ctrs_pending[chan].tx_underruns - ctr->current);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_DROP_EV];
rate_ctr_add(ctr, ctrs_pending[chan].rx_dropped_events - ctr->current);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_DROP_SMPL];
rate_ctr_add(ctr, ctrs_pending[chan].rx_dropped_samples - ctr->current);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_DROP_EV];
rate_ctr_add(ctr, ctrs_pending[chan].tx_dropped_events - ctr->current);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_DROP_SMPL];
rate_ctr_add(ctr, ctrs_pending[chan].tx_dropped_samples - ctr->current);
/* Mark as done */
ctrs_pending[chan].chan = PENDING_CHAN_NONE;
}
if (osmo_timerfd_disable(&rate_ctr_timerfd) < 0)
LOGC(DMAIN, ERROR) << "Failed to disable timerfd";
rate_ctr_mutex.unlock();
return 0;
}
/* Callback function to be called every time we receive a signal from DEVICE */
static int device_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct device_counters *ctr;
/* Delay sched around 20 ms, in case we receive several calls from several
* channels batched */
struct timespec next_sched = {.tv_sec = 0, .tv_nsec = 20*1000*1000};
/* no automatic re-trigger */
struct timespec intv_sched = {.tv_sec = 0, .tv_nsec = 0};
switch (signal) {
case S_DEVICE_COUNTER_CHANGE:
ctr = (struct device_counters *)signal_data;
LOGCHAN(ctr->chan, DMAIN, NOTICE) << "Received counter change from radioDevice";
rate_ctr_mutex.lock();
ctrs_pending[ctr->chan] = *ctr;
if (osmo_timerfd_schedule(&rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
LOGC(DMAIN, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno);
}
rate_ctr_mutex.unlock();
break;
default:
break;
}
return 0;
}
/************************************
* ctr_threshold APIs
************************************/
static const char* ctr_threshold_2_vty_str(struct ctr_threshold *ctr)
{
static char buf[256];
int rc = 0;
rc += snprintf(buf, sizeof(buf), "ctr-error-threshold %s", get_value_string(trx_chan_ctr_names, ctr->ctr_id));
rc += snprintf(buf + rc, sizeof(buf) - rc, " %d %s", ctr->val, get_value_string(rate_ctr_intv, ctr->intv));
return buf;
}
static void threshold_timer_cb(void *data)
{
struct ctr_threshold *ctr_thr;
struct rate_ctr *rate_ctr;
size_t chan;
LOGC(DMAIN, DEBUG) << "threshold_timer_cb fired!";
llist_for_each_entry(ctr_thr, &threshold_list, list) {
for (chan = 0; chan < chan_len; chan++) {
rate_ctr = &rate_ctrs[chan]->ctr[ctr_thr->ctr_id];
LOGCHAN(chan, DMAIN, INFO) << "checking threshold: " << ctr_threshold_2_vty_str(ctr_thr)
<< " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
if (rate_ctr->intv[ctr_thr->intv].rate >= ctr_thr->val) {
LOGCHAN(chan, DMAIN, FATAL) << "threshold reached, stopping! " << ctr_threshold_2_vty_str(ctr_thr)
<< " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
return;
}
}
}
osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);
}
static size_t ctr_threshold_2_seconds(struct ctr_threshold *ctr)
{
size_t mult = 0;
switch (ctr->intv) {
case RATE_CTR_INTV_SEC:
mult = 1;
break;
case RATE_CTR_INTV_MIN:
mult = 60;
break;
case RATE_CTR_INTV_HOUR:
mult = 60*60;
break;
case RATE_CTR_INTV_DAY:
mult = 60*60*24;
break;
default:
OSMO_ASSERT(false);
}
return mult;
}
static void threshold_timer_update_intv() {
struct ctr_threshold *ctr, *min_ctr;
size_t secs, min_secs;
/* Avoid scheduling timer until itself and other structures are prepared
by trx_rate_ctr_init */
if (!threshold_initied)
return;
if (llist_empty(&threshold_list)) {
if (osmo_timer_pending(&threshold_timer))
osmo_timer_del(&threshold_timer);
return;
}
min_ctr = llist_first_entry(&threshold_list, struct ctr_threshold, list);
min_secs = ctr_threshold_2_seconds(min_ctr);
llist_for_each_entry(ctr, &threshold_list, list) {
secs = ctr_threshold_2_seconds(ctr);
if( min_secs > secs)
min_secs = secs;
}
threshold_timer_sched_secs = OSMO_MAX(min_secs / 2 - 1, 1);
LOGC(DMAIN, INFO) << "New ctr-error-threshold check interval: "
<< threshold_timer_sched_secs << " seconds";
osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);
}
/* Init rate_ctr subsystem. Expected to be called during process start by main thread before VTY is ready */
void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx)
{
size_t i;
trx_rate_ctr_ctx = ctx;
chan_len = trx_ctx->cfg.num_chans;
ctrs_pending = (struct device_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct device_counters));
rate_ctrs = (struct rate_ctr_group**) talloc_zero_size(ctx, chan_len * sizeof(struct rate_ctr_group*));
for (i = 0; i < chan_len; i++) {
ctrs_pending[i].chan = PENDING_CHAN_NONE;
rate_ctrs[i] = rate_ctr_group_alloc(ctx, &trx_chan_ctr_group_desc, i);
if (!rate_ctrs[i]) {
LOGCHAN(i, DMAIN, ERROR) << "Failed to allocate rate ctr";
exit(1);
}
}
rate_ctr_timerfd.fd = -1;
if (osmo_timerfd_setup(&rate_ctr_timerfd, rate_ctr_timerfd_cb, NULL) < 0) {
LOGC(DMAIN, ERROR) << "Failed to setup timerfd";
exit(1);
}
osmo_signal_register_handler(SS_DEVICE, device_sig_cb, NULL);
/* Now set up threshold checks */
threshold_initied = true;
osmo_timer_setup(&threshold_timer, threshold_timer_cb, NULL);
threshold_timer_update_intv();
}
void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr)
{
struct ctr_threshold *new_ctr;
new_ctr = talloc_zero(trx_rate_ctr_ctx, struct ctr_threshold);
*new_ctr = *ctr;
LOGC(DMAIN, NOTICE) << "Adding new threshold check: " << ctr_threshold_2_vty_str(new_ctr);
llist_add(&new_ctr->list, &threshold_list);
threshold_timer_update_intv();
}
int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr)
{
struct ctr_threshold *ctr;
llist_for_each_entry(ctr, &threshold_list, list) {
if (ctr->intv != del_ctr->intv ||
ctr->ctr_id != del_ctr->ctr_id ||
ctr->val != del_ctr->val)
continue;
LOGC(DMAIN, NOTICE) << "Deleting threshold check: " << ctr_threshold_2_vty_str(del_ctr);
llist_del(&ctr->list);
talloc_free(ctr);
threshold_timer_update_intv();
return 0;
}
return -1;
}
void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix)
{
struct ctr_threshold *ctr;
llist_for_each_entry(ctr, &threshold_list, list) {
vty_out(vty, "%s%s%s", indent_prefix, ctr_threshold_2_vty_str(ctr), VTY_NEWLINE);
}
}

30
CommonLibs/trx_rate_ctr.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/command.h>
enum TrxCtr {
TRX_CTR_RX_OVERRUNS,
TRX_CTR_TX_UNDERRUNS,
TRX_CTR_RX_DROP_EV,
TRX_CTR_RX_DROP_SMPL,
TRX_CTR_TX_DROP_EV,
TRX_CTR_TX_DROP_SMPL,
};
struct ctr_threshold {
/*! Linked list of all counter groups in the system */
struct llist_head list;
enum rate_ctr_intv intv;
enum TrxCtr ctr_id;
uint32_t val;
};
extern const struct value_string rate_ctr_intv[];
extern const struct value_string trx_chan_ctr_names[];
struct trx_ctx;
void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx);
void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr);
int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr);
void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix);

View File

@@ -1,20 +1,24 @@
/*
* Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH
* Copyright (C) 2018-2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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 General Public License for more details.
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* See the COPYING file in the main directory for details.
*/
#include <string.h>
@@ -28,30 +32,42 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/misc.h>
#include "trx_rate_ctr.h"
#include "trx_vty.h"
#include "../config.h"
static struct trx_ctx* g_trx_ctx;
static const struct value_string clock_ref_names[] = {
const struct value_string clock_ref_names[] = {
{ REF_INTERNAL, "internal" },
{ REF_EXTERNAL, "external" },
{ REF_GPS, "gpsdo" },
{ 0, NULL }
};
static const struct value_string filler_names[] = {
{ FILLER_DUMMY, "Dummy bursts" },
{ FILLER_ZERO, "Disabled" },
{ FILLER_NORM_RAND, "Normal bursts with random payload" },
{ FILLER_EDGE_RAND, "EDGE bursts with random payload" },
{ FILLER_ACCESS_RAND, "Access bursts with random payload" },
const struct value_string filler_names[] = {
{ FILLER_DUMMY, "Dummy bursts (C0 only)" },
{ FILLER_ZERO, "Empty bursts" },
{ FILLER_NORM_RAND, "GMSK Normal Bursts with random payload" },
{ FILLER_EDGE_RAND, "8-PSK Normal Bursts with random payload" },
{ FILLER_ACCESS_RAND, "Access Bursts with random payload" },
{ 0, NULL }
};
static const struct value_string filler_types[] = {
{ FILLER_DUMMY, "dummy" },
{ FILLER_ZERO, "zero" },
{ FILLER_NORM_RAND, "random-nb-gmsk" },
{ FILLER_EDGE_RAND, "random-nb-8psk" },
{ FILLER_ACCESS_RAND, "random-ab" },
{ 0, NULL }
};
struct trx_ctx *trx_from_vty(struct vty *v)
{
/* It can't hurt to force callers to continue to pass the vty instance
@@ -167,57 +183,10 @@ DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd,
"test rtsc <0-7>",
"Set the Random Normal Burst test mode with TSC\n"
"TSC\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (trx->cfg.rach_delay_set) {
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
VTY_NEWLINE);
return CMD_WARNING;
}
trx->cfg.rtsc_set = true;
trx->cfg.rtsc = atoi(argv[0]);
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
trx->cfg.filler = FILLER_NORM_RAND;
return CMD_SUCCESS;
}
DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd,
"test rach-delay <0-68>",
"Set the Random Access Burst test mode with delay\n"
"RACH delay\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (trx->cfg.rtsc_set) {
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
VTY_NEWLINE);
return CMD_WARNING;
}
if (trx->cfg.egprs) {
vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s",
VTY_NEWLINE);
return CMD_WARNING;
}
trx->cfg.rach_delay_set = true;
trx->cfg.rach_delay = atoi(argv[0]);
trx->cfg.filler = FILLER_ACCESS_RAND;
return CMD_SUCCESS;
}
DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
"clock-ref (internal|external|gpsdo)",
"Set the Reference Clock\n"
"Enable internal referece (default)\n"
"Enable internal reference (default)\n"
"Enable external 10 MHz reference\n"
"Enable GPSDO reference\n")
{
@@ -300,7 +269,6 @@ DEFUN(cfg_egprs, cfg_egprs_cmd,
trx->cfg.egprs = false;
} else if (strcmp("enable", argv[0]) == 0) {
trx->cfg.egprs = true;
trx->cfg.filler = FILLER_EDGE_RAND;
} else {
return CMD_WARNING;
}
@@ -335,14 +303,171 @@ DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_filler, cfg_filler_cmd,
"filler dummy",
"Enable C0 filler table\n"
"Dummy method\n")
DEFUN(cfg_stack_size, cfg_stack_size_cmd,
"stack-size <0-2147483647>",
"Set the stack size per thread in BYTE, 0 = OS default\n"
"Stack size per thread in BYTE\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.filler = FILLER_DUMMY;
trx->cfg.stack_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_filler, cfg_filler_type_cmd,
"filler type (zero|dummy|random-nb-gmsk|random-nb-8psk|random-ab)",
"Filler burst settings\n"
"Filler burst type (default=zero)\n"
"Send an empty burst when there is nothing to send (default)\n"
"Send a dummy burst when there is nothing to send on C0 (TRX0) and empty burst on other channels."
" Use for OpenBTS compatibility only, don't use with OsmoBTS as it breaks encryption.\n"
"Send a GMSK modulated Normal Burst with random bits when there is nothing to send."
" Use for spectrum mask testing. Configure 'filler tsc' to set training sequence.\n"
"Send an 8-PSK modulated Normal Burst with random bits when there is nothing to send."
" Use for spectrum mask testing. Configure 'filler tsc' to set training sequence.\n"
"Send an Access Burst with random bits when there is nothing to send. Use for Rx/Tx alignment."
" Configure 'filler access-burst-delay' to introduce artificial delay.\n"
)
{
struct trx_ctx *trx = trx_from_vty(vty);
// trx->cfg.filler is unsigned, so we need an interim int var to detect errors
int type = get_string_value(filler_types, argv[0]);
if (type < 0) {
trx->cfg.filler = FILLER_ZERO;
return CMD_WARNING;
}
trx->cfg.filler = type;
return CMD_SUCCESS;
}
DEFUN(cfg_test_rtsc, cfg_filler_tsc_cmd,
"filler tsc <0-7>",
"Filler burst settings\n"
"Set the TSC for GMSK/8-PSK Normal Burst random fillers. Used only with 'random-nb-gmsk' and"
" 'random-nb-8psk' filler types. (default=0)\n"
"TSC\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rtsc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_test_rach_delay, cfg_filler_rach_delay_cmd,
"filler access-burst-delay <0-68>",
"Filler burst settings\n"
"Set the delay for Access Burst random fillers. Used only with 'random-ab' filler type. (default=0)\n"
"RACH delay in symbols\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rach_delay = atoi(argv[0]);
return CMD_SUCCESS;
}
static int vty_ctr_name_2_id(const char* str) {
size_t i;
for (i = 0; trx_chan_ctr_names[i].str; i++) {
if (strstr(trx_chan_ctr_names[i].str, str)) {
return i;
}
}
return -1;
}
static int vty_intv_name_2_id(const char* str) {
size_t i;
for (i = 0; rate_ctr_intv[i].str; i++) {
if (strcmp(rate_ctr_intv[i].str, str) == 0) {
return i;
}
}
return -1;
}
#define THRESHOLD_ARGS "(rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples)"
#define THRESHOLD_STR_VAL(s) "Set threshold value for rate_ctr device:" OSMO_STRINGIFY_VAL(s) "\n"
#define THRESHOLD_STRS \
THRESHOLD_STR_VAL(rx_overruns) \
THRESHOLD_STR_VAL(tx_underruns) \
THRESHOLD_STR_VAL(rx_drop_events) \
THRESHOLD_STR_VAL(rx_drop_samples) \
THRESHOLD_STR_VAL(tx_drop_events) \
THRESHOLD_STR_VAL(tx_drop_samples)
#define INTV_ARGS "(per-second|per-minute|per-hour|per-day)"
#define INTV_STR_VAL(s) "Threshold value sampled " OSMO_STRINGIFY_VAL(s) "\n"
#define INTV_STRS \
INTV_STR_VAL(per-second) \
INTV_STR_VAL(per-minute) \
INTV_STR_VAL(per-hour) \
INTV_STR_VAL(per-day)
DEFUN(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
"ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
"Threshold rate for error counter\n"
THRESHOLD_STRS
"Value to set for threshold\n"
INTV_STRS)
{
int rc;
struct ctr_threshold ctr;
struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.ctr_id = (enum TrxCtr)rc;
ctr.val = atoi(argv[1]);
rc = vty_intv_name_2_id(argv[2]);
if (rc < 0) {
vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s",
argv[0], ctr.val, argv[2], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.intv = (enum rate_ctr_intv) rc;
trx_rate_ctr_threshold_add(&ctr);
return CMD_SUCCESS;
}
DEFUN(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
"no ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
NO_STR "Threshold rate for error counter\n"
THRESHOLD_STRS
"Value to set for threshold\n"
INTV_STRS)
{
int rc;
struct ctr_threshold ctr;
struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.ctr_id = (enum TrxCtr)rc;
ctr.val = atoi(argv[1]);
rc = vty_intv_name_2_id(argv[2]);
if (rc < 0) {
vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s",
argv[0], ctr.val, argv[2], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.intv = (enum rate_ctr_intv) rc;
if (trx_rate_ctr_threshold_del(&ctr) < 0) {
vty_out(vty, "no ctr-error-threshold: Entry to delete not found%s", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
@@ -428,10 +553,6 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
if (trx->cfg.rx_sps != DEFAULT_RX_SPS)
vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
if (trx->cfg.rtsc_set)
vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
if (trx->cfg.rach_delay_set)
vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
if (trx->cfg.clock_ref != REF_INTERNAL)
vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE);
@@ -444,6 +565,15 @@ static int config_write_trx(struct vty *vty)
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);
if (trx->cfg.filler != FILLER_ZERO)
vty_out(vty, " filler type %s%s", get_value_string(filler_types, trx->cfg.filler), VTY_NEWLINE);
if (trx->cfg.rtsc > 0)
vty_out(vty, " filler tsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
if (trx->cfg.rach_delay > 0)
vty_out(vty, " filler access-burst-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
if (trx->cfg.stack_size != 0)
vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
trx_rate_ctr_threshold_write_config(vty, " ");
for (i = 0; i < trx->cfg.num_chans; i++) {
chan = &trx->cfg.chans[i];
@@ -468,11 +598,9 @@ static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE);
vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc,
trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay,
trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
vty_out(vty, " Filler Burst Type: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
vty_out(vty, " Filler Burst TSC: %u%s", trx->cfg.rtsc, VTY_NEWLINE);
vty_out(vty, " Filler Burst RACH Delay: %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE);
@@ -482,6 +610,7 @@ static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
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, " Stack size per Thread in BYTE (0 = OS default): %u%s", trx->cfg.stack_size, VTY_NEWLINE);
vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE);
for (i = 0; i < trx->cfg.num_chans; i++) {
chan = &trx->cfg.chans[i];
@@ -540,6 +669,7 @@ static int trx_vty_go_parent(struct vty *vty)
static const char trx_copyright[] =
"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n"
"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n"
"Copyright (C) 2013-2019 Fairwaves, Inc.\r\n"
"Copyright (C) 2015 Ettus Research LLC\r\n"
"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
@@ -582,8 +712,6 @@ int trx_vty_init(struct trx_ctx* trx)
install_element(TRX_NODE, &cfg_dev_args_cmd);
install_element(TRX_NODE, &cfg_tx_sps_cmd);
install_element(TRX_NODE, &cfg_rx_sps_cmd);
install_element(TRX_NODE, &cfg_test_rtsc_cmd);
install_element(TRX_NODE, &cfg_test_rach_delay_cmd);
install_element(TRX_NODE, &cfg_clock_ref_cmd);
install_element(TRX_NODE, &cfg_multi_arfcn_cmd);
install_element(TRX_NODE, &cfg_offset_cmd);
@@ -592,12 +720,19 @@ int trx_vty_init(struct trx_ctx* trx)
install_element(TRX_NODE, &cfg_egprs_cmd);
install_element(TRX_NODE, &cfg_ext_rach_cmd);
install_element(TRX_NODE, &cfg_rt_prio_cmd);
install_element(TRX_NODE, &cfg_filler_cmd);
install_element(TRX_NODE, &cfg_filler_type_cmd);
install_element(TRX_NODE, &cfg_filler_tsc_cmd);
install_element(TRX_NODE, &cfg_filler_rach_delay_cmd);
install_element(TRX_NODE, &cfg_ctr_error_threshold_cmd);
install_element(TRX_NODE, &cfg_no_ctr_error_threshold_cmd);
install_element(TRX_NODE, &cfg_stack_size_cmd);
install_element(TRX_NODE, &cfg_chan_cmd);
install_node(&chan_node, dummy_config_write);
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
logging_vty_add_deprecated_subsys(g_trx_ctx, "lms");
return 0;
}

View File

@@ -5,6 +5,8 @@
#include "config_defs.h"
extern struct vty_app_info g_vty_info;
extern const struct value_string clock_ref_names[];
extern const struct value_string filler_names[];
/* Maximum number of physical RF channels */
#define TRX_CHAN_MAX 8
@@ -51,9 +53,7 @@ struct trx_ctx {
unsigned int tx_sps;
unsigned int rx_sps;
unsigned int rtsc;
bool rtsc_set;
unsigned int rach_delay;
bool rach_delay_set;
enum ReferenceType clock_ref;
enum FillerType filler;
bool multi_arfcn;
@@ -63,6 +63,7 @@ struct trx_ctx {
bool ext_rach;
bool egprs;
unsigned int sched_rr;
unsigned int stack_size;
unsigned int num_chans;
struct trx_chan chans[TRX_CHAN_MAX];
} cfg;

View File

@@ -2,6 +2,8 @@
* Copyright 2008 Free Software Foundation, Inc.
* Copyright 2011 Range Networks, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*

View File

@@ -2,6 +2,8 @@
/*
* Copyright 2008-2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -166,7 +168,7 @@ class Time {
unsigned newTN = (mTN + other.mTN) % 8;
uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe;
return Time(newFN,newTN);
}
}
int operator-(const Time& other) const
{

View File

@@ -1,19 +0,0 @@
Installation Requirements
osmo-trx compiles to a simple Unix binary and does not require special
installation.
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
running configure.
To run osmo-trx, the following should be installed:
libuhd (https://gnuradio.org).
This is part of the GNURadio installation.
For information on specific executables, see tests/README.tests and
apps/README.apps.
See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
information.

View File

@@ -36,11 +36,9 @@ SUBDIRS = \
tests
EXTRA_DIST = \
autogen.sh \
INSTALLATION \
LEGAL \
COPYING \
README
README.md
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

0
NEWS
View File

116
README
View File

@@ -1,116 +0,0 @@
This is the interface to the transcevier.
Each TRX Manager UDP socket interface represents a single ARFCN.
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
Give a base port B (5700), the master clock interface is at port P=B.
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
The corresponding core-side interface for every socket is at P+100.
For any given build, the number of ARFCN interfaces can be fixed.
Indications on the Master Clock Interface
The master clock interface is output only (from the radio).
Messages are "indications".
CLOCK gives the current value of the transceiver clock to be used by the core.
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
IND CLOCK <totalFrames>
Commands on the Per-ARFCN Control Interface
The per-ARFCN control interface uses a command-reponse protocol.
Commands are NULL-terminated ASCII strings, one per UDP socket.
Each command has a corresponding response.
Every command is of the form:
CMD <cmdtype> [params]
The <cmdtype> is the actual command.
Parameters are optional depending on the commands type.
Every response is of the form:
RSP <cmdtype> <status> [result]
The <status> is 0 for success and a non-zero error code for failure.
Successful responses may include results, depending on the command type.
Power Control
POWEROFF shuts off transmitter power and stops the demodulator.
CMD POWEROFF
RSP POWEROFF <status>
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
This command fails if the transmitter and receiver are not yet tuned.
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
If the transceiver is already on, it response with success to this command.
CMD POWERON
RSP POWERON <status>
SETPOWER sets output power in dB wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD SETPOWER <dB>
RSP SETPOWER <status> <dB>
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD ADJPOWER <dBStep>
RSP ADJPOWER <status> <dBLevel>
Tuning Control
RXTUNE tunes the receiver to a given frequency in kHz.
This command fails if the receiver is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD RXTUNE <kHz>
RSP RXTUNE <status> <kHz>
TXTUNE tunes the transmitter to a given frequency in kHz.
This command fails if the transmitter is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD TXTUNE <kHz>
RSP TXTUNE <status> <kHz>
Timeslot Control
SETSLOT sets the format of the uplink timeslots in the ARFCN.
The <timeslot> indicates the timeslot of interest.
The <chantype> indicates the type of channel that occupies the timeslot.
A chantype of zero indicates the timeslot is off.
CMD SETSLOT <timeslot> <chantype>
RSP SETSLOT <status> <timeslot> <chantype>
Messages on the per-ARFCN Data Interface
Messages on the data interface carry one radio burst per UDP message.
Received Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte RSSI in -dBm
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
Transmit Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte transmit level wrt ARFCN max, -dB (attenuation)
148 bytes output symbol values, 0 & 1

66
README.md Normal file
View File

@@ -0,0 +1,66 @@
About OsmTRX
============
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"
OsmoTRX is based on the transceiver code from the
[OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup
to operate independently with the purpose of using with non-OpenBTS software and
projects, while still maintaining backwards compatibility with OpenBTS when
possible. Currently there are numerous features contained in OsmoTRX that extend
the functionality of the OpenBTS transceiver. These features include enhanced
support for various embedded platforms - notably ARM - and dual channel
diversity support for the Fairwaves umtrx.
Homepage
--------
The official homepage of the project is
<https://osmocom.org/projects/osmotrx/wiki/OsmoTRX>
GIT Repository
--------------
You can clone from the official osmo-trx.git repository using
git clone git://git.osmocom.org/osmo-trx.git
There is a cgit interface at <http://git.osmocom.org/osmo-trx/>
Documentation
-------------
Doxygen-generated API documentation is generated during the build process, but
also available online for each of the sub-libraries at User Manual for OsmoTRX
can be generated during the build process, and is also available online at
<http://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
Mailing List
------------
Discussions related to OsmoTRX are happening on the openbsc@lists.osmocom.org
mailing list, please see <https://lists.osmocom.org/mailman/listinfo/openbsc>
for subscription options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We us a gerrit based patch submission/review process for managing contributions.
Please see <https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit>
for more details
The current patch queue for OsmoTRX can be seen at
<https://gerrit.osmocom.org/q/project:osmo-trx+status:open>

View File

@@ -1,9 +1,11 @@
/*
* Polyphase channelizer
*
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -63,7 +65,7 @@ float *Channelizer::outputBuffer(size_t chan) const
return hInputs[chan];
}
/*
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
@@ -78,8 +80,8 @@ bool Channelizer::rotate(const float *in, size_t len)
deinterleave(in, len, hInputs, blockLen, m);
/*
* Convolve through filterbank while applying and saving sample history
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
@@ -96,7 +98,7 @@ bool Channelizer::rotate(const float *in, size_t len)
return true;
}
/* Setup channelizer paramaters */
/* Setup channelizer parameters */
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
: ChannelizerBase(m, blockLen, hLen)
{

View File

@@ -1,8 +1,10 @@
/*
* Polyphase channelizer
*
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -55,10 +57,10 @@ static void reverse(float *buf, size_t len)
}
}
/*
/*
* Create polyphase filterbank
*
* Implementation based material found in,
* Implementation based material found in,
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
@@ -70,7 +72,7 @@ bool ChannelizerBase::initFilters()
float sum = 0.0f, scale = 0.0f;
float midpt = (float) (protoLen - 1.0) / 2.0;
/*
/*
* Allocate 'M' partition filters and the temporary prototype
* filter. Coefficients are real only and must be 16-byte memory
* aligned for SSE usage.
@@ -90,7 +92,7 @@ bool ChannelizerBase::initFilters()
memalign(16, hLen * 2 * sizeof(float));
}
/*
/*
* Generate the prototype filter with a Blackman-harris window.
* Scale coefficients with DC filter gain set to unity divided
* by the number of channels.
@@ -110,7 +112,7 @@ bool ChannelizerBase::initFilters()
}
scale = (float) m / sum;
/*
/*
* Populate partition filters and reverse the coefficients per
* convolution requirements.
*/
@@ -174,7 +176,7 @@ bool ChannelizerBase::mapBuffers()
return true;
}
/*
/*
* Setup filterbank internals
*/
bool ChannelizerBase::init()
@@ -222,11 +224,12 @@ bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
return true;
}
/*
* Setup channelizer paramaters
/*
* Setup channelizer parameters
*/
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
: fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
: subFilters(NULL), hInputs(NULL), hOutputs(NULL), hist(NULL),
fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
{
this->m = m;
this->hLen = hLen;

View File

@@ -32,7 +32,7 @@ protected:
/* Buffer length validity checking */
bool checkLen(size_t innerLen, size_t outerLen);
public:
/* Initilize channelizer/synthesis filter internals */
/* Initialize channelizer/synthesis filter internals */
bool init();
};

View File

@@ -5,7 +5,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

View File

@@ -25,14 +25,7 @@ SUBDIRS = arch 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
rev4dir = $(datadir)/usrp/rev4
dist_rev2_DATA = std_inband.rbf
dist_rev4_DATA = std_inband.rbf
EXTRA_DIST = README
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
noinst_LTLIBRARIES = libtransceiver_common.la
@@ -46,7 +39,8 @@ COMMON_SOURCES = \
Transceiver.cpp \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp
Synthesis.cpp \
proto_trxd.c
libtransceiver_common_la_SOURCES = \
$(COMMON_SOURCES) \
@@ -66,7 +60,8 @@ noinst_HEADERS = \
Resampler.h \
ChannelizerBase.h \
Channelizer.h \
Synthesis.h
Synthesis.h \
proto_trxd.h
COMMON_LDADD = \
libtransceiver_common.la \

View File

@@ -1,35 +0,0 @@
The Transceiver
The transceiver consists of three modules:
--- transceiver
--- radioInterface
--- USRPDevice
The USRPDevice module is basically a driver that reads/writes
packets to a USRP with two RFX900 daughterboards, board
A is the Tx chain and board B is the Rx chain.
The radioInterface module is basically an interface b/w the
transceiver and the USRP. It operates the basestation clock
based upon the sample count of received USRP samples. Packets
from the USRP are queued and segmented into GSM bursts that are
passed up to the transceiver; bursts from the transceiver are
passed down to the USRP.
The transceiver basically operates "layer 0" of the GSM stack,
performing the modulation, detection, and demodulation of GSM
bursts. It communicates with the GSM stack via three UDP sockets,
one socket for data, one for control messages, and one socket to
pass clocking information. The transceiver contains a priority
queue to sort to-be-transmitted bursts, and a filler table to fill
in timeslots that do not have bursts in the priority queue. The
transceiver tries to stay ahead of the basestation clock, adapting
its latency when underruns are reported by the radioInterface/USRP.
Received bursts (from the radioInterface) pass through a simple
energy detector, a RACH or midamble correlator, and a DFE-based demodulator.
NOTE: There's a SWLOOPBACK #define statement, where the USRP is replaced
with a memory buffer. In this mode, data written to the USRP is actually stored
in a buffer, and read commands to the USRP simply pull data from this buffer.
This was very useful in early testing, and still may be useful in testing basic
Transceiver and radioInterface functionality.

View File

@@ -2,6 +2,8 @@
* Rational Sample Rate Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -51,7 +53,7 @@ void Resampler::initFilters(float bw)
float cutoff;
float sum = 0.0f, scale = 0.0f;
/*
/*
* Allocate partition filters and the temporary prototype filter
* according to numerator of the rational rate. Coefficients are
* real only and must be 16-byte memory aligned for SSE usage.
@@ -60,10 +62,10 @@ void Resampler::initFilters(float bw)
for (auto &part : partitions)
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
/*
/*
* Generate the prototype filter with a Blackman-harris window.
* Scale coefficients with DC filter gain set to unity divided
* by the number of filter partitions.
* by the number of filter partitions.
*/
float a0 = 0.35875;
float a1 = 0.48829;
@@ -137,8 +139,8 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
/* Generate output from precomputed input/output paths */
for (size_t i = 0; i < out_len; i++) {
n = in_index[i];
path = out_path[i];
n = in_index[i];
path = out_path[i];
convolve_real(in, in_len,
reinterpret_cast<float *>(partitions[path]),

View File

@@ -2,6 +2,8 @@
* Rational Sample Rate Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -28,17 +30,17 @@ public:
/* Constructor for rational sample rate conversion
* @param p numerator of resampling ratio
* @param q denominator of resampling ratio
* @param filt_len length of each polyphase subfilter
* @param filt_len length of each polyphase subfilter
*/
Resampler(size_t p, size_t q, size_t filt_len = 16);
~Resampler();
/* Initilize resampler filterbank.
/* Initialize resampler filterbank.
* @param bw bandwidth factor on filter generation (pre-window)
* @return false on error, zero otherwise
*
* Automatic setting is to compute the filter to prevent aliasing with
* a Blackman-Harris window. Adjustment is made through a bandwith
* a Blackman-Harris window. Adjustment is made through a bandwidth
* factor to shift the cutoff and/or the constituent filter lengths.
* Calculation of specific rolloff factors or 3-dB cutoff points is
* left as an excersize for the reader.
@@ -58,7 +60,7 @@ public:
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
/* Get filter length
* @return number of taps in each filter partition
* @return number of taps in each filter partition
*/
size_t len();

View File

@@ -1,9 +1,11 @@
/*
* Polyphase synthesis filter
*
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -74,7 +76,7 @@ bool Synthesis::resetBuffer(size_t chan)
return true;
}
/*
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
@@ -92,8 +94,8 @@ bool Synthesis::rotate(float *out, size_t len)
cxvec_fft(fftHandle);
/*
* Convolve through filterbank while applying and saving sample history
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: GPL-3.0+
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
@@ -22,6 +24,7 @@
*/
#include <stdio.h>
#include <netinet/in.h>
#include <iomanip> // std::setprecision
#include <fstream>
#include "Transceiver.h"
@@ -29,6 +32,11 @@
extern "C" {
#include "osmo_signal.h"
#include "proto_trxd.h"
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/bits.h>
}
#ifdef HAVE_CONFIG_H
@@ -115,12 +123,12 @@ Transceiver::Transceiver(int wBasePort,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset)
double wRssiOffset, int wStackSize)
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset), sig_cbfn(NULL),
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset), stackSize(wStackSize),
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mExtRACH(false), mEdge(false),
mOn(false), mForceClockInterface(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
mWriteBurstToDiskMask(0)
{
@@ -139,14 +147,21 @@ Transceiver::~Transceiver()
sigProcLibDestroy();
if (mClockSocket >= 0)
close(mClockSocket);
for (size_t i = 0; i < mChans; i++) {
mControlServiceLoopThreads[i]->cancel();
mControlServiceLoopThreads[i]->join();
delete mControlServiceLoopThreads[i];
if (mControlServiceLoopThreads[i]) {
mControlServiceLoopThreads[i]->cancel();
mControlServiceLoopThreads[i]->join();
delete mControlServiceLoopThreads[i];
}
mTxPriorityQueues[i].clear();
delete mCtrlSockets[i];
delete mDataSockets[i];
if (mCtrlSockets[i] >= 0)
close(mCtrlSockets[i]);
if (mDataSockets[i] >= 0)
close(mDataSockets[i]);
}
}
@@ -165,20 +180,20 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
int d_srcport, d_dstport, c_srcport, c_dstport;
if (!mChans) {
LOG(ALERT) << "No channels assigned";
LOG(FATAL) << "No channels assigned";
return false;
}
if (!sigProcLibSetup()) {
LOG(ALERT) << "Failed to initialize signal processing library";
LOG(FATAL) << "Failed to initialize signal processing library";
return false;
}
mExtRACH = ext_rach;
mEdge = edge;
mDataSockets.resize(mChans);
mCtrlSockets.resize(mChans);
mDataSockets.resize(mChans, -1);
mCtrlSockets.resize(mChans, -1);
mControlServiceLoopThreads.resize(mChans);
mTxPriorityQueueServiceLoopThreads.resize(mChans);
mRxServiceLoopThreads.resize(mChans);
@@ -186,20 +201,39 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
mTxPriorityQueues.resize(mChans);
mReceiveFIFO.resize(mChans);
mStates.resize(mChans);
mVersionTRXD.resize(mChans);
/* Filler table retransmissions - support only on channel 0 */
if (filler == FILLER_DUMMY)
mStates[0].mRetrans = true;
/* Setup sockets */
mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
mLocalAddr.c_str(), mBasePort,
mRemoteAddr.c_str(), mBasePort + 100,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (mClockSocket < 0)
return false;
for (size_t i = 0; i < mChans; i++) {
c_srcport = mBasePort + 2 * i + 1;
c_dstport = mBasePort + 2 * i + 101;
d_srcport = mBasePort + 2 * i + 2;
d_dstport = mBasePort + 2 * i + 102;
mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
mLocalAddr.c_str(), c_srcport,
mRemoteAddr.c_str(), c_dstport,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (mCtrlSockets[i] < 0)
return false;
mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
mLocalAddr.c_str(), d_srcport,
mRemoteAddr.c_str(), d_dstport,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (mCtrlSockets[i] < 0)
return false;
}
/* Randomize the central clock */
@@ -211,10 +245,12 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
/* Start control threads */
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mControlServiceLoopThreads[i] = new Thread(32768);
TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
params->trx = this;
params->num = i;
mControlServiceLoopThreads[i] = new Thread(stackSize);
mControlServiceLoopThreads[i]->start((void * (*)(void*))
ControlServiceLoopAdapter, (void*) chan);
ControlServiceLoopAdapter, (void*) params);
if (i && filler == FILLER_DUMMY)
filler = FILLER_ZERO;
@@ -225,17 +261,6 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
return true;
}
void Transceiver::setSignalHandler(osmo_signal_cbfn cbfn)
{
if (this->sig_cbfn)
osmo_signal_unregister_handler(SS_TRANSC, this->sig_cbfn, NULL);
if (cbfn) {
this->sig_cbfn = cbfn;
osmo_signal_register_handler(SS_TRANSC, this->sig_cbfn, NULL);
}
}
/*
* Start the transceiver
*
@@ -260,13 +285,13 @@ bool Transceiver::start()
mLatencyUpdateTime = time;
if (!mRadioInterface->start()) {
LOG(ALERT) << "Device failed to start";
LOG(FATAL) << "Device failed to start";
return false;
}
/* Device is running - launch I/O threads */
mRxLowerLoopThread = new Thread(32768);
mTxLowerLoopThread = new Thread(32768);
mRxLowerLoopThread = new Thread(stackSize);
mTxLowerLoopThread = new Thread(stackSize);
mTxLowerLoopThread->start((void * (*)(void*))
TxLowerLoopAdapter,(void*) this);
mRxLowerLoopThread->start((void * (*)(void*))
@@ -274,15 +299,19 @@ bool Transceiver::start()
/* Launch uplink and downlink burst processing threads */
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mRxServiceLoopThreads[i] = new Thread(32768);
TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
params->trx = this;
params->num = i;
mRxServiceLoopThreads[i] = new Thread(stackSize);
mRxServiceLoopThreads[i]->start((void * (*)(void*))
RxUpperLoopAdapter, (void*) chan);
RxUpperLoopAdapter, (void*) params);
chan = new TransceiverChannel(this, i);
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
params->trx = this;
params->num = i;
mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
TxUpperLoopAdapter, (void*) chan);
TxUpperLoopAdapter, (void*) params);
}
mForceClockInterface = true;
@@ -342,12 +371,12 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
radioVector *radio_burst;
if (chan >= mTxPriorityQueues.size()) {
LOG(ALERT) << "Invalid channel " << chan;
LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
return;
}
if (wTime.TN() > 7) {
LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
return;
}
@@ -390,7 +419,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
state = &mStates[i];
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
LOGCHAN(i, DTRXDDL, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
if (state->mRetrans)
updateFillerTable(i, burst);
@@ -529,8 +558,8 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
return mExtRACH ? EXT_RACH : RACH;
else if ((mod52 == 25) || (mod52 == 51))
return IDLE;
else
return TSC;
else /* Enable 8-PSK burst detection if EDGE is enabled */
return mEdge ? EDGE : TSC;
break;
}
case LOOPBACK:
@@ -558,44 +587,57 @@ void writeToFile(radioVector *radio_burst, size_t chan)
/*
* Pull bursts from the FIFO and handle according to the slot
* and burst correlation type. Equalzation is currently disabled.
* returns 0 on success (bi filled), negative on error (bi content undefined):
* -ENOENT: timeslot is off (fn and tn in bi are filled),
* -EIO: read error
*/
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan)
int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
{
int rc;
complex amp;
float toa, max = -1.0, avg = 0.0;
struct estim_burst_params ebp;
float max = -1.0, avg = 0.0;
unsigned max_toa;
int max_i = -1;
signalVector *burst;
SoftVector *bits = NULL;
GSM::Time burstTime;
SoftVector *rxBurst;
TransceiverState *state = &mStates[chan];
isRssiValid = false;
/* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read();
if (!radio_burst)
return NULL;
if (!radio_burst) {
LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
return -EIO;
}
/* Set time and determine correlation type */
GSM::Time time = radio_burst->getTime();
CorrType type = expectedCorrType(time, chan);
burstTime = radio_burst->getTime();
CorrType type = expectedCorrType(burstTime, chan);
/* Enable 8-PSK burst detection if EDGE is enabled */
if (mEdge && (type == TSC))
type = EDGE;
/* Initialize struct bi */
bi->nbits = 0;
bi->fn = burstTime.FN();
bi->tn = burstTime.TN();
bi->rssi = 0.0;
bi->toa = 0.0;
bi->noise = 0.0;
bi->idle = false;
bi->modulation = MODULATION_GMSK;
bi->tss = 0; /* TODO: we only support tss 0 right now */
bi->tsc = 0;
bi->ci = 0.0;
/* Debug: dump bursts to disk */
/* bits 0-7 - chan 0 timeslots
* bits 8-15 - chan 1 timeslots */
if (mWriteBurstToDiskMask & ((1<<time.TN()) << (8*chan)))
if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
writeToFile(radio_burst, chan);
/* No processing if the timeslot is off.
* Not even power level or noise calculation. */
if (type == OFF) {
delete radio_burst;
return NULL;
return -ENOENT;
}
/* Select the diversity channel with highest energy */
@@ -609,59 +651,65 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
}
if (max_i < 0) {
LOG(ALERT) << "Received empty burst";
delete radio_burst;
return NULL;
LOGCHAN(chan, DTRXDUL, FATAL) << "Received empty burst";
goto ret_idle;
}
/* Average noise on diversity paths and update global levels */
burst = radio_burst->getVector(max_i);
avg = sqrt(avg / radio_burst->chans());
wTime = time;
RSSI = 20.0 * log10(rxFullScale / avg);
/* RSSI estimation are valid */
isRssiValid = true;
if (type == IDLE) {
/* Update noise levels */
state->mNoises.insert(avg);
state->mNoiseLev = state->mNoises.avg();
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
delete radio_burst;
return NULL;
} else {
/* Do not update noise levels */
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
}
unsigned max_toa = (type == RACH || type == EXT_RACH) ?
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
if (type == IDLE)
goto ret_idle;
max_toa = (type == RACH || type == EXT_RACH) ?
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
/* Detect normal or RACH bursts */
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
if (rc > 0) {
type = (CorrType) rc;
} else if (rc <= 0) {
if (rc == -SIGERR_CLIP) {
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
} else if (rc != SIGERR_NONE) {
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
}
delete radio_burst;
return NULL;
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
if (rc <= 0) {
if (rc == -SIGERR_CLIP)
LOGCHAN(chan, DTRXDUL, NOTICE) << "Clipping detected on received RACH or Normal Burst";
else if (rc != SIGERR_NONE)
LOGCHAN(chan, DTRXDUL, NOTICE) << "Unhandled RACH or Normal Burst detection error";
goto ret_idle;
}
timingOffset = toa;
type = (CorrType) rc;
bi->toa = ebp.toa;
bi->tsc = ebp.tsc;
bi->ci = ebp.ci;
rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
/* EDGE demodulator returns 444 (gSlotLen * 3) bits */
if (rxBurst->size() == EDGE_BURST_NBITS) {
bi->modulation = MODULATION_8PSK;
bi->nbits = EDGE_BURST_NBITS;
} else { /* size() here is actually gSlotLen + 8, due to guard periods */
bi->modulation = MODULATION_GMSK;
bi->nbits = gSlotLen;
}
// Convert -1..+1 soft bits to 0..1 soft bits
vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
delete rxBurst;
delete radio_burst;
return bits;
return 0;
ret_idle:
bi->idle = true;
delete radio_burst;
return 0;
}
void Transceiver::reset()
@@ -702,7 +750,7 @@ static bool match_cmd(char *buf,
return true;
}
void Transceiver::driveControl(size_t chan)
bool Transceiver::driveControl(size_t chan)
{
char buffer[MAX_PACKET_LENGTH + 1];
char response[MAX_PACKET_LENGTH + 1];
@@ -710,17 +758,19 @@ void Transceiver::driveControl(size_t chan)
int msgLen;
/* Attempt to read from control socket */
msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
if (msgLen < 1)
return;
msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
if (msgLen <= 0) {
LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
return false;
}
/* Zero-terminate received string */
buffer[msgLen] = '\0';
/* Verify a command signature */
if (strncmp(buffer, "CMD ", 4)) {
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
return;
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
return false;
}
/* Set command pointer */
@@ -744,7 +794,7 @@ void Transceiver::driveControl(size_t chan)
unsigned ts = 0, ss = 0;
sscanf(params, "%u %u", &ts, &ss);
if (ts > 7 || ss > 7) {
sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
} else {
mHandover[ts][ss] = true;
sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
@@ -804,7 +854,7 @@ void Transceiver::driveControl(size_t chan)
sscanf(params, "%d", &freqKhz);
mRxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
}
else
@@ -815,7 +865,7 @@ void Transceiver::driveControl(size_t chan)
sscanf(params, "%d", &freqKhz);
mTxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
}
else
@@ -837,146 +887,173 @@ void Transceiver::driveControl(size_t chan)
int timeslot;
sscanf(params, "%d %d", &timeslot, &corrCode);
if ((timeslot < 0) || (timeslot > 7)) {
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
return;
return true;
}
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
setModulus(timeslot, chan);
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
} else if (match_cmd(command, "SETFORMAT", &params)) {
// set TRXD protocol version
unsigned version_recv;
sscanf(params, "%u", &version_recv);
LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
if (version_recv > TRX_DATA_FORMAT_VER) {
LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
<< "in favor of " << TRX_DATA_FORMAT_VER;
sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
} else {
LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
mVersionTRXD[chan] = version_recv;
sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
}
} else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
// debug command! may change or disapear without notice
// debug command! may change or disappear without notice
// set a mask which bursts to dump to disk
int mask;
sscanf(params, "%d", &mask);
mWriteBurstToDiskMask = mask;
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
} else {
LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
LOGCHAN(chan, DTRXCTRL, NOTICE) << "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);
msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
if (msgLen <= 0) {
LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
return false;
}
return true;
}
bool Transceiver::driveTxPriorityQueue(size_t chan)
{
int msgLen;
int burstLen;
char buffer[EDGE_BURST_NBITS + 50];
struct trxd_hdr_v01_dl *dl;
char buffer[sizeof(*dl) + EDGE_BURST_NBITS];
uint32_t fn;
// check data socket
size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
if (msgLen == gSlotLen + 1 + 4 + 1) {
burstLen = gSlotLen;
} else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
if (mSPSTx != 4)
return false;
burstLen = EDGE_BURST_NBITS;
} else {
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
if (msgLen <= 0) {
LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
return false;
}
int timeSlot = (int) buffer[0];
uint64_t frameNum = 0;
for (int i = 0; i < 4; i++)
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
switch (msgLen) {
case sizeof(*dl) + gSlotLen: /* GSM burst */
burstLen = gSlotLen;
break;
case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
if (mSPSTx != 4) {
LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << mSPSTx;
return false;
}
burstLen = EDGE_BURST_NBITS;
break;
default:
LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
return false;
}
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
dl = (struct trxd_hdr_v01_dl *) buffer;
/* Convert TDMA FN to the host endianness */
fn = osmo_load32be(&dl->common.fn);
/* Make sure we support the received header format */
switch (dl->common.version) {
case 0:
/* Version 1 has the same format */
case 1:
break;
default:
LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
return false;
}
LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
<< "): fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", burst_len=" << burstLen;
int RSSI = (int) buffer[5];
BitVector newBurst(burstLen);
BitVector::iterator itr = newBurst.begin();
char *bufferItr = buffer+6;
uint8_t *bufferItr = dl->soft_bits;
while (itr < newBurst.end())
*itr++ = *bufferItr++;
GSM::Time currTime = GSM::Time(frameNum,timeSlot);
GSM::Time currTime = GSM::Time(fn, dl->common.tn);
addRadioVector(chan, newBurst, RSSI, currTime);
addRadioVector(chan, newBurst, dl->tx_att, currTime);
return true;
}
void Transceiver::driveReceiveRadio()
bool Transceiver::driveReceiveRadio()
{
int rc = mRadioInterface->driveReceiveRadio();
if (rc == 0) {
usleep(100000);
} else if (rc < 0) {
LOG(FATAL) << "radio Interface receive failed, requesting stop.";
osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this);
} else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
mForceClockInterface = false;
writeClockInterface();
return true;
}
if (rc < 0)
return false;
if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
mForceClockInterface = false;
return writeClockInterface();
}
return true;
}
void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
double rssi, double noise, double toa)
void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
{
LOG(DEBUG) << std::fixed << std::right
<< " chan: " << chan
<< " time: " << time
<< " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
<< "dBFS/" << std::setw(6) << -dbm << "dBm"
<< " noise: " << std::setw(5) << std::setprecision(1) << noise
<< "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm"
<< " TOA: " << std::setw(5) << std::setprecision(2) << toa
<< " bits: " << *burst;
std::ostringstream os;
for (size_t i=0; i < bi->nbits; i++) {
if (bi->rx_burst[i] > 0.5) os << "1";
else if (bi->rx_burst[i] > 0.25) os << "|";
else if (bi->rx_burst[i] > 0.0) os << "'";
else os << "-";
}
LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
<< " time: " << unsigned(bi->tn) << ":" << bi->fn
<< " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
<< "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
<< " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
<< "dBFS/" << std::setw(6) << -bi->noise << "dBm"
<< " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
<< " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
<< " bits: " << os;
}
void Transceiver::driveReceiveFIFO(size_t chan)
bool Transceiver::driveReceiveFIFO(size_t chan)
{
SoftVector *rxBurst = NULL;
double RSSI; // in dBFS
double dBm; // in dBm
double TOA; // in symbols
int TOAint; // in 1/256 symbols
double noise; // noise level in dBFS
GSM::Time burstTime;
bool isRssiValid; // are RSSI, noise and burstTime valid
unsigned nbits = gSlotLen;
struct trx_ul_burst_ind bi;
int rc;
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
if (!rxBurst)
return;
if ((rc = pullRadioVector(chan, &bi)) < 0) {
if (rc == -ENOENT) { /* timeslot off, continue processing */
LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
return true;
}
return false; /* other errors: we want to stop the process */
}
// Convert -1..+1 soft bits to 0..1 soft bits
vectorSlicer(rxBurst);
if (!bi.idle)
logRxBurst(chan, &bi);
/*
* EDGE demodulator returns 444 (148 * 3) bits
*/
if (rxBurst->size() == gSlotLen * 3)
nbits = gSlotLen * 3;
dBm = RSSI + rssiOffset;
logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
char burstString[nbits + 10];
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = (int)dBm;
burstString[6] = (TOAint >> 8) & 0x0ff;
burstString[7] = TOAint & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
for (unsigned i = 0; i < nbits; i++)
burstString[8 + i] = (char) round((*burstItr++) * 255.0);
burstString[nbits + 9] = '\0';
delete rxBurst;
mDataSockets[chan]->write(burstString, nbits + 10);
switch (mVersionTRXD[chan]) {
case 0:
return trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
case 1:
return trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
default:
OSMO_ASSERT(false);
}
}
void Transceiver::driveTxFIFO()
@@ -997,7 +1074,7 @@ void Transceiver::driveTxFIFO()
if (mOn) {
//radioClock->wait(); // wait until clock updates
LOG(DEBUG) << "radio clock " << radioClock->get();
LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
// if underrun, then we're not providing bursts to radio/USRP fast
// enough. Need to increase latency by one GSM frame.
@@ -1006,8 +1083,9 @@ 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 << " (underrun "
<< radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
<< radioClock->get() << " vs "
<< mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
mLatencyUpdateTime = radioClock->get();
}
}
@@ -1017,7 +1095,7 @@ void Transceiver::driveTxFIFO()
if (mTransmitLatency > mRadioInterface->minLatency()) {
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
mTransmitLatency.decTN();
LOG(INFO) << "reduced latency: " << mTransmitLatency;
LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
mLatencyUpdateTime = radioClock->get();
}
}
@@ -1034,35 +1112,42 @@ void Transceiver::driveTxFIFO()
void Transceiver::writeClockInterface()
bool Transceiver::writeClockInterface()
{
int msgLen;
char command[50];
// FIXME -- This should be adaptive.
sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
LOG(INFO) << "ClockInterface: sending " << command;
LOGC(DTRXCLK, INFO) << "sending " << command;
mClockSocket.write(command, strlen(command) + 1);
msgLen = write(mClockSocket, command, strlen(command) + 1);
if (msgLen <= 0) {
LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
return false;
}
mLastClockUpdateTime = mTransmitDeadlineClock;
return true;
}
void *RxUpperLoopAdapter(TransceiverChannel *chan)
void *RxUpperLoopAdapter(TrxChanThParams *params)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
Transceiver *trx = params->trx;
size_t num = params->num;
delete chan;
free(params);
snprintf(thread_name, 16, "RxUpper%zu", num);
set_selfthread_name(thread_name);
trx->setPriority(0.42);
while (1) {
trx->driveReceiveFIFO(num);
if (!trx->driveReceiveFIFO(num)) {
LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
break;
}
pthread_testcancel();
}
return NULL;
@@ -1072,10 +1157,12 @@ void *RxLowerLoopAdapter(Transceiver *transceiver)
{
set_selfthread_name("RxLower");
transceiver->setPriority(0.45);
while (1) {
transceiver->driveReceiveRadio();
if (!transceiver->driveReceiveRadio()) {
LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
break;
}
pthread_testcancel();
}
return NULL;
@@ -1085,8 +1172,6 @@ void *TxLowerLoopAdapter(Transceiver *transceiver)
{
set_selfthread_name("TxLower");
transceiver->setPriority(0.44);
while (1) {
transceiver->driveTxFIFO();
pthread_testcancel();
@@ -1094,39 +1179,45 @@ void *TxLowerLoopAdapter(Transceiver *transceiver)
return NULL;
}
void *ControlServiceLoopAdapter(TransceiverChannel *chan)
void *ControlServiceLoopAdapter(TrxChanThParams *params)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
Transceiver *trx = params->trx;
size_t num = params->num;
delete chan;
free(params);
snprintf(thread_name, 16, "CtrlService%zu", num);
set_selfthread_name(thread_name);
while (1) {
trx->driveControl(num);
if (!trx->driveControl(num)) {
LOGCHAN(num, DTRXCTRL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
break;
}
pthread_testcancel();
}
return NULL;
}
void *TxUpperLoopAdapter(TransceiverChannel *chan)
void *TxUpperLoopAdapter(TrxChanThParams *params)
{
char thread_name[16];
Transceiver *trx = chan->trx;
size_t num = chan->num;
Transceiver *trx = params->trx;
size_t num = params->num;
delete chan;
free(params);
snprintf(thread_name, 16, "TxUpper%zu", num);
set_selfthread_name(thread_name);
trx->setPriority(0.40);
while (1) {
trx->driveTxPriorityQueue(num);
if (!trx->driveTxPriorityQueue(num)) {
LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
break;
}
pthread_testcancel();
}
return NULL;

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: GPL-3.0+
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
@@ -25,7 +27,6 @@
#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
#include "Sockets.h"
#include <sys/types.h>
#include <sys/socket.h>
@@ -38,19 +39,9 @@ extern "C" {
class Transceiver;
/** Channel descriptor for transceiver object and channel number pair */
struct TransceiverChannel {
TransceiverChannel(Transceiver *trx, int num)
{
this->trx = trx;
this->num = num;
}
~TransceiverChannel()
{
}
Transceiver *trx;
size_t num;
struct TrxChanThParams {
Transceiver *trx;
size_t num;
};
/** Internal transceiver state variables */
@@ -108,7 +99,7 @@ public:
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset);
double wRssiOffset, int stackSize);
/** Destructor */
~Transceiver();
@@ -130,8 +121,6 @@ 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
@@ -157,9 +146,9 @@ private:
std::string mLocalAddr;
std::string mRemoteAddr;
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
std::vector<int> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<int> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
int mClockSocket; ///< socket for writing clock updates to GSM core
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
@@ -180,8 +169,7 @@ private:
double rxFullScale; ///< full scale output to radio
double rssiOffset; ///< RSSI to dBm conversion offset
osmo_signal_cbfn *sig_cbfn; ///< Registered Signal Handler to announce events.
int stackSize; ///< stack size for threads, 0 = OS default
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
@@ -194,9 +182,7 @@ private:
void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan = 0);
int pullRadioVector(size_t chan, struct trx_ul_burst_ind *ind);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
@@ -205,7 +191,7 @@ private:
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
/** send messages over the clock socket */
void writeClockInterface(void);
bool writeClockInterface(void);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
@@ -223,27 +209,28 @@ private:
unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
std::vector<unsigned> mVersionTRXD; ///< Format version to use for TRXD protocol communication, per channel
std::vector<TransceiverState> mStates;
/** Start and stop I/O threads through the control socket API */
bool start();
void stop();
/** Protect destructor accessable stop call */
/** Protect destructor accessible stop call */
Mutex mLock;
protected:
/** drive lower receive I/O and burst generation */
void driveReceiveRadio();
bool driveReceiveRadio();
/** drive demodulation of GSM bursts */
void driveReceiveFIFO(size_t chan);
bool driveReceiveFIFO(size_t chan);
/** drive transmission of GSM bursts */
void driveTxFIFO();
/** drive handling of control messages from GSM core */
void driveControl(size_t chan);
bool driveControl(size_t chan);
/**
drive modulation and sorting of GSM bursts from GSM core
@@ -251,34 +238,26 @@ protected:
*/
bool driveTxPriorityQueue(size_t chan);
friend void *RxUpperLoopAdapter(TransceiverChannel *);
friend void *TxUpperLoopAdapter(TransceiverChannel *);
friend void *RxLowerLoopAdapter(Transceiver *);
friend void *TxLowerLoopAdapter(Transceiver *);
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
friend void *RxUpperLoopAdapter(TrxChanThParams *params);
friend void *TxUpperLoopAdapter(TrxChanThParams *params);
friend void *RxLowerLoopAdapter(Transceiver *transceiver);
friend void *TxLowerLoopAdapter(Transceiver *transceiver);
friend void *ControlServiceLoopAdapter(TrxChanThParams *params);
void reset();
/** set priority on current thread */
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
double rssi, double noise, double toa);
void logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi);
};
void *RxUpperLoopAdapter(TransceiverChannel *);
void *RxUpperLoopAdapter(TrxChanThParams *params);
/** Main drive threads */
void *RxLowerLoopAdapter(Transceiver *);
void *TxLowerLoopAdapter(Transceiver *);
void *RxLowerLoopAdapter(Transceiver *transceiver);
void *TxLowerLoopAdapter(Transceiver *transceiver);
/** control message handler thread loop */
void *ControlServiceLoopAdapter(TransceiverChannel *);
void *ControlServiceLoopAdapter(TrxChanThParams *params);
/** transmit queueing thread loop */
void *TxUpperLoopAdapter(TransceiverChannel *);
void *TxUpperLoopAdapter(TrxChanThParams *params);

View File

@@ -2,6 +2,8 @@
* NEON type conversions
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,6 +2,8 @@
* NEON type conversions
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,6 +2,8 @@
* NEON Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -56,7 +58,7 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
}
#endif
/* API: Initalize convolve module */
/* API: Initialize convolve module */
void convolve_init(void)
{
/* Stub */

View File

@@ -2,6 +2,8 @@
* NEON Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,6 +2,8 @@
* NEON scaling
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -1,7 +1,9 @@
/*
* NEON complex multiplication
* NEON complex multiplication
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,6 +2,8 @@
* NEON scaling
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,6 +2,8 @@
* ARM NEON Scaling
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,6 +2,8 @@
* Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -31,4 +33,3 @@ void base_convert_short_float(float *out, const short *in, int len)
for (int i = 0; i < len; i++)
out[i] = in[i];
}

View File

@@ -2,6 +2,8 @@
* Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -1,18 +1,20 @@
/*
* Fast Fourier transform
* Fast Fourier transform
*
* Copyright (C) 2012 Tom Tsou <tom@tsou.cc>
*
*
* SPDX-License-Identifier: LGPL-2.1+
*
* 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.
@@ -32,9 +34,9 @@ struct fft_hdl {
fftwf_plan fft_plan;
};
/*! \brief Initialize FFT backend
/*! \brief Initialize FFT backend
* \param[in] reverse FFT direction
* \param[in] m FFT length
* \param[in] m FFT length
* \param[in] istride input stride count
* \param[in] ostride output stride count
* \param[in] in input buffer (FFTW aligned)
@@ -92,7 +94,7 @@ void fft_free(void *ptr)
free(ptr);
}
/*! \brief Free FFT backend resources
/*! \brief Free FFT backend resources
*/
void free_fft(struct fft_hdl *hdl)
{
@@ -101,7 +103,7 @@ void free_fft(struct fft_hdl *hdl)
}
/*! \brief Run multiple DFT operations with the initialized plan
* \param[in] hdl handle to an intitialized fft struct
* \param[in] hdl handle to an initialized fft struct
*
* Input and output buffers are configured with init_fft().
*/

View File

@@ -27,7 +27,7 @@
#include "config.h"
#endif
/* Architecture dependant function pointers */
/* Architecture dependent function pointers */
struct convert_cpu_context {
void (*convert_si16_ps_16n) (float *, const short *, int);
void (*convert_si16_ps) (float *, const short *, int);

View File

@@ -27,7 +27,7 @@
#include "config.h"
#endif
/* Architecture dependant function pointers */
/* Architecture dependent function pointers */
struct convolve_cpu_context {
void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
int, int, int);
@@ -66,7 +66,7 @@ int _base_convolve_complex(const float *x, int x_len,
int bounds_check(int x_len, int h_len, int y_len,
int start, int len);
/* API: Initalize convolve module */
/* API: Initialize convolve module */
void convolve_init(void)
{
c.conv_cmplx_4n = (void *)_base_convolve_complex;

View File

@@ -1,7 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -23,6 +23,7 @@
extern "C" {
#include "config_defs.h"
#include "osmo_signal.h"
}
#ifdef HAVE_CONFIG_H
@@ -69,9 +70,6 @@ class RadioDevice {
/** Get the Tx window type */
virtual enum TxWindowType getWindowType()=0;
/** Enable thread priority */
virtual void setPriority(float prio = 0.5) = 0;
/**
Read samples from the radio.
@param buf preallocated buf to contain read result
@@ -79,23 +77,20 @@ class RadioDevice {
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
unsigned *RSSI = 0) = 0;
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0) = 0;
/**
Write samples to the radio.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp, bool isControl = false) = 0;
TIMESTAMP timestamp) = 0;
/** Update the alignment between the read and write timestamps */
virtual bool updateAlignment(TIMESTAMP timestamp)=0;
@@ -133,6 +128,9 @@ class RadioDevice {
/** sets the transmit chan gain, returns the gain setting **/
virtual double setTxGain(double dB, size_t chan = 0) = 0;
/** get transmit gain */
virtual double getTxGain(size_t chan = 0) = 0;
/** return maximum Tx Gain **/
virtual double maxTxGain(void) = 0;
@@ -168,13 +166,25 @@ class RadioDevice {
size_t chans;
double lo_offset;
std::vector<std::string> tx_paths, rx_paths;
std::vector<struct device_counters> m_ctr;
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset,
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, 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_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
tx_paths(tx_paths), rx_paths(rx_paths)
{ }
{
if (iface == MULTI_ARFCN) {
LOGC(DDEV, INFO) << "Multi-ARFCN: "<< chan_num << " logical chans -> 1 physical chans";
chans = 1;
}
m_ctr.resize(chans);
for (size_t i = 0; i < chans; i++) {
memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
m_ctr[i].chan = i;
}
}
bool set_antennas() {
unsigned int i;
@@ -182,9 +192,9 @@ class RadioDevice {
for (i = 0; i < tx_paths.size(); i++) {
if (tx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
LOGCHAN(i, DDEV, DEBUG) << "Configuring Tx antenna " << tx_paths[i];
if (!setTxAntenna(tx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
LOGCHAN(i, DDEV, ALERT) << "Failed configuring Tx antenna " << tx_paths[i];
return false;
}
}
@@ -192,9 +202,9 @@ class RadioDevice {
for (i = 0; i < rx_paths.size(); i++) {
if (rx_paths[i] == "")
continue;
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
LOGCHAN(i, DDEV, DEBUG) << "Configuring Rx antenna " << rx_paths[i];
if (!setRxAntenna(rx_paths[i], i)) {
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
LOGCHAN(i, DDEV, ALERT) << "Failed configuring Rx antenna " << rx_paths[i];
return false;
}
}

View File

@@ -7,6 +7,8 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -152,7 +154,7 @@ std::string smpl_buf::str_status(TIMESTAMP timestamp) const
return ost.str();
}
std::string smpl_buf::str_code(ssize_t code)
std::string smpl_buf::str_code(int code)
{
switch (code) {
case ERROR_TIMESTAMP:
@@ -164,6 +166,8 @@ std::string smpl_buf::str_code(ssize_t code)
case ERROR_OVERFLOW:
return "Sample buffer: Overrun";
default:
return "Sample buffer: Unknown error";
std::stringstream ss;
ss << "Sample buffer: Unknown error " << code;
return ss.str();
}
}

View File

@@ -7,6 +7,8 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -31,7 +33,7 @@
/*
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.
internally or accessible through the static convert calls.
*/
class smpl_buf {
public:
@@ -66,7 +68,7 @@ public:
@param code an error code
@return a formatted error string
*/
static std::string str_code(ssize_t code);
static std::string str_code(int code);
enum err_code {
ERROR_TIMESTAMP = -1,

View File

@@ -1,5 +1,7 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: AGPL-3.0+
*
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
@@ -18,6 +20,10 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <map>
#include "trx_vty.h"
#include "Logger.h"
#include "Threads.h"
#include "LMSDevice.h"
@@ -25,7 +31,10 @@
#include <lime/LimeSuite.h>
extern "C" {
#include "osmo_signal.h"
#include <osmocom/core/utils.h>
}
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -33,32 +42,89 @@
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,
/* Device Name Prefixes as presented by LimeSuite API LMS_GetDeviceInfo(): */
#define LMS_DEV_SDR_USB_PREFIX_NAME "LimeSDR-USB"
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
/* Device parameter descriptor */
struct dev_desc {
/* Does LimeSuite allow switching the clock source for this device?
* LimeSDR-Mini does not have switches but needs soldering to select
* external/internal clock. Any call to LMS_SetClockFreq() will fail.
*/
bool clock_src_switchable;
/* Does LimeSuite allow using REF_INTERNAL for this device?
* LimeNET-Micro does not like selecting internal clock
*/
bool clock_src_int_usable;
/* Device specific maximum tx levels selected by phasenoise measurements, in dB */
double max_tx_gain;
/* Sample rate coef (without having TX/RX samples per symbol into account) */
double rate;
/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
double rate_multiarfcn;
/* Coefficient multiplied by TX sample rate in order to shift Tx time */
double ts_offset_coef;
/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
double ts_offset_coef_multiarfcn;
/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
std::string name_prefix;
};
static const std::map<enum lms_dev_type, struct dev_desc> dev_param_map {
{ LMS_DEV_SDR_USB, { true, true, 73.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
{ LMS_DEV_SDR_MINI, { false, true, 66.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
{ LMS_DEV_NET_MICRO, { true, false, 71.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
{ LMS_DEV_UNKNOWN, { true, true, 73.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
};
static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
{
std::map<enum lms_dev_type, struct dev_desc>::const_iterator it = dev_param_map.begin();
const lms_dev_info_t* device_info = LMS_GetDeviceInfo(m_lms_dev);
while (it != dev_param_map.end())
{
enum lms_dev_type dev_type = it->first;
struct dev_desc desc = it->second;
if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
return dev_type;
}
it++;
}
return LMS_DEV_UNKNOWN;
}
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, 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)
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
m_lms_dev(NULL), started(false), m_dev_type(LMS_DEV_UNKNOWN)
{
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_gains.resize(chans);
tx_gains.resize(chans);
rx_buffers.resize(chans);
/* 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));
}
LMSDevice::~LMSDevice()
@@ -93,7 +159,7 @@ static void lms_log_callback(int lvl, const char *msg)
if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map))
lvl = ARRAY_SIZE(lvl_map)-1;
LOGLV(DLMS, lvl_map[lvl]) << msg;
LOGLV(DDEVDRV, lvl_map[lvl]) << msg;
}
static void print_range(const char* name, lms_range_t *range)
@@ -110,7 +176,7 @@ static void print_range(const char* name, lms_range_t *range)
int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args)
{
unsigned int i, j;
vector<string> filters;
std::vector<string> filters;
filters = comma_delimited_to_vector(args.c_str());
@@ -134,11 +200,11 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
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;
struct dev_desc dev_desc;
LOGC(DDEV, INFO) << "Opening LMS device..";
@@ -175,19 +241,20 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
delete [] info_list;
device_info = LMS_GetDeviceInfo(m_lms_dev);
m_dev_type = parse_dev_type(m_lms_dev);
dev_desc = dev_param_map.at(m_dev_type);
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 reference clock is external, setup must happen _before_ calling LMS_Init */
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)
/* FIXME: Assume an external 10 MHz reference clock. make
external reference frequency configurable */
if (!do_clock_src_freq(REF_EXTERNAL, 10000000.0))
goto out_close;
}
@@ -197,22 +264,13 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
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?*/
/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
if (ref == REF_INTERNAL) {
LOGC(DDEV, INFO) << "Setting Internal clock reference";
/* Internal freq param is not used */
if (!do_clock_src_freq(REF_INTERNAL, 0))
goto out_close;
}
/* enable all used channels */
for (i=0; i<chans; i++) {
@@ -227,16 +285,22 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
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)
if (iface == MULTI_ARFCN)
sr_host = dev_desc.rate_multiarfcn * tx_sps;
else
sr_host = dev_desc.rate * tx_sps;
LOGC(DDEV, INFO) << "Setting sample rate to " << sr_host << " " << tx_sps;
if (LMS_SetSampleRate(m_lms_dev, sr_host, 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 */
if (iface == MULTI_ARFCN)
ts_offset = static_cast<TIMESTAMP>(dev_desc.ts_offset_coef_multiarfcn * sr_host);
else
ts_offset = static_cast<TIMESTAMP>(dev_desc.ts_offset_coef * sr_host);
/* configure antennas */
if (!set_antennas()) {
@@ -244,13 +308,7 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
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;
return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
out_close:
LOGC(DDEV, FATAL) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
@@ -344,6 +402,43 @@ bool LMSDevice::stop()
return true;
}
bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
{
struct dev_desc dev_desc = dev_param_map.at(m_dev_type);
size_t lms_clk_id;
switch (ref) {
case REF_EXTERNAL:
lms_clk_id = LMS_CLOCK_EXTREF;
break;
case REF_INTERNAL:
if (!dev_desc.clock_src_int_usable) {
LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
<< " doesn't support internal reference clock";
return false;
}
/* According to lms using LMS_CLOCK_EXTREF with a
frequency <= 0 is the correct way to set clock to
internal reference */
lms_clk_id = LMS_CLOCK_EXTREF;
freq = -1;
break;
default:
LOGC(DDEV, ERROR) << "Invalid reference type " << get_value_string(clock_ref_names, ref);
return false;
}
if (dev_desc.clock_src_switchable) {
if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
return false;
} else {
LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
<< " doesn't support switching clock source through SW";
}
return true;
}
/* do rx/tx calibration - depends on gain, freq and bw */
bool LMSDevice::do_calib(size_t chan)
{
@@ -385,7 +480,7 @@ bool LMSDevice::do_filters(size_t chan)
double LMSDevice::maxTxGain()
{
return maxTxGainClamp;
return dev_param_map.at(m_dev_type).max_tx_gain;
}
double LMSDevice::minTxGain()
@@ -414,8 +509,9 @@ double LMSDevice::setTxGain(double dB, size_t chan)
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;
else
tx_gains[chan] = dB;
return tx_gains[chan];
}
double LMSDevice::setRxGain(double dB, size_t chan)
@@ -429,8 +525,23 @@ double LMSDevice::setRxGain(double dB, size_t chan)
if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0)
LOGCHAN(chan, DDEV, ERR) << "Error setting RX gain to " << dB << " dB";
else
rx_gains[chan] = dB;
return rx_gains[chan];
}
return dB;
void LMSDevice::log_ant_list(bool dir_tx, size_t chan, std::ostringstream& os)
{
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
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 (i)
os << ", ";
os << "'" << name_list[i] << "'";
}
}
int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan)
@@ -486,7 +597,10 @@ bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan)
idx = get_ant_idx(ant, LMS_CH_RX, chan);
if (idx < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna";
std::ostringstream os;
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna: " << ant;
log_ant_list(LMS_CH_RX, chan, os);
LOGCHAN(chan, DDEV, NOTICE) << "Available Rx Antennas: " << os;
return false;
}
@@ -532,7 +646,10 @@ bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan)
idx = get_ant_idx(ant, LMS_CH_TX, chan);
if (idx < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna";
std::ostringstream os;
LOGCHAN(chan, DDEV, ERROR) << "Invalid Tx Antenna: " << ant;
log_ant_list(LMS_CH_TX, chan, os);
LOGCHAN(chan, DDEV, NOTICE) << "Available Tx Antennas: " << os;
return false;
}
@@ -576,39 +693,53 @@ 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)
/*!
* Issue tracking description of several events: https://github.com/myriadrf/LimeSuite/issues/265
*/
void LMSDevice::update_stream_stats_rx(size_t chan, 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;
bool changed = false;
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];
if (LMS_GetStreamStatus(&m_lms_stream_rx[chan], &status) != 0) {
LOGCHAN(chan, DDEV, ERROR) << "Rx LMS_GetStreamStatus failed";
return;
}
/* FIFO overrun is counted when Rx FIFO is full but new data comes from
the board and oldest samples in FIFO are overwritte. Value count
since the last call to LMS_GetStreamStatus(stream). */
if (status.overrun) {
changed = true;
*overrun = true;
LOGCHAN(chan, DDEV, ERROR) << "Rx Overrun! ("
<< m_ctr[chan].rx_overruns << " -> "
<< status.overrun << ")";
}
m_ctr[chan].rx_overruns += status.overrun;
/* Dropped packets in Rx are counted when gaps in Rx timestamps are
detected (likely because buffer overflow in hardware). Value count
since the last call to LMS_GetStreamStatus(stream). */
if (status.droppedPackets) {
changed = true;
LOGCHAN(chan, DDEV, ERROR) << "Rx Dropped packets by HW! ("
<< m_ctr[chan].rx_dropped_samples << " -> "
<< m_ctr[chan].rx_dropped_samples +
status.droppedPackets
<< ")";
m_ctr[chan].rx_dropped_events++;
}
m_ctr[chan].rx_dropped_samples += status.droppedPackets;
if (changed)
osmo_signal_dispatch(SS_DEVICE, S_DEVICE_COUNTER_CHANGE, &m_ctr[chan]);
}
// NOTE: Assumes sequential reads
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
TIMESTAMP timestamp, bool * underrun)
{
int rc, num_smpls, expect_smpls;
ssize_t avail_smpls;
@@ -640,7 +771,7 @@ int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
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);
update_stream_stats_rx(i, overrun);
thread_enable_cancel(true);
if (num_smpls <= 0) {
LOGCHAN(i, DDEV, ERROR) << "Device receive timed out (" << rc << " vs exp " << len << ").";
@@ -675,8 +806,9 @@ int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
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);
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". "
<< rx_buffers[i]->str_status(timestamp)
<< ", (len=" << len << ")";
return 0;
}
}
@@ -684,23 +816,57 @@ int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
return len;
}
void LMSDevice::update_stream_stats_tx(size_t chan, bool *underrun)
{
lms_stream_status_t status;
bool changed = false;
if (LMS_GetStreamStatus(&m_lms_stream_tx[chan], &status) != 0) {
LOGCHAN(chan, DDEV, ERROR) << "Tx LMS_GetStreamStatus failed";
return;
}
/* FIFO underrun is counted when Tx is running but FIFO is empty for
>100 ms (500ms in older versions). Value count since the last call to
LMS_GetStreamStatus(stream). */
if (status.underrun) {
changed = true;
*underrun = true;
LOGCHAN(chan, DDEV, ERROR) << "Tx Underrun! ("
<< m_ctr[chan].tx_underruns << " -> "
<< status.underrun << ")";
}
m_ctr[chan].tx_underruns += status.underrun;
/* Dropped packets in Tx are counted only when timestamps are enabled
and SDR drops packet because of late timestamp. Value count since the
last call to LMS_GetStreamStatus(stream). */
if (status.droppedPackets) {
changed = true;
LOGCHAN(chan, DDEV, ERROR) << "Tx Dropped packets by HW! ("
<< m_ctr[chan].tx_dropped_samples << " -> "
<< m_ctr[chan].tx_dropped_samples +
status.droppedPackets
<< ")";
m_ctr[chan].tx_dropped_events++;
}
m_ctr[chan].tx_dropped_samples += status.droppedPackets;
if (changed)
osmo_signal_dispatch(SS_DEVICE, S_DEVICE_COUNTER_CHANGE, &m_ctr[chan]);
}
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
bool * underrun, unsigned long long timestamp,
bool isControl)
bool * underrun, unsigned long long timestamp)
{
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;
@@ -712,16 +878,12 @@ int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
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;
}
update_stream_stats_tx(i, underrun);
thread_enable_cancel(true);
if (rc != len) {
LOGCHAN(i, DDEV, ERROR) << "LMS: Device Tx timed out (" << rc << " vs exp " << len << ").";
return -1;
}
}
return rc;

View File

@@ -1,7 +1,10 @@
/*
* 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.
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -38,22 +41,21 @@
* A^2 = 1 */
#define LIMESDR_TX_AMPL 0.707
enum lms_dev_type {
LMS_DEV_SDR_USB, /* LimeSDR-USB */
LMS_DEV_SDR_MINI, /* LimeSDR-Mini */
LMS_DEV_NET_MICRO, /* LimeNet-micro */
LMS_DEV_UNKNOWN,
};
/** 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
@@ -63,19 +65,23 @@ private:
TIMESTAMP ts_initial, ts_offset;
double rxGain;
double maxTxGainClamp;
std::vector<double> tx_gains, rx_gains;
enum lms_dev_type m_dev_type;
bool do_calib(size_t chan);
bool do_filters(size_t chan);
void log_ant_list(bool dir_tx, size_t chan, std::ostringstream& os);
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);
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
bool do_clock_src_freq(enum ReferenceType ref, double freq);
public:
/** Object constructor */
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~LMSDevice();
@@ -89,10 +95,6 @@ public:
/** Stop the LMS */
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) {
}
enum TxWindowType getWindowType() {
return TX_WINDOW_LMS1;
}
@@ -104,24 +106,21 @@ public:
@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);
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);
TIMESTAMP timestamp = 0xffffffff);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
@@ -157,7 +156,7 @@ public:
/** get the current receive gain */
double getRxGain(size_t chan = 0) {
return rxGain;
return rx_gains[chan];
}
/** return maximum Rx Gain **/
@@ -169,6 +168,11 @@ public:
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
/** get transmit gain */
double getTxGain(size_t chan = 0) {
return tx_gains[chan];
}
/** return maximum Tx Gain **/
double maxTxGain(void);

View File

@@ -6,6 +6,8 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -31,11 +33,12 @@
#include "config.h"
#endif
#ifndef USE_UHD_3_11
#ifdef USE_UHD_3_11
#include <uhd/utils/log_add.hpp>
#include <uhd/utils/thread.hpp>
#else
#include <uhd/utils/msg.hpp>
#include <uhd/utils/thread_priority.hpp>
#else
#include <uhd/utils/thread.hpp>
#endif
#define USRP_TX_AMPL 0.3
@@ -118,12 +121,13 @@ static const std::map<dev_key, dev_desc> dev_param_map {
{ std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
{ std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
{ std::make_tuple(OCR01, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "OCR01 4/1 Tx/Rx SPS"} },
{ std::make_tuple(OCR01, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "OCR01 4/4 Tx/Rx SPS"} },
};
void *async_event_loop(uhd_device *dev)
{
set_selfthread_name("UHDAsyncEvent");
dev->setPriority(0.43);
while (1) {
dev->recv_async_msg();
@@ -133,23 +137,52 @@ void *async_event_loop(uhd_device *dev)
return NULL;
}
#ifndef USE_UHD_3_11
#ifdef USE_UHD_3_11
static void uhd_log_handler(const uhd::log::logging_info &info)
{
int level;
switch (info.verbosity)
{
case uhd::log::trace:
case uhd::log::debug:
level = LOGL_DEBUG;
break;
case uhd::log::info:
level = LOGL_INFO;
break;
case uhd::log::warning:
level = LOGL_NOTICE;
break;
case uhd::log::error:
level = LOGL_ERROR;
break;
case uhd::log::fatal:
level = LOGL_FATAL;
break;
default:
level = LOGL_NOTICE;
}
LOGSRC(DDEVDRV, level, info.file.c_str(), info.line) << "[" << info.component << "] " << info.message;
}
#else
/*
Catch and drop underrun 'U' and overrun 'O' messages from stdout
since we already report using the logging facility. Direct
everything else appropriately.
*/
void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
static void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
{
switch (type) {
case uhd::msg::status:
LOGC(DDEV, INFO) << msg;
LOGC(DDEVDRV, INFO) << msg;
break;
case uhd::msg::warning:
LOGC(DDEV, WARNING) << msg;
LOGC(DDEVDRV, NOTICE) << msg;
break;
case uhd::msg::error:
LOGC(DDEV, ERROR) << msg;
LOGC(DDEVDRV, ERROR) << msg;
break;
case uhd::msg::fastpath:
break;
@@ -158,10 +191,10 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
#endif
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
InterfaceType iface, size_t chan_num, 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),
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
tx_gain_min(0.0), tx_gain_max(0.0),
rx_gain_min(0.0), rx_gain_max(0.0),
tx_spp(0), rx_spp(0),
@@ -246,9 +279,6 @@ void uhd_device::set_rates()
double uhd_device::setTxGain(double db, size_t chan)
{
if (iface == MULTI_ARFCN)
chan = 0;
if (chan >= tx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
return 0.0f;
@@ -280,9 +310,6 @@ double uhd_device::setTxGain(double db, size_t chan)
double uhd_device::setRxGain(double db, size_t chan)
{
if (iface == MULTI_ARFCN)
chan = 0;
if (chan >= rx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return 0.0f;
@@ -298,9 +325,6 @@ double uhd_device::setRxGain(double db, size_t chan)
double uhd_device::getRxGain(size_t chan)
{
if (iface == MULTI_ARFCN)
chan = 0;
if (chan >= rx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return 0.0f;
@@ -309,6 +333,16 @@ double uhd_device::getRxGain(size_t chan)
return rx_gains[chan];
}
double uhd_device::getTxGain(size_t chan)
{
if (chan >= tx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return 0.0f;
}
return tx_gains[chan];
}
/*
Parse the UHD device tree and mboard name to find out what device we're
dealing with. We need the window type so that the transceiver knows how to
@@ -336,6 +370,7 @@ bool uhd_device::parse_dev_type()
{ "USRP2", { USRP2, TX_WINDOW_FIXED } },
{ "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
{ "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
{ "OCR01", { OCR01, TX_WINDOW_USRP1 } },
};
// Compare UHD motherboard and device strings */
@@ -377,10 +412,9 @@ static bool uhd_e3xx_version_chk()
void uhd_device::set_channels(bool swap)
{
if (iface == MULTI_ARFCN) {
if (dev_type != B200 && dev_type != B210)
if (dev_type != B200 && dev_type != B210 && dev_type != OCR01)
throw std::invalid_argument("Device does not support MCBTS");
dev_type = B2XX_MCBTS;
chans = 1;
}
if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
@@ -390,6 +424,7 @@ void uhd_device::set_channels(bool swap)
switch (dev_type) {
case B210:
case E3XX:
case OCR01:
if (chans == 1)
subdev_string = swap ? "A:B" : "A:A";
else if (chans == 2)
@@ -417,6 +452,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
{
const char *refstr;
/* Register msg handler. Different APIs depending on UHD version */
#ifdef USE_UHD_3_11
uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
uhd::log::set_log_level(uhd::log::debug);
uhd::log::set_console_level(uhd::log::off);
uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
#else
uhd::msg::register_handler(&uhd_msg_handler);
#endif
// Find UHD devices
uhd::device_addr_t addr(args);
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
@@ -525,7 +570,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
init_gains();
// Print configuration
LOGC(DDEV, INFO) << "\n" << usrp_dev->get_pp_string();
LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
if (iface == MULTI_ARFCN)
return MULTI_ARFCN;
@@ -541,6 +586,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
case E1XX:
case E3XX:
case LIMESDR:
case OCR01:
default:
break;
}
@@ -603,10 +649,6 @@ bool uhd_device::start()
return false;
}
#ifndef USE_UHD_3_11
// Register msg handler
uhd::msg::register_handler(&uhd_msg_handler);
#endif
// Start asynchronous event (underrun check) loop
async_event_thrd = new Thread();
async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
@@ -641,12 +683,6 @@ bool uhd_device::stop()
return true;
}
void uhd_device::setPriority(float prio)
{
uhd::set_thread_priority_safe(prio);
return;
}
int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
{
if (!num_smpls) {
@@ -690,7 +726,7 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
}
int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
TIMESTAMP timestamp, bool *underrun)
{
ssize_t rc;
uhd::time_spec_t ts;
@@ -747,7 +783,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
for (size_t i = 0; i < rx_buffers.size(); i++) {
rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
num_smpls,
metadata.time_spec.to_ticks(rx_rate));
ts.to_ticks(rx_rate));
// Continue on local overrun, exit on other errors
if ((rc < 0)) {
@@ -773,7 +809,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
}
int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
unsigned long long timestamp,bool isControl)
unsigned long long timestamp)
{
uhd::tx_metadata_t metadata;
metadata.has_time_spec = true;
@@ -783,12 +819,6 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
*underrun = false;
// No control packets
if (isControl) {
LOGC(DDEV, ERROR) << "Control packets not supported";
return 0;
}
if (bufs.size() != chans) {
LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
return -1;
@@ -888,15 +918,18 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
std::vector<double> freqs;
uhd::tune_result_t tres;
uhd::tune_request_t treq = select_freq(freq, chan, tx);
std::string str_dir;
if (tx) {
tres = usrp_dev->set_tx_freq(treq, chan);
tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
str_dir = "Tx";
} else {
tres = usrp_dev->set_rx_freq(treq, chan);
rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
str_dir = "Rx";
}
LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
return true;
@@ -916,7 +949,7 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
}
LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
}
return true;

View File

@@ -7,6 +7,8 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -48,6 +50,7 @@ enum uhd_dev_type {
X3XX,
UMTRX,
LIMESDR,
OCR01,
};
/*
@@ -60,7 +63,7 @@ enum uhd_dev_type {
class uhd_device : public RadioDevice {
public:
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chans, double offset,
size_t chan_num, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~uhd_device();
@@ -69,14 +72,13 @@ public:
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);
TIMESTAMP timestamp, bool *underrun);
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp, bool isControl);
TIMESTAMP timestamp);
bool updateAlignment(TIMESTAMP timestamp);
@@ -95,6 +97,7 @@ public:
double minRxGain(void) { return rx_gain_min; }
double setTxGain(double db, size_t chan);
double getTxGain(size_t chan = 0);
double maxTxGain(void) { return tx_gain_max; }
double minTxGain(void) { return tx_gain_min; }

View File

@@ -3,6 +3,12 @@ include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS)
rev2dir = $(datadir)/usrp/rev2
rev4dir = $(datadir)/usrp/rev4
dist_rev2_DATA = std_inband.rbf
dist_rev4_DATA = std_inband.rbf
noinst_HEADERS = USRPDevice.h
noinst_LTLIBRARIES = libdevice.la

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -59,16 +61,17 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
size_t chans, double lo_offset,
size_t chan_num, 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)
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
{
LOGC(DDEV, INFO) << "creating USRP device...";
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
actualSampleRate = masterClockRate/decimRate;
rxGain = 0;
txGain = 0;
/*
* Undetermined delay b/w ping response timestamp and true
@@ -277,10 +280,11 @@ double USRPDevice::setTxGain(double dB, size_t chan)
if (!m_dbTx->set_gain(dB))
LOGC(DDEV, ERR) << "Error setting TX gain";
else
txGain = dB;
writeLock.unlock();
return dB;
return txGain;
}
@@ -303,10 +307,11 @@ double USRPDevice::setRxGain(double dB, size_t chan)
if (!m_dbRx->set_gain(dB))
LOGC(DDEV, ERR) << "Error setting RX gain";
else
rxGain = dB;
writeLock.unlock();
return dB;
return rxGain;
}
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
@@ -360,7 +365,7 @@ GSM::Time USRPDevice::minLatency() {
// NOTE: Assumes sequential reads
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
TIMESTAMP timestamp, bool *underrun)
{
#ifndef SWLOOPBACK
if (!m_uRx)
@@ -375,7 +380,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
return len;
}
if (underrun) *underrun = false;
*underrun = false;
uint32_t readBuf[2000];
@@ -425,11 +430,13 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
continue;
}
if ((word0 >> 28) & 0x04) {
if (underrun) *underrun = true;
*underrun = true;
LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
}
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
#if 0
/* FIXME: Do something with this ? */
unsigned RSSI = (word0 >> 21) & 0x3f;
#endif
if (!isAligned) continue;
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
@@ -508,9 +515,8 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
#endif
}
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp,
bool isControl)
int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp, bool isControl)
{
writeLock.lock();
@@ -564,6 +570,12 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
#endif
}
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp)
{
return writeSamplesControl(bufs, len, underrun, timestamp, false);
}
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
{
#ifndef SWLOOPBACK
@@ -573,7 +585,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
bool tmpUnderrun;
std::vector<short *> buf(1, data);
if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
if (writeSamplesControl(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
pingTimestamp = timestamp;
return true;
}

View File

@@ -1,7 +1,10 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -77,6 +80,10 @@ private:
unsigned long lastPktTimestamp;
double rxGain;
double txGain;
int writeSamplesControl(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
#ifdef SWLOOPBACK
short loopbackBuffer[1000000];
@@ -91,7 +98,7 @@ private:
public:
/** Object constructor */
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
@@ -104,9 +111,6 @@ private:
/** Stop the USRP */
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) { }
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
/**
@@ -116,23 +120,20 @@ private:
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
unsigned *RSSI = NULL);
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL);
/**
Write samples to the USRP.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
TIMESTAMP timestamp = 0xffffffff);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
@@ -170,6 +171,9 @@ private:
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
/** get transmit gain */
double getTxGain(size_t chan = 0) { return txGain; }
/** return maximum Tx Gain **/
double maxTxGain(void);

View File

@@ -1,314 +0,0 @@
This file specifies the format of USB packets used for in-band data
transmission and signaling on the USRP. All packets are 512-byte long,
and are transfered using USB "bulk" transfers.
IN packets are sent towards the host.
OUT packets are sent away from the host.
The layout is 32-bits wide. All data is transmitted in little-endian
format across the USB.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|O|U|D|S|E| RSSI | Chan | mbz | Tag | Payload Len |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| Payload |
. .
. .
. .
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... | .
+-+-+-+-+-+-+-+ .
. .
. Padding .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
mbz Must be Zero: these bits must be zero in both IN and OUT packets.
O Overrun Flag: set in an IN packet if an overrun condition was
detected. Must be zero in OUT packets. Overrun occurs when
the FPGA has data to transmit to the host and there is no
buffer space available. This generally indicates a problem on
the host. Either it is not keeping up, or it has configured
the FPGA to transmit data at a higher rate than the transport
(USB) can support.
U Underrun Flag: set in an IN packet if an underrun condition
was detected. Must be zero in OUT packets. Underrun occurs
when the FPGA runs out of samples, and it's not between
bursts. See the "End of Burst flag" below.
D Dropped Packet Flag: Set in an IN packet if the FPGA
discarded an OUT packet because its timestamp had already
passed.
S Start of Burst Flag: Set in an OUT packet if the data is the
first segment of what is logically a continuous burst of data.
Must be zero in IN packets.
E End of Burst Flag: Set in an OUT packet if the data is the
last segment of what is logically a continuous burst of data.
Must be zero in IN packets. Underruns are not reported
when the FPGA runs out of samples between bursts.
RSSI 6-bit Received Strength Signal Indicator: Must be zero in OUT
packets. In IN packets, indicates RSSI as reported by front end.
FIXME The format and interpretation are to be determined.
Chan 5-bit logical channel number. Channel number 0x1f is reserved
for control information. See "Control Channel" below. Other
channels are "data channels." Each data channel is logically
independent of the others. A data channel payload field
contains a sequence of homogeneous samples. The format of the
samples is determined by the configuration associated with the
given channel. It is often the case that the payload field
contains 32-bit complex samples, each containing 16-bit real
and imaginary components.
Tag 4-bit tag for matching IN packets with OUT packets.
[FIXME, write more...]
Payload Len: 9-bit field that specifies the length of the payload
field in bytes. Must be in the range 0 to 504 inclusive.
Timestamp: 32-bit timestamp.
On IN packets, the timestamp indicates the time at which the
first sample of the packet was produced by the A/D converter(s)
for that channel. On OUT packets, the timestamp specifies the
time at which the first sample in the packet should go out the
D/A converter(s) for that channel. If a packet reaches the
head of the transmit queue, and the current time is later than
the timestamp, an error is assumed to have occurred and the
packet is discarded. As a special case, the timestamp
0xffffffff is interpreted as "Now".
The time base is a free running 32-bit counter that is
incremented by the A/D sample-clock.
Payload: Variable length field. Length is specified by the
Payload Len field.
Padding: This field is 504 - Payload Len bytes long, and its content
is unspecified. This field pads the packet out to a constant
512 bytes.
"Data Channel" payload format:
-------------------------------
If Chan != 0x1f, the packet is a "data packet" and the payload is a
sequence of homogeneous samples. The format of the samples is
determined by the configuration associated with the given channel.
It is often the case that the payload field contains 32-bit complex
samples, each containing 16-bit real and imaginary components.
"Control Channel" payload format:
---------------------------------
If Chan == 0x1f, the packet is a "control packet". The control channel
payload consists of a sequence of 0 or more sub-packets.
Each sub-packet starts on a 32-bit boundary, and consists of an 8-bit
Opcode field, an 8-bit Length field, Length bytes of arguments, and 0,
1, 2 or 3 bytes of padding to align the tail of the sub-packet to
a 32-bit boundary.
Control channel packets shall be processed at the head of the queue,
and shall observe the timestamp semantics described above.
General sub-packet format:
--------------------------
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+
| Opcode | Length | <length bytes> ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+
Specific sub-packet formats:
----------------------------
RID: 6-bit Request-ID. Copied from request sub-packet into corresponding
reply sub-packet. RID allows the host to match requests and replies.
Reg Number: 10-bit Register Number.
Ping Fixed Length:
Opcode: OP_PING_FIXED
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Ping Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Ping Fixed Length Reply:
Opcode: OP_PING_FIXED_REPLY
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Ping Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Write Register:
Opcode: OP_WRITE_REG
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 6 | mbz | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Write Register Masked:
Opcode: OP_WRITE_REG_MASKED
REG[Num] = (REG[Num] & ~Mask) | (Value & Mask)
That is, only the register bits that correspond to 1's in the
mask are written with the new value.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 10 | mbz | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Mask Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Read Register:
Opcode: OP_READ_REG
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Read Register Reply:
Opcode: OP_READ_REG_REPLY
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 6 | RID | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
I2C Write:
Opcode: OP_I2C_WRITE
I2C Addr: 7-bit I2C address
Data: The bytes to write to the I2C bus
Length: Length of Data + 2
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
I2C Read:
Opcode: OP_I2C_READ
I2C Addr: 7-bit I2C address
Nbytes: Number of bytes to read from I2C bus
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 3 | RID | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nbytes | unspecified padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
I2C Read Reply:
Opcode: OP_I2C_READ_REPLY
I2C Addr: 7-bit I2C address
Data: Length - 2 bytes of data read from I2C bus.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | RID | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SPI Write:
Opcode: OP_SPI_WRITE
Enables: Which SPI enables to assert (mask)
Format: Specifies format of SPI data and Opt Header Bytes
Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format
Data: The bytes to write to the SPI bus
Length: Length of Data + 6
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Enables | Format | Opt Header Bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SPI Read:
Opcode: OP_SPI_READ
Enables: Which SPI enables to assert (mask)
Format: Specifies format of SPI data and Opt Header Bytes
Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format
Nbytes: Number of bytes to read from SPI bus.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 7 | RID | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Enables | Format | Opt Header Bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nbytes | unspecified padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SPI Read Reply:
Opcode: OP_SPI_READ_REPLY
Data: Length - 2 bytes of data read from SPI bus.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | RID | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Delay:
Opcode: OP_DELAY
Ticks: 16-bit unsigned delay count
Delay Ticks clock ticks before executing next operation.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | Ticks |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

View File

@@ -1,6 +1,8 @@
/*
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -59,6 +61,7 @@ extern "C" {
#include "trx_vty.h"
#include "debug.h"
#include "osmo_signal.h"
#include "trx_rate_ctr.h"
}
#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
@@ -122,7 +125,7 @@ static int transc_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
switch (signal) {
case S_TRANSC_STOP_REQUIRED:
case S_MAIN_STOP_REQUIRED:
gshutdown = true;
break;
default:
@@ -144,15 +147,13 @@ int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr,
trx->cfg.remote_addr, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0),
radio, trx->cfg.rssi_offset);
radio, trx->cfg.rssi_offset, trx->cfg.stack_size);
if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc,
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))
@@ -172,11 +173,11 @@ static void sig_handler(int signo)
action like printing */
return;
fprintf(stdout, "signal %d received\n", signo);
fprintf(stderr, "signal %d received\n", signo);
switch (signo) {
case SIGINT:
case SIGTERM:
fprintf(stdout, "shutting down\n");
fprintf(stderr, "shutting down\n");
gshutdown = true;
break;
case SIGABRT:
@@ -256,7 +257,7 @@ static void print_deprecated(char opt)
{
LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
<< " Please use VTY cfg option instead."
<< " All cmd line options are already being overriden by VTY options if set.";
<< " All cmd line options are already being overridden by VTY options if set.";
}
static void handle_options(int argc, char **argv, struct trx_ctx* trx)
@@ -333,14 +334,12 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
break;
case 'r':
print_deprecated(option);
trx->cfg.rtsc_set = true;
trx->cfg.rtsc = atoi(optarg);
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
trx->cfg.filler = FILLER_NORM_RAND;
break;
case 'A':
print_deprecated(option);
trx->cfg.rach_delay_set = true;
trx->cfg.rach_delay = atoi(optarg);
trx->cfg.filler = FILLER_ACCESS_RAND;
break;
@@ -382,6 +381,11 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
}
}
if (argc > optind) {
LOG(ERROR) << "Unsupported positional arguments on command line";
goto bad_config;
}
/* Cmd line option specific validation & setup */
if (trx->cfg.num_chans > TRX_CHAN_MAX) {
@@ -458,7 +462,9 @@ static void print_config(struct trx_ctx *trx)
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 << " Filler Burst Type....... " << get_value_string(filler_names, trx->cfg.filler) << std::endl;
ost << " Filler Burst TSC........ " << trx->cfg.rtsc << std::endl;
ost << " Filler Burst RACH Delay. " << trx->cfg.rach_delay << std::endl;
ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
ost << " Tuning offset........... " << trx->cfg.offset << std::endl;
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl;
@@ -570,16 +576,22 @@ int main(int argc, char *argv[])
#endif
#endif
#ifndef HAVE_ATOMIC_OPS
#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
printf("Built without atomic operation support. Using Mutex, it may affect performance!\n");
#endif
convolve_init();
convert_init();
osmo_init_logging2(tall_trx_ctx, &log_info);
log_enable_multithread();
osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info);
logging_vty_add_cmds();
ctrl_vty_init(tall_trx_ctx);
trx_vty_init(g_trx_ctx);
logging_vty_add_cmds();
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();
@@ -597,7 +609,7 @@ int main(int argc, char *argv[])
if (rc < 0)
exit(1);
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1);
@@ -626,6 +638,9 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
osmo_signal_register_handler(SS_MAIN, transc_sig_cb, NULL);
trx_rate_ctr_init(tall_trx_ctx, g_trx_ctx);
srandom(time(NULL));
if(trx_start(g_trx_ctx) < 0)
@@ -638,5 +653,6 @@ int main(int argc, char *argv[])
osmo_fd_unregister(&signal_ofd);
osmo_fd_close(&signal_ofd);
osmo_signal_unregister_handler(SS_MAIN, transc_sig_cb, NULL);
return 0;
}

117
Transceiver52M/proto_trxd.c Normal file
View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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 "proto_trxd.h"
#include <osmocom/core/bits.h>
static void trxd_fill_common(struct trxd_hdr_common *common, const struct trx_ul_burst_ind *bi, uint8_t version)
{
common->version = version & 0b1111;
common->reserved = 0;
common->tn = bi->tn;
osmo_store32be(bi->fn, &common->fn);
}
static void trxd_fill_v0_specific(struct trxd_hdr_v0_specific *v0, const struct trx_ul_burst_ind *bi)
{
int toa_int;
/* in 1/256 symbols, round to closest integer */
toa_int = (int) (bi->toa * 256.0 + 0.5);
v0->rssi = bi->rssi;
osmo_store16be(toa_int, &v0->toa);
}
static void trxd_fill_v1_specific(struct trxd_hdr_v1_specific *v1, const struct trx_ul_burst_ind *bi)
{
int16_t ci_int_cB;
/* deciBels->centiBels, round to closest integer */
ci_int_cB = (int16_t)((bi->ci * 10) + 0.5);
v1->idle = !!bi->idle;
v1->modulation = (bi->modulation == MODULATION_GMSK) ?
TRXD_MODULATION_GMSK(bi->tss) :
TRXD_MODULATION_8PSK(bi->tss);
v1->tsc = bi->tsc;
osmo_store16be(ci_int_cB, &v1->ci);
}
static void trxd_fill_burst_normalized255(uint8_t* soft_bits, const struct trx_ul_burst_ind *bi)
{
unsigned i;
for (i = 0; i < bi->nbits; i++)
soft_bits[i] = (char) round(bi->rx_burst[i] * 255.0);
}
bool trxd_send_burst_ind_v0(size_t chan, int fd, const struct trx_ul_burst_ind *bi) {
int rc;
/* v0 doesn't support idle frames, they are simply dropped, not sent */
if(bi->idle)
return true;
/* +2: Historically (OpenBTS times), two extra non-used bytes are sent appended to each burst */
char buf[sizeof(struct trxd_hdr_v0) + bi->nbits + 2];
struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)buf;
trxd_fill_common(&pkt->common, bi, 0);
trxd_fill_v0_specific(&pkt->v0, bi);
trxd_fill_burst_normalized255(&pkt->soft_bits[0], bi);
/* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi->nbits] */
pkt->soft_bits[bi->nbits + 1] = '\0';
rc = write(fd, buf, sizeof(struct trxd_hdr_v0) + bi->nbits + 2);
if (rc <= 0) {
CLOGCHAN(chan, DMAIN, LOGL_NOTICE, "mDataSockets write(%d) failed: %d\n", fd, rc);
return false;
}
return true;
}
bool trxd_send_burst_ind_v1(size_t chan, int fd, const struct trx_ul_burst_ind *bi) {
int rc;
size_t buf_len;
buf_len = sizeof(struct trxd_hdr_v1);
if (!bi->idle)
buf_len += bi->nbits;
char buf[buf_len];
struct trxd_hdr_v1* pkt = (struct trxd_hdr_v1*)buf;
trxd_fill_common(&pkt->common, bi, 1);
trxd_fill_v0_specific(&pkt->v0, bi);
trxd_fill_v1_specific(&pkt->v1, bi);
if (!bi->idle)
trxd_fill_burst_normalized255(&pkt->soft_bits[0], bi);
rc = write(fd, buf, buf_len);
if (rc <= 0) {
CLOGCHAN(chan, DMAIN, LOGL_NOTICE, "mDataSockets write(%d) failed: %d\n", fd, rc);
return false;
}
return true;
}

101
Transceiver52M/proto_trxd.h Normal file
View File

@@ -0,0 +1,101 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <math.h>
#include <osmocom/core/endian.h>
#include "debug.h"
#define MAX_RX_BURST_BUF_SIZE 444 /* 444 = EDGE_BURST_NBITS */
enum Modulation {
MODULATION_GMSK,
MODULATION_8PSK,
/* Not supported yet:
MODULATION_AQPSK,
MODULATION_16QAM,
MODULATION_32QAM
*/
};
struct trx_ul_burst_ind {
float rx_burst[MAX_RX_BURST_BUF_SIZE]; /* soft bits normalized 0..1 */
unsigned nbits; // number of symbols per slot in rxBurst, not counting guard periods
uint32_t fn; // TDMA frame number
uint8_t tn; // TDMA time-slot number
double rssi; // in dBFS
double toa; // in symbols
double noise; // noise level in dBFS
bool idle; // true if no valid burst is included
enum Modulation modulation; // modulation type
uint8_t tss; // training sequence set
uint8_t tsc; // training sequence code
float ci; // Carrier-to-Interference ratio, in dB
};
bool trxd_send_burst_ind_v0(size_t chan, int fd, const struct trx_ul_burst_ind *bi);
bool trxd_send_burst_ind_v1(size_t chan, int fd, const struct trx_ul_burst_ind *bi);
/* The latest supported TRXD header format version */
#define TRX_DATA_FORMAT_VER 1
struct trxd_hdr_common {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t tn:3,
reserved:1,
version:4;
#elif OSMO_IS_BIG_ENDIAN
uint8_t version:4,
reserved:1,
tn:3;
#endif
uint32_t fn; /* big endian */
} __attribute__ ((packed));
struct trxd_hdr_v0_specific {
uint8_t rssi;
uint16_t toa; /* big endian */
} __attribute__ ((packed));
struct trxd_hdr_v0 {
struct trxd_hdr_common common;
struct trxd_hdr_v0_specific v0;
uint8_t soft_bits[0];
} __attribute__ ((packed));
/* Downlink burst (BTS->TRX), v0 anf v1 use same format */
struct trxd_hdr_v01_dl {
struct trxd_hdr_common common;
uint8_t tx_att; /* Tx Attentuation */
uint8_t soft_bits[0];
} __attribute__ ((packed));
#define TRXD_MODULATION_GMSK(ts_set) (0b0000 | (ts_set & 0b0011))
#define TRXD_MODULATION_8PSK(ts_set) (0b0100 | (ts_set & 0b0001))
#define TRXD_MODULATION_AQPSK(ts_set) (0b0110 | (ts_set & 0b0001))
#define TRXD_MODULATION_16QAM(ts_set) (0b1000 | (ts_set & 0b0001))
#define TRXD_MODULATION_32QAM(ts_set) (0b1010 | (ts_set & 0b0001))
struct trxd_hdr_v1_specific {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t tsc:3,
modulation:4,
idle:1;
#elif OSMO_IS_BIG_ENDIAN
uint8_t idle:1,
modulation:4,
tsc:3;
#endif
int16_t ci; /* big endian, in centiBels */
} __attribute__ ((packed));
struct trxd_hdr_v1 {
struct trxd_hdr_common common;
struct trxd_hdr_v0_specific v0;
struct trxd_hdr_v1_specific v1;
uint8_t soft_bits[0];
} __attribute__ ((packed));

View File

@@ -5,6 +5,8 @@
*
* Author: Tom Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -96,7 +98,7 @@ const float *RadioBuffer::getReadSegment()
/*
* Output direction
*
* Write a non-segment length of samples to the buffer.
* Write a non-segment length of samples to the buffer.
*/
bool RadioBuffer::write(const float *wr, size_t len)
{

View File

@@ -4,6 +4,8 @@
*
* Copyright 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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

View File

@@ -4,6 +4,8 @@
*
* Copyright 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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

View File

@@ -4,6 +4,8 @@
* Copyright (C) 2008-2014 Free Software Foundation, Inc.
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -22,6 +24,7 @@
#include "radioInterface.h"
#include "Resampler.h"
#include <Logger.h>
#include <Threads.h>
extern "C" {
#include "convert.h"
@@ -30,11 +33,12 @@ extern "C" {
#define CHUNK 625
#define NUMCHUNKS 4
RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
size_t rx_sps, size_t chans,
int wReceiveOffset, GSM::Time wStartTime)
: mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
underrun(false), overrun(false), receiveOffset(wReceiveOffset), mOn(false)
: mDevice(wDevice), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
underrun(false), overrun(false), writeTimestamp(0), readTimestamp(0),
receiveOffset(wReceiveOffset), mOn(false)
{
mClock.set(wStartTime);
}
@@ -90,11 +94,11 @@ void RadioInterface::close()
}
double RadioInterface::fullScaleInputValue(void) {
return mRadio->fullScaleInputValue();
return mDevice->fullScaleInputValue();
}
double RadioInterface::fullScaleOutputValue(void) {
return mRadio->fullScaleOutputValue();
return mDevice->fullScaleOutputValue();
}
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
@@ -109,8 +113,8 @@ int RadioInterface::setPowerAttenuation(int atten, size_t chan)
if (atten < 0.0)
atten = 0.0;
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
rfGain = setTxGain(mDevice->maxTxGain() - (double) atten, chan);
digAtten = (double) atten - mDevice->maxTxGain() + rfGain;
if (digAtten < 1.0)
powerScaling[chan] = 1.0;
@@ -145,12 +149,12 @@ int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
bool RadioInterface::tuneTx(double freq, size_t chan)
{
return mRadio->setTxFreq(freq, chan);
return mDevice->setTxFreq(freq, chan);
}
bool RadioInterface::tuneRx(double freq, size_t chan)
{
return mRadio->setRxFreq(freq, chan);
return mDevice->setRxFreq(freq, chan);
}
/** synchronization thread loop */
@@ -166,7 +170,7 @@ void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
}
void RadioInterface::alignRadio() {
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
mDevice->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
}
bool RadioInterface::start()
@@ -175,12 +179,12 @@ bool RadioInterface::start()
return true;
LOG(INFO) << "Starting radio device";
if (mRadio->requiresRadioAlign())
if (mDevice->requiresRadioAlign())
mAlignRadioServiceLoopThread.start(
(void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
if (!mRadio->start())
if (!mDevice->start())
return false;
for (size_t i = 0; i < mChans; i++) {
@@ -188,11 +192,11 @@ bool RadioInterface::start()
recvBuffer[i]->reset();
}
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
writeTimestamp = mDevice->initialWriteTimestamp();
readTimestamp = mDevice->initialReadTimestamp();
mRadio->updateAlignment(writeTimestamp-10000);
mRadio->updateAlignment(writeTimestamp-10000);
mDevice->updateAlignment(writeTimestamp-10000);
mDevice->updateAlignment(writeTimestamp-10000);
mOn = true;
LOG(INFO) << "Radio started";
@@ -208,7 +212,7 @@ bool RadioInterface::start()
*/
bool RadioInterface::stop()
{
if (!mOn || !mRadio->stop())
if (!mOn || !mDevice->stop())
return false;
mOn = false;
@@ -285,9 +289,9 @@ int RadioInterface::driveReceiveRadio()
bool RadioInterface::isUnderrun()
{
bool retVal = underrun;
underrun = false;
bool retVal;
/* atomically get previous value of "underrun" and set the var to false */
retVal = osmo_trx_sync_fetch_and_and(&underrun, false);
return retVal;
}
@@ -301,12 +305,12 @@ VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
double RadioInterface::setRxGain(double dB, size_t chan)
{
return mRadio->setRxGain(dB, chan);
return mDevice->setRxGain(dB, chan);
}
double RadioInterface::getRxGain(size_t chan)
double RadioInterface::setTxGain(double dB, size_t chan)
{
return mRadio->getRxGain(chan);
return mDevice->setTxGain(dB, chan);
}
/* Receive a timestamped chunk from the device */
@@ -320,7 +324,7 @@ int RadioInterface::pullBuffer()
return -1;
/* Outer buffer access size is fixed */
numRecv = mRadio->readSamples(convertRecvBuffer,
numRecv = mDevice->readSamples(convertRecvBuffer,
segmentLen,
&overrun,
readTimestamp,
@@ -337,7 +341,7 @@ int RadioInterface::pullBuffer()
segmentLen * 2);
}
underrun |= local_underrun;
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
readTimestamp += numRecv;
return 0;
}
@@ -345,6 +349,7 @@ int RadioInterface::pullBuffer()
/* Send timestamped chunk to the device with arbitrary size */
bool RadioInterface::pushBuffer()
{
bool local_underrun;
size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
if (sendBuffer[0]->getAvailSegments() < 1)
@@ -358,10 +363,11 @@ bool RadioInterface::pushBuffer()
}
/* Send the all samples in the send buffer */
numSent = mRadio->writeSamples(convertSendBuffer,
numSent = mDevice->writeSamples(convertSendBuffer,
segmentLen,
&underrun,
&local_underrun,
writeTimestamp);
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
writeTimestamp += numSent;
return true;

View File

@@ -1,7 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -36,7 +36,7 @@ protected:
std::vector<VectorFIFO> mReceiveFIFO; ///< FIFO that holds receive bursts
RadioDevice *mRadio; ///< the USRP object
RadioDevice *mDevice; ///< the USRP object
size_t mSPSTx;
size_t mSPSRx;
@@ -48,7 +48,7 @@ protected:
std::vector<short *> convertRecvBuffer;
std::vector<short *> convertSendBuffer;
std::vector<float> powerScaling;
bool underrun; ///< indicates writes to USRP are too slow
int underrun; ///< indicates writes to USRP are too slow
bool overrun; ///< indicates reads from USRP are too slow
TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
TIMESTAMP readTimestamp; ///< sample timestamp of next packet read from USRP
@@ -79,12 +79,12 @@ public:
bool start();
bool stop();
/** intialization */
/** initialization */
virtual bool init(int type);
virtual void close();
/** constructor */
RadioInterface(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps,
RadioInterface(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps,
size_t chans = 1, int receiveOffset = 3,
GSM::Time wStartTime = GSM::Time(0));
@@ -107,10 +107,7 @@ public:
virtual bool tuneRx(double freq, size_t chan = 0);
/** set receive gain */
double setRxGain(double dB, size_t chan = 0);
/** get receive gain */
double getRxGain(size_t chan = 0);
virtual double setRxGain(double dB, size_t chan = 0);
/** drive transmission of GSM bursts */
void driveTransmitRadio(std::vector<signalVector *> &bursts,
@@ -127,19 +124,19 @@ public:
/** returns the full-scale receive amplitude **/
double fullScaleOutputValue();
/** set thread priority on current thread */
void setPriority(float prio = 0.5) { mRadio->setPriority(prio); }
/** get transport window type of attached device */
enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
enum RadioDevice::TxWindowType getWindowType() { return mDevice->getWindowType(); }
/** Minimum latency that the device can achieve */
GSM::Time minLatency() { return mRadio->minLatency(); }
GSM::Time minLatency() { return mDevice->minLatency(); }
protected:
/** drive synchronization of Tx/Rx of USRP */
void alignRadio();
/** set transmit gain */
virtual double setTxGain(double dB, size_t chan = 0);
friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
};
@@ -152,22 +149,31 @@ private:
int pullBuffer();
public:
RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
~RadioInterfaceResamp();
bool init(int type);
void close();
};
struct freq_cfg_state {
bool set;
double freq_hz;
};
class RadioInterfaceMulti : public RadioInterface {
private:
bool pushBuffer();
int pullBuffer();
bool verify_arfcn_consistency(double freq, size_t chan, bool tx);
virtual double setTxGain(double dB, size_t chan);
signalVector *outerSendBuffer;
signalVector *outerRecvBuffer;
std::vector<signalVector *> history;
std::vector<bool> active;
std::vector<struct freq_cfg_state> rx_freq_state;
std::vector<struct freq_cfg_state> tx_freq_state;
Resampler *dnsampler;
Resampler *upsampler;
@@ -184,5 +190,5 @@ public:
bool tuneTx(double freq, size_t chan);
bool tuneRx(double freq, size_t chan);
double setRxGain(double dB, size_t chan);
virtual double setRxGain(double dB, size_t chan);
};

View File

@@ -5,6 +5,8 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -71,6 +73,8 @@ void RadioInterfaceMulti::close()
powerScaling.resize(0);
history.resize(0);
active.resize(0);
rx_freq_state.resize(0);
tx_freq_state.resize(0);
RadioInterface::close();
}
@@ -146,6 +150,8 @@ bool RadioInterfaceMulti::init(int type)
mReceiveFIFO.resize(mChans);
powerScaling.resize(mChans);
history.resize(mChans);
rx_freq_state.resize(mChans);
tx_freq_state.resize(mChans);
active.resize(MCHANS, false);
inchunk = RESAMP_INRATE * 4;
@@ -236,7 +242,7 @@ int RadioInterfaceMulti::pullBuffer()
return -1;
/* Outer buffer access size is fixed */
num = mRadio->readSamples(convertRecvBuffer,
num = mDevice->readSamples(convertRecvBuffer,
outerRecvBuffer->size(),
&overrun,
readTimestamp,
@@ -249,7 +255,7 @@ int RadioInterfaceMulti::pullBuffer()
convert_short_float((float *) outerRecvBuffer->begin(),
convertRecvBuffer[0], 2 * outerRecvBuffer->size());
underrun |= local_underrun;
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
readTimestamp += num;
channelizer->rotate((float *) outerRecvBuffer->begin(),
@@ -286,7 +292,7 @@ int RadioInterfaceMulti::pullBuffer()
complex *dst = history[lchan]->begin();
float *fsrc = &buf[2 * (cLen - hLen)];
for (i = 0; i < hLen; i++) {
*dst = complex(fdst[0], fdst[1]);
*dst = complex(fsrc[0], fsrc[1]);
fsrc += 2;
dst++;
}
@@ -307,6 +313,7 @@ int RadioInterfaceMulti::pullBuffer()
/* Send a timestamped chunk to the device */
bool RadioInterfaceMulti::pushBuffer()
{
bool local_underrun;
if (sendBuffer[0]->getAvailSegments() <= 0)
return false;
@@ -337,14 +344,15 @@ bool RadioInterfaceMulti::pushBuffer()
(float *) outerSendBuffer->begin(),
1.0 / (float) mChans, 2 * outerSendBuffer->size());
size_t num = mRadio->writeSamples(convertSendBuffer,
size_t num = mDevice->writeSamples(convertSendBuffer,
outerSendBuffer->size(),
&underrun,
&local_underrun,
writeTimestamp);
if (num != outerSendBuffer->size()) {
LOG(ALERT) << "Transmit error " << num;
}
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
writeTimestamp += num;
return true;
@@ -358,48 +366,82 @@ static bool fltcmp(double a, double b)
return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
}
bool RadioInterfaceMulti::verify_arfcn_consistency(double freq, size_t chan, bool tx)
{
double freq_i;
std::string str_dir = tx ? "Tx" : "Rx";
std::vector<struct freq_cfg_state> &v = tx ? tx_freq_state : rx_freq_state;
for (size_t i = 0; i < mChans; i++) {
if (i == chan)
continue;
if (!v[i].set)
continue;
freq_i = v[i].freq_hz + (double) ((int)chan - (int)i) * MCBTS_SPACING;
if (!fltcmp(freq, freq_i)) {
LOGCHAN(chan, DMAIN, ERROR)
<< "Setting " << str_dir << " frequency " << freq
<< " is incompatible: already configured channel "
<< i << " uses frequency " << v[i].freq_hz
<< " (expected " << freq_i << ")";
return false;
}
}
v[chan].set = true;
v[chan].freq_hz = freq;
return true;
}
bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
{
if (chan >= mChans)
return false;
double shift;
double shift = (double) getFreqShift(mChans);
if (chan >= mChans)
return false;
if (!chan)
return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
if (!verify_arfcn_consistency(freq, chan, true))
return false;
double center = mRadio->getTxFreq();
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
<< freq / 1e6 << " MHz";
}
if (chan == 0) {
shift = (double) getFreqShift(mChans);
return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
}
return true;
return true;
}
bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
{
if (chan >= mChans)
return false;
double shift;
double shift = (double) getFreqShift(mChans);
if (chan >= mChans)
return false;
if (!chan)
return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
if (!verify_arfcn_consistency(freq, chan, false))
return false;
double center = mRadio->getRxFreq();
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
<< freq / 1e6 << " MHz";
}
if (chan == 0) {
shift = (double) getFreqShift(mChans);
return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
}
return true;
return true;
}
double RadioInterfaceMulti::setRxGain(double db, size_t chan)
{
if (!chan)
return mRadio->setRxGain(db);
if (chan == 0)
return mDevice->setRxGain(db);
else
return mRadio->getRxGain();
return mDevice->getRxGain();
}
double RadioInterfaceMulti::setTxGain(double dB, size_t chan)
{
if (chan == 0)
return mDevice->setTxGain(dB);
else
return mDevice->getTxGain();
}

View File

@@ -6,6 +6,8 @@
*
* Author: Tom Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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
@@ -57,9 +59,9 @@ static size_t resamp_inchunk = 0;
static size_t resamp_outrate = 0;
static size_t resamp_outchunk = 0;
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wDevice,
size_t tx_sps, size_t rx_sps)
: RadioInterface(wRadio, tx_sps, rx_sps, 1),
: RadioInterface(wDevice, tx_sps, rx_sps, 1),
outerSendBuffer(NULL), outerRecvBuffer(NULL)
{
}
@@ -169,7 +171,7 @@ int RadioInterfaceResamp::pullBuffer()
return -1;
/* Outer buffer access size is fixed */
num_recv = mRadio->readSamples(convertRecvBuffer,
num_recv = mDevice->readSamples(convertRecvBuffer,
resamp_outchunk,
&overrun,
readTimestamp,
@@ -182,7 +184,7 @@ int RadioInterfaceResamp::pullBuffer()
convert_short_float((float *) outerRecvBuffer->begin(),
convertRecvBuffer[0], 2 * resamp_outchunk);
underrun |= local_underrun;
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
readTimestamp += (TIMESTAMP) resamp_outchunk;
/* Write to the end of the inner receive buffer */
@@ -202,6 +204,7 @@ int RadioInterfaceResamp::pullBuffer()
/* Send a timestamped chunk to the device */
bool RadioInterfaceResamp::pushBuffer()
{
bool local_underrun;
int rc;
size_t numSent;
@@ -221,14 +224,15 @@ bool RadioInterfaceResamp::pushBuffer()
(float *) outerSendBuffer->begin(),
powerScaling[0], 2 * resamp_outchunk);
numSent = mRadio->writeSamples(convertSendBuffer,
numSent = mDevice->writeSamples(convertSendBuffer,
resamp_outchunk,
&underrun,
&local_underrun,
writeTimestamp);
if (numSent != resamp_outchunk) {
LOG(ALERT) << "Transmit error " << numSent;
}
osmo_trx_sync_or_and_fetch(&underrun, local_underrun);
writeTimestamp += resamp_outchunk;
return true;

View File

@@ -4,6 +4,8 @@
*
* Copyright 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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

View File

@@ -4,6 +4,8 @@
*
* Copyright 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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

View File

@@ -1,6 +1,8 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -84,7 +86,7 @@ static Resampler *dnsampler = NULL;
* perform 16-byte memory alignment required by many SSE instructions.
*/
struct CorrelationSequence {
CorrelationSequence() : sequence(NULL)
CorrelationSequence() : sequence(NULL), buffer(NULL), toa(0.0)
{
}
@@ -343,7 +345,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
_x = x;
/*
* Four convovle types:
* Four convolve types:
* 1. Complex-Real (aligned)
* 2. Complex-Complex (aligned)
* 3. Complex-Real (!aligned)
@@ -530,19 +532,17 @@ static PulseSequence *generateGSMPulse(int sps)
return pulse;
}
bool vectorSlicer(SoftVector *x)
/* Convert -1..+1 soft bits to 0..1 soft bits */
void vectorSlicer(float *dest, const float *src, size_t len)
{
SoftVector::iterator xP = x->begin();
SoftVector::iterator xPEnd = x->end();
while (xP < xPEnd) {
*xP = 0.5 * (*xP + 1.0f);
if (*xP > 1.0)
*xP = 1.0;
if (*xP < 0.0)
*xP = 0.0;
xP++;
}
return true;
size_t i;
for (i = 0; i < len; i++) {
dest[i] = 0.5 * (src[i] + 1.0f);
if (dest[i] > 1.0)
dest[i] = 1.0;
else if (dest[i] < 0.0)
dest[i] = 0.0;
}
}
static signalVector *rotateBurst(const BitVector &wBurst,
@@ -723,7 +723,7 @@ static signalVector *mapEdgeSymbols(const BitVector &bits)
*
* Delay the EDGE downlink bursts by one symbol in order to match GMSK pulse
* shaping group delay. The difference in group delay arises from the dual
* pulse filter combination of the GMSK Laurent represenation whereas 8-PSK
* pulse filter combination of the GMSK Laurent representation whereas 8-PSK
* uses a single pulse linear filter.
*/
static signalVector *shapeEdgeBurst(const signalVector &symbols)
@@ -1455,6 +1455,33 @@ static signalVector *downsampleBurst(const signalVector &burst)
return out;
};
/*
* Computes C/I (Carrier-to-Interference ratio) in dB (deciBels).
* It is computed from the training sequence of each received burst,
* by comparing the "ideal" training sequence with the actual one.
*/
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
*
@@ -1465,11 +1492,13 @@ static signalVector *downsampleBurst(const signalVector &burst)
*/
static int detectBurst(const signalVector &burst,
signalVector &corr, CorrelationSequence *sync,
float thresh, int sps, complex *amp, float *toa,
int start, int len)
float thresh, int sps, int start, int len,
struct estim_burst_params *ebp)
{
const signalVector *corr_in;
signalVector *dec = NULL;
complex xcorr;
int rc = 1;
if (sps == 4) {
dec = downsampleBurst(burst);
@@ -1482,35 +1511,42 @@ static int detectBurst(const signalVector &burst,
/* Correlate */
if (!convolve(corr_in, sync->sequence, &corr,
CUSTOM, start, len)) {
delete dec;
return -1;
rc = -1;
goto del_ret;
}
delete dec;
/* Running at the downsampled rate at this point */
sps = 1;
/* Peak detection - place restrictions at correlation edges */
*amp = fastPeakDetect(corr, toa);
ebp->amp = fastPeakDetect(corr, &ebp->toa);
if ((*toa < 3 * sps) || (*toa > len - 3 * sps))
return 0;
if ((ebp->toa < 3 * sps) || (ebp->toa > len - 3 * sps)) {
rc = 0;
goto del_ret;
}
/* Peak -to-average ratio */
if (computePeakRatio(&corr, sps, *toa, *amp) < thresh)
return 0;
/* Peak-to-average ratio */
if (computePeakRatio(&corr, sps, ebp->toa, ebp->amp) < thresh) {
rc = 0;
goto del_ret;
}
/* 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, &ebp->toa, NULL);
/* Compute C/I */
ebp->ci = computeCI(corr_in, sync, ebp->toa, start, xcorr);
/* Normalize our channel gain */
*amp = *amp / sync->gain;
ebp->amp = xcorr / sync->gain;
/* Compensate for residuate time lag */
*toa = *toa - sync->toa;
ebp->toa = ebp->toa - sync->toa;
return 1;
del_ret:
delete dec;
return rc;
}
static float maxAmplitude(const signalVector &burst)
@@ -1534,13 +1570,10 @@ static float maxAmplitude(const signalVector &burst)
* head: Search symbols before target
* tail: Search symbols after target
*/
static int detectGeneralBurst(const signalVector &rxBurst,
float thresh,
int sps,
complex &amp,
float &toa,
static int detectGeneralBurst(const signalVector &rxBurst, float thresh, int sps,
int target, int head, int tail,
CorrelationSequence *sync)
CorrelationSequence *sync,
struct estim_burst_params *ebp)
{
int rc, start, len;
bool clipping = false;
@@ -1562,17 +1595,18 @@ static int detectGeneralBurst(const signalVector &rxBurst,
signalVector corr(len);
rc = detectBurst(rxBurst, corr, sync,
thresh, sps, &amp, &toa, start, len);
thresh, sps, start, len, ebp);
if (rc < 0) {
return -SIGERR_INTERNAL;
} else if (!rc) {
amp = 0.0f;
toa = 0.0f;
ebp->amp = 0.0f;
ebp->toa = 0.0f;
ebp->ci = 0.0f;
return clipping?-SIGERR_CLIP:SIGERR_NONE;
}
/* Subtract forward search bits from delay */
toa -= head;
ebp->toa -= head;
return 1;
}
@@ -1587,7 +1621,7 @@ 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, bool ext)
unsigned max_toa, bool ext, struct estim_burst_params *ebp)
{
int rc, target, head, tail;
int i, num_seq;
@@ -1598,10 +1632,12 @@ static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
num_seq = ext ? 3 : 1;
for (i = 0; i < num_seq; i++) {
rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
target, head, tail, gRACHSequences[i]);
if (rc > 0)
rc = detectGeneralBurst(burst, threshold, sps, target, head, tail,
gRACHSequences[i], ebp);
if (rc > 0) {
ebp->tsc = i;
break;
}
}
return rc;
@@ -1616,7 +1652,7 @@ static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
* tail: Search 6 symbols + maximum expected delay
*/
static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold,
int sps, complex &amplitude, float &toa, unsigned max_toa)
int sps, unsigned max_toa, struct estim_burst_params *ebp)
{
int rc, target, head, tail;
CorrelationSequence *sync;
@@ -1629,13 +1665,13 @@ static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float th
tail = 6 + max_toa;
sync = gMidambles[tsc];
rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
target, head, tail, sync);
ebp->tsc = tsc;
rc = detectGeneralBurst(burst, threshold, sps, target, head, tail, sync, ebp);
return rc;
}
static int detectEdgeBurst(const signalVector &burst, unsigned tsc, float threshold,
int sps, complex &amplitude, float &toa, unsigned max_toa)
int sps, unsigned max_toa, struct estim_burst_params *ebp)
{
int rc, target, head, tail;
CorrelationSequence *sync;
@@ -1648,33 +1684,31 @@ static int detectEdgeBurst(const signalVector &burst, unsigned tsc, float thresh
tail = 6 + max_toa;
sync = gEdgeMidambles[tsc];
rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa,
target, head, tail, sync);
ebp->tsc = tsc;
rc = detectGeneralBurst(burst, threshold, sps,
target, head, tail, sync, ebp);
return rc;
}
int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
int sps, CorrType type, complex &amp, float &toa,
unsigned max_toa)
int sps, CorrType type, unsigned max_toa,
struct estim_burst_params *ebp)
{
int rc = 0;
switch (type) {
case EDGE:
rc = detectEdgeBurst(burst, tsc, threshold, sps,
amp, toa, max_toa);
rc = detectEdgeBurst(burst, tsc, threshold, sps, max_toa, ebp);
if (rc > 0)
break;
else
type = TSC;
case TSC:
rc = analyzeTrafficBurst(burst, tsc, threshold, sps,
amp, toa, max_toa);
rc = analyzeTrafficBurst(burst, tsc, threshold, sps, max_toa, ebp);
break;
case EXT_RACH:
case RACH:
rc = detectRACHBurst(burst, threshold, sps, amp, toa,
max_toa, type == EXT_RACH);
rc = detectRACHBurst(burst, threshold, sps, max_toa, type == EXT_RACH, ebp);
break;
default:
LOG(ERR) << "Invalid correlation type";

View File

@@ -1,7 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -59,7 +59,7 @@ bool sigProcLibSetup();
void sigProcLibDestroy(void);
/** Operate soft slicer on a soft-bit vector */
bool vectorSlicer(SoftVector *x);
void vectorSlicer(float *dest, const float *src, size_t len);
/** GMSK modulate a GSM burst of bits */
signalVector *modulateBurst(const BitVector &wBurst,
@@ -101,15 +101,26 @@ void scaleVector(signalVector &x,
*/
float energyDetect(const signalVector &rxBurst,
unsigned windowLength);
/** Struct used to fill out parameters in detectAnyBurst(): estimated burst parameters
@param amplitude The estimated amplitude of received TSC burst.
@param toa The estimated time-of-arrival of received TSC burst (in symbols).
@param tsc The TSC used to detect the burst.
*/
struct estim_burst_params {
complex amp;
float toa;
uint8_t tsc;
float ci;
};
/**
8-PSK/GMSK/RACH burst detector
@param burst The received GSM burst of interest
@param tsc Midamble type (0..7) also known as TSC
@param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received TSC burst.
@param toa The estimate time-of-arrival of received TSC burst (in symbols).
@param max_toa The maximum expected time-of-arrival (in symbols).
@param ebp The estimated parameters of the detected burst.
@return positive value (CorrType) if threshold value is reached,
negative value (-SignalError) on error,
zero (SIGERR_NONE) if no burst is detected
@@ -119,9 +130,8 @@ int detectAnyBurst(const signalVector &burst,
float threshold,
int sps,
CorrType type,
complex &amp,
float &toa,
unsigned max_toa);
unsigned max_toa,
struct estim_burst_params *ebp);
/** Demodulate burst basde on type and output soft bits */
SoftVector *demodAnyBurst(const signalVector &burst, int sps,

View File

@@ -47,7 +47,7 @@ void signalVector::operator=(const signalVector& vector)
complex *src = vector.mData;
for (i = 0; i < size(); i++, src++, dst++)
*dst = *src;
/* TODO: optimize for non non-trivially copyable types: */
/* TODO: optimize for non non-trivially copiable types: */
/*memcpy(mData, vector.mData, bytes()); */
mStart = mData + vector.getStart();
}
@@ -70,7 +70,7 @@ size_t signalVector::updateHistory()
complex *src = mStart + this->size() - num;
for (i = 0; i < num; i++, src++, dst++)
*dst = *src;
/* TODO: optimize for non non-trivially copyable types: */
/* TODO: optimize for non non-trivially copiable types: */
/*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */
return num;

1578
autogen.sh

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,7 @@ AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([subdir-objects])
AM_INIT_AUTOMAKE([foreign subdir-objects])
dnl Linux kernel KBuild style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -65,7 +65,7 @@ AC_PROG_LIBTOOL
dnl Checks for header files.
AC_HEADER_STDC
dnl This is required for GnuRadio includes to understand endianess correctly:
dnl This is required for GnuRadio includes to understand endianness correctly:
AC_CHECK_HEADERS([byteswap.h])
dnl Checks for typedefs, structures, and compiler characteristics.
@@ -75,9 +75,18 @@ AC_TYPE_SIZE_T
AC_HEADER_TIME
AC_C_BIGENDIAN
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
# Check if gettid is available (despite not being documented in glibc doc, it requires __USE_GNU on some systems)
# C compiler is used since __USE_GNU seems to be always defined for g++.
save_CPPFLAGS=$CPPFLAGS
AC_LANG_PUSH(C)
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
AC_CHECK_FUNCS([gettid])
AC_LANG_POP(C)
CPPFLAGS=$save_CPPFLAGS
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.3.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -190,29 +199,39 @@ dnl Check if the compiler supports specified GCC's built-in function
AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
AC_CACHE_CHECK(
[whether ${CC} has $1 built-in],
[osmo_cv_cc_has_builtin], [
[osmo_cv_cc_has_$1], [
AC_LINK_IFELSE([
AC_LANG_PROGRAM([], [
__builtin_cpu_supports("sse");
$2
])
],
[AS_VAR_SET([osmo_cv_cc_has_builtin], [yes])],
[AS_VAR_SET([osmo_cv_cc_has_builtin], [no])])
[AS_VAR_SET([osmo_cv_cc_has_$1], [yes])],
[AS_VAR_SET([osmo_cv_cc_has_$1], [no])])
]
)
AS_IF([test yes = AS_VAR_GET([osmo_cv_cc_has_builtin])], [
AS_IF([test yes = AS_VAR_GET([osmo_cv_cc_has_$1])], [
AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
[Define to 1 if compiler has the '$1' built-in function])
], [
AC_MSG_WARN($2)
AC_MSG_WARN($3)
])
])
dnl Check if the compiler supports runtime SIMD detection
CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports], [__builtin_cpu_supports("sse");],
[Runtime SIMD detection will be disabled])
dnl Check for __sync_fetch_and_add().
CHECK_BUILTIN_SUPPORT([__sync_fetch_and_and], [int x;__sync_fetch_and_and(&x,1);],
[Atomic operation not available, will use mutex])
dnl Check for __sync_or_and_fetch().
CHECK_BUILTIN_SUPPORT([__sync_or_and_fetch], [int x;__sync_or_and_fetch(&x,1);],
[Atomic operation not available, will use mutex])
AS_IF([test "x$osmo_cv_cc_has___sync_fetch_and_and" = "xyes" && test "x$osmo_cv_cc_has___sync_or_and_fetch" = "xyes"], [
AC_DEFINE(HAVE_ATOMIC_OPS, 1, [Support all required atomic operations], [AC_MSG_WARN("At least one aotmic operation missing, will use mutex")])
])
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"])

View File

@@ -15,7 +15,7 @@ substr() { [ -z "${2##*$1*}" ]; }
mychroot_nocwd() {
# LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container
# PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH.
# PROOT_NO_SECCOMP is requried due to proot bug #106
# PROOT_NO_SECCOMP is required due to proot bug #106
LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@"
}
@@ -23,15 +23,8 @@ mychroot() {
mychroot_nocwd -w / "$@"
}
base="$PWD"
deps="$base/deps"
inst="$deps/install"
export deps inst
if [ -z "${INSIDE_CHROOT}" ]; then
osmo-clean-workspace.sh
# Only use ARM chroot if host is not ARM and the target is ARM:
if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
@@ -69,10 +62,24 @@ if [ -z "${INSIDE_CHROOT}" ]; then
fi
fi
set -ex
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
exit 2
fi
base="$PWD"
deps="$base/deps"
inst="$deps/install"
export deps inst
osmo-clean-workspace.sh
mkdir "$deps" || true
osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc"
osmo-build-dep.sh libusrp
PARALLEL_MAKE="" osmo-build-dep.sh libusrp
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
@@ -107,4 +114,5 @@ if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
$MAKE maintainer-clean
osmo-clean-workspace.sh

184
debian/changelog vendored
View File

@@ -1,3 +1,187 @@
osmo-trx (1.2.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* osmo-trx: log to stderr on signal received
* Drop old setPriority related code
* Transceiver: fix segfault during init if IP addr binding fails
* Transceiver: Check return value when binding IP addr for clock socket
* Transceiver: Clean up receival of downlink bursts
* Transceiver: Fix idle ul burst indications being dropped
* Transceiver: exit process when BTS drops connection
* Transceiver: Enable EDGE detection only on PDCH timeslots
* lms: Log available antennas if requested antenna fails
* device: Use LOGCHAN in set_antennas()
* Transceiver: Fix logging TN and version
* Transceiver: Use LOGCHAN in logRxBurst to unify log format
* Transceiver: Log error condition no burst in pullRadioVector()
* Transceiver: pullRadioVector(): Fix use of uninitialized value bi->tn
* Transceiver: Don't stop TRX if pulling from OFF timeslot
* radioInterface: Rename mRadio to mDevice
* radioInterfaceMulti: Check equals zero explicitly
* USRPDevice: Fix setRxGain return on error and getRxGain() returning always 0
* USRPDevice: Return previous txGain if setting value failed
* LMSDevice: Return previous txGain/rxGain if setting value failed
* radioInterface: Remove unusued getRxGain()
* radioDevice: Introduce getTxGain() API
* radioInterfaceMulti: Override setTxGain() to avoid chan!=0 calls
* UHDDevice: Drop unneeded MULTI_ARFCN checks
* radioInterface{Multi,Resamp}: Fix successful writeSamples() masking underrun from readSamples()
* radioInterface: Mark setRxGain as virtual
* Move multi-ARFCN chan amount modification from UHDDevice to parent class
* radioInterface: Atomically fetch and change underrun variable
* radioInterfaceMulti: write frequency offset direction (rx/tx) in log line
* Use new libosmocore logging lock API
* Transceiver: Fix wrong response upon CMD HANDOVER failure
* uhd: use value already cached in tmp variable
* Transceiver.cpp: Introduce and use new logging categories
[ Timo Jacobus ]
* Transceiver: Fixed copying of history into and from channelizer buffer.
[ Alexander Chemeris ]
* vty: Don't enable random filler bursts automatically with EDGE.
* vty: Simplify filler burst settings and improve help and readability.
[ Martin Hauke ]
* Fix common misspellings and typos
[ Harald Welte ]
* trx: exit() on unsupported positional arguments on command line
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 19:54:00 +0100
osmo-trx (1.1.1) unstable; urgency=medium
* UNRELEASED
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 08 Aug 2019 13:00:05 +0200
osmo-trx (1.1.0) unstable; urgency=medium
[ Sylvain Munaut ]
* arm/convolve: Fix the vfp4 real convolution for h_len=12
* convolve: Remove support for step, offset parameters
* tests: Rework the convolve_test
* tests: Re-enable the convolve_test by default
* sigProcLib: Add C/I (Carrier-to-Interference ratio) computation
[ Vadim Yanitskiy ]
* VTY: add extended (11-bit) RACH detection toggle
* doc/configuration.adoc: fix incorrect number of physical RF channels for B210
* driveTxPriorityQueue(): cosmetic: use proper type for TDMA TN
* driveTxPriorityQueue(): use trxd_hdr_common for message parsing
* driveTxPriorityQueue(): check if message header format is supported
* driveTxPriorityQueue(): enrich logging message
* trxd_fill_common(): fix TRXD header version coding
* manuals/configuration.adoc: fix copy-paste error in config example
[ Pau Espin Pedrol ]
* osmo-trx: Avoid handling signals after shutdown triggered
* osmo-trx: Use signalfd to serialize signals in main thread ctx
* osmo-trx: Check return code of osmo_fd_register
* lms: flush_recv: alloc buf on stack instead of heap
* lms: Improve log during flush recv error
* cosmetic: Threads.h: Remove trailing whitespace
* Move duplicated thread_enable_cancel to CommonLibs
* lms: Log underrun/overrun events
* lms: Remove references to ALERT loglevel
* lms: Remove unused var m_last_tx_overruns
* lms: Catch and log dropped packets by HW during recv
* cosmetic: uhd: Move smpl_buf out of UHDDevice, move UHDDevice class definition to .h
* uhd: smpl_buf: Drop UHD specifics out back to UHDDevice
* uhd: smpl_buf: Use TIMESTAMP type in str_status
* cosmetic: uhd: Use loglevel ERROR instead of ERR
* uhd: Avoid reallocation of buffers every read
* Move smpl_buf out of uhd dir to re-use it in other devices
* device: Drop unused numberRead/numberWritten APIs
* smpl_buf: Remove unused clk_rt variable
* smpl_buf: Remove unused clk_rt variable (fixup)
* smpl_buf: Remove dbg log line with duplicated info
* Introduce LOGCHAN macro to standarize logging channel info
* smpl_buf: Move it to device/common and create libdevice_common.la
* lms: Use smpl_buf to recover from timestamp jumps
* lms: Fix stream_stats checks with droppedPackets
* Add rate_ctr support to store/retrieve SDR errors through VTY
* Rename and move STOP signal from Transceiver to main
* doc: vty: Update trx_vty_reference.xml
* lms: Drop unusued variable masterClockRate
* lms: Fix stream_stats checks with overrun/underrun
* Add VTY commands to set error ctr thresholds
* Remove AUTHORS file
* trx_{vty,rate_ctr}: Set proper license AGPLv3+
* Introduce structs to encode TRXD packets
* Transceiver: refactor: gather uplink burst parameters in struct
* Transceiver: Drop unused rssi_valid struct field
* Transceiver: Move nbits burst size calculation to pullRadioVector()
* Transceiver: Move calculation of normalized values (rssiOffset) to pullRadioVector()
* Transceiver: Move soft bits normalization to pullRadioVector()
* Transceiver: Drop use of GSM::Time from trx_ul_burst_ind
* Transceiver: Get rid of SoftVector in struct trx_ul_burst_ind
* Transceiver: replace UDPSocket with libosmocore socket API
* Transceiver: Avoid noise calculation formula in 2 branches in pullRadioVector
* Transceiver: Simplify code on early error return when calling detectAnyBurst
* Transceiver: pullRadioVector(): Move initialization of var to start of function
* Transceiver: Support pulling idle frames in pullRadioVector()
* sigProcLib: detectAnyBurst() family: Use struct to gather all out params
* sigProcLib: detectAnyBurst(): make TSC used to detect burst available to caller
* Logger: global Log mutex is now available from C code
* Transceiver: Move out TRXD socket send code to prepare for TRXDv1
* Transceiver: Support SETFORMAT command
* Transceiver: Support TRXD v1
* Transceiver: Initialize mExtRACH in constructor
* debian/copyright: Remove non existent radioInterfaceDiversity.cpp from list
* debian/copyright: Update wrong paths in license list
* debian/copyright: Add missing file Utils.* to LGPL-2.1+ list
* cosmetic: Fix trailing whitespace in several files
* Add SPDX annotation
* Bind CTRL port to IP addr specified in VTY config
* Transceiver: Store TRXD version per channel
* Transceiver: Clean up code passing parameters to threads
* Remove empty ChangeLog fnd NEWS files
* Remove unused autogen.sh
* radioInterface: Clarify how underruns are handled driving a radioDevice
* usrp1: don't check for non-null underrun pointer
* jenkins.sh: Workaround libusrp build race conditon
* lms: Drop rx_underruns rate ctr, add tx_drop_* rate ctr
* Move inband-signaling-usb documentation to UserManual
* Move matlab files under utils/matlab
* Move Transceiver52/README to UserManual
* Move README.DFEsymbolspaced to utils/matlab
* Move std_inband.rbf under device/usrp1/ dir
* Drop old README information, provide new updated README
* Transceiver: Add missing include netinet/in.h
* Require newer version of libosmocore to avoid build failure
[ Tom Tsou ]
* multi-ARFCN: fix maximum number of carriers limitation
[ Harald Welte ]
* use BSC_FD_READ and not OSMO_FD_READ
* proto_trxd.c: Use bit-wise AND, not boolean AND
* Timeval: Restore output stream flags after changing them
* ChannelizerBase: fix initialization of class members
* CorrelationSequence: fix initialization of class members
* radioInterface.cpp: Fix missing member initialization of RadioInterface()
[ Joachim Steiger ]
* lms: Remove wrong unused code copied from -uhd
* lms: move LMS_EnableChannel from Start/Stop to Open/Close device
* lms: move LMS_GetLPFBWRange and LMS_Calibrate calls from open to start
* lms: add device type detection and device specific gains
* lms: properly call close if set_antennas() fails, add some comments
[ Oliver Smith ]
* debian: create -doc subpackage with pdf manuals
* contrib/jenkins.sh: run "make maintainer-clean"
[ Eric Wild ]
* Add option to set stack size in config file, default == 0 == OS default
[ Ruben Undheim ]
* Fix spelling discovered by lintian
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 07 Aug 2019 21:12:56 +0200
osmo-trx (1.0.0) unstable; urgency=medium
[ Pau Espin Pedrol ]

12
debian/control vendored
View File

@@ -14,7 +14,8 @@ Build-Depends: debhelper (>= 9),
libtalloc-dev,
libusrp-dev,
liblimesuite-dev,
libosmocore-dev (>= 0.10.0)
libosmocore-dev (>= 1.3.0),
osmo-gsm-manuals-dev
Standards-Version: 3.9.6
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
Vcs-Git: git://git.osmocom.org/osmo-trx
@@ -89,3 +90,12 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
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-doc
Architecture: all
Section: doc
Priority: optional
Depends: ${misc:Depends}
Description: ${misc:Package} PDF documentation
Various manuals: user manual, VTY reference manual and/or
protocol/interface manuals.

18
debian/copyright vendored
View File

@@ -1,7 +1,7 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: OsmoTRX
Source: http://cgit.osmocom.org/osmo-trx/
Files-Excluded: Transceiver52M/std_inband.rbf
Files-Excluded: Transceiver52M/device/usrp1/std_inband.rbf
Files: *
Copyright: 2008-2013 Free Software Foundation
@@ -9,13 +9,14 @@ Copyright: 2008-2013 Free Software Foundation
2010-2012 Range Networks, Inc.
License: AGPL-3+
Files: Transceiver52M/arm/*
Transceiver52M/x86/*
Transceiver52M/common/*
Files: Transceiver52M/arch/arm/*
Transceiver52M/arch/x86/*
Transceiver52M/arch/common/*
Transceiver52M/Resampler.cpp
Transceiver52M/Resampler.h
Transceiver52M/Utils.cpp
Transceiver52M/Utils.h
Transceiver52M/osmo-trx.cpp
Transceiver52M/radioInterfaceDiversity.cpp
Copyright: 2012-2013 Thomas Tsou <tom@tsou.cc>
License: LGPL-2.1+
@@ -47,11 +48,6 @@ Copyright: 2008-2010 Free Software Foundation
2010-2012 Range Networks, Inc.
License: GPL-3+
Files: autogen.sh
Copyright: 2005-2009 United States Government as represented by
the U.S. Army Research Laboratory.
License: BSD-3-clause
Files: debian/*
Copyright: 2015 Ruben Undheim <ruben.undheim@gmail.com>
License: GPL-3+
@@ -65,7 +61,7 @@ License: AGPL-3+
.
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
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

1
debian/osmo-trx-doc.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/share/doc/osmo-trx-doc/*.pdf

View File

@@ -1,16 +1,16 @@
Index: osmo-trx/debian/control
===================================================================
--- osmo-trx.orig/debian/control
+++ osmo-trx/debian/control
diff --git a/debian/control b/debian/control
index 8ff59f0..126c16a 100644
--- a/debian/control
+++ b/debian/control
@@ -13,7 +13,6 @@ Build-Depends: debhelper (>= 9),
libfftw3-dev,
libtalloc-dev,
libusrp-dev,
- liblimesuite-dev,
libosmocore-dev (>= 0.10.0)
libosmocore-dev (>= 1.3.0),
osmo-gsm-manuals-dev
Standards-Version: 3.9.6
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
@@ -29,7 +28,7 @@ Package: osmo-trx-dbg
@@ -30,7 +29,7 @@ Package: osmo-trx-dbg
Architecture: any
Section: debug
Priority: extra
@@ -19,11 +19,10 @@ Index: osmo-trx/debian/control
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
@@ -72,25 +71,6 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (USRP1)
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}
@@ -42,16 +41,20 @@ Index: osmo-trx/debian/control
- 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
-
Package: osmo-trx-doc
Architecture: all
Section: doc
diff --git a/debian/rules b/debian/rules
index 627c0c8..d9285e2 100755
--- a/debian/rules
+++ b/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
- dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
+ dh_auto_configure -- --with-uhd --with-usrp1 --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
override_dh_strip:
dh_strip --dbg-package=osmo-trx-dbg

6
debian/rules vendored
View File

@@ -9,7 +9,11 @@ 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-lms --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
override_dh_strip:
dh_strip --dbg-package=osmo-trx-dbg
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
override_dh_compress:
dh_compress -X.pdf

View File

@@ -12,5 +12,6 @@ if BUILD_MANUALS
VTY_REFERENCE = osmotrx-vty-reference.xml
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
OSMO_REPOSITORY = osmo-trx
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
endif

View File

@@ -12,7 +12,7 @@ Example configuration files for different devices and setups can be found in
=== Documented example
.Example: Static GGSN/APN configuration (single catch-all GGSN)
.Example: Single carrier configuration
----
trx
bind-ip 127.0.0.1 <1>
@@ -46,15 +46,16 @@ 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.
Ettus B200 device, or more than 2 TRXs 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
From BTS and BSC point of view, supporting multiple TRXs 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.

View File

@@ -2,3 +2,62 @@
== Counters
include::./counters_generated.adoc[]
=== Rate Counter Configurable Error Thresholds
Some rate counters such as overruns, underruns and dropped packets indicate
events that can really harm correct operation of the BTS served by OsmoTRX,
specially if they happen frequently. OsmoTRX is in most cases (depending on
maturity of device driver) prepared to dodge the temporary failure and keep
running and providing service.
Still, it is sometimes important for this kind of events to not go unnoticed by
the operator, since they may indicate issues regarding the set up that may
require operator intervention to fix it.
For instance, frequent dropped packets could indicate SDR HW/FW/power errors, or
a faulty connection against the host running OsmoTRX.
They can also indicate issues on the host running OsmoTRX itself: For instance,
OsmoTRX may not be running under a high enough priority (hence other processes
eventually battling for resources with it), or that simply the HW running
OsmoTRX is not powerful enough to accomplish all work in a timely fashion all
the time.
As a result, OsmoTRX can be configured to exit the process upon certain
conditions being met, in order to let osmoBTS notice something is wrong and thus
announcing issues through alarms to the network, where the operator can then
investigate the issue by looking at OsmoTRX logs.
These conditions are configured by means of introducing rate counter thresholds
in the VTY. The OsmoTRX user can provide those threshold commands either in the
VTY cfg file read by OsmoTRX process during startup, or by adding/removing them
dynamically through the VTY interactive console.
Each threshold cmd states an event (a rate counter type), a value and an time
interval (a second, a minute, an hour or a day). A threshold will be reached
(and OsmoTRX stopped) if its value grows bigger than the configured threshold
value over the configured time interval. This is the syntax used to manage rate
counter thresholds:
`(no) ctr-error-threshold <EVENT> <VALUE> <INTERVAL>`
If several rate counter thresholds are set, then all of them are checked over
time and the first one reached will stop OsmoTRX.
.Example: rate counter threshold configuration (VTY .cfg file)
----
trx
ctr-error-threshold rx_drop_events 2 per-minute <1>
ctr-error-threshold rx_drop_samples 800 per-second <2>
----
<1> Stop OsmoTRX if dropped event (any amount of samples) during Rx was detected 2 times or more during a minute.
<2> Stop OsmoTRX if 800 or more samples were detected during Rx to be dropped by the HW during a second.
.Example: rate counter threshold configuration (VTY interactive)
----
OsmoTRX(config-trx)# ctr-error-threshold tx_underruns 3 per-hour <1>
OsmoTRX(config-trx)# no ctr-error-threshold tx_underruns 3 per-hour <2>
----
<1> Stop OsmoTRX if 3 or more underruns were detected during Tx over the last hour
<2> Remove previously set rate counter threshold

View File

@@ -1,7 +1,26 @@
// autogenerated by show asciidoc counters
These counters and their description based on OsmoTRX 0.2.0.61-408f (OsmoTRX).
These counters and their description are based on OsmoTRX 1.0.0.95-9527 (OsmoTRX).
=== Rate Counters
// generating tables for rate_ctr_group
// rate_ctr_group table osmo-trx statistics
.trx:chan - osmo-trx statistics
[options="header"]
|===
| Name | Reference | Description
| device:rx_overruns | <<trx:chan_device:rx_overruns>> | Number of Rx overruns in FIFO queue
| device:tx_underruns | <<trx:chan_device:tx_underruns>> | Number of Tx underruns in FIFO queue
| device:rx_drop_events | <<trx:chan_device:rx_drop_events>> | Number of times Rx samples were dropped by HW
| device:rx_drop_samples | <<trx:chan_device:rx_drop_samples>> | Number of Rx samples dropped by HW
| device:tx_drop_events | <<trx:chan_device:tx_drop_events>> | Number of times Tx samples were dropped by HW
| device:tx_drop_samples | <<trx:chan_device:tx_drop_samples>> | Number of Tx samples dropped by HW
|===
== Osmo Stat Items
// generating tables for osmo_stat_items
== Osmo Counters
// generating tables for osmo_counters
// there are no ungrouped osmo_counters

View File

@@ -0,0 +1,303 @@
[[dev_USRP1_inband_signaling_usb]]
==== USRP1 in-band USB protocol
This section specifies the format of USB packets used for in-band data
transmission and signaling on the USRP1. All packets are 512-byte long, and are
transferred using USB "bulk" transfers.
IN packets are sent towards the host. OUT packets are sent away from the host.
The layout is 32-bits wide. All data is transmitted in little-endian format
across the USB.
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|O|U|D|S|E| RSSI | Chan | mbz | Tag | Payload Len |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| Payload |
. .
. .
. .
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... | .
+-+-+-+-+-+-+-+ .
. .
. Padding .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
mbz: Must be Zero::
These bits must be zero in both IN and OUT packets.
O: Overrun Flag::
Set in an IN packet if an overrun condition was detected. Must be zero in OUT
packets. Overrun occurs when the FPGA has data to transmit to the host and there
is no buffer space available. This generally indicates a problem on the host.
Either it is not keeping up, or it has configured the FPGA to transmit data at a
higher rate than the transport (USB) can support.
U: Underrun Flag::
Set in an IN packet if an underrun condition was detected. Must be zero in OUT
packets. Underrun occurs when the FPGA runs out of samples, and it's not between
bursts. See the "End of Burst flag" below.
D: Dropped Packet Flag::
Set in an IN packet if the FPGA discarded an OUT packet because its timestamp
had already passed.
S: Start of Burst Flag::
Set in an OUT packet if the data is the first segment of what is logically a
continuous burst of data. Must be zero in IN packets.
E: End of Burst Flag::
Set in an OUT packet if the data is the last segment of what is logically a
continuous burst of data. Must be zero in IN packets. Underruns are not
reported when the FPGA runs out of samples between bursts.
RSSI: 6-bit Received Strength Signal Indicator::
Must be zero in OUT packets. In IN packets, indicates RSSI as reported by front
end. FIXME The format and interpretation are to be determined.
Chan: 5-bit logical channel number::
Channel number 0x1f is reserved for control information. See "Control Channel"
below. Other channels are "data channels". Each data channel is logically
independent of the others. A data channel payload field contains a sequence of
homogeneous samples. The format of the samples is determined by the
configuration associated with the given channel. It is often the case that the
payload field contains 32-bit complex samples, each containing 16-bit real and
imaginary components.
Tag::
4-bit tag for matching IN packets with OUT packets.
//FIXME, write more...
Payload Len::
9-bit field that specifies the length of the payload field in bytes. Must be in
the range 0 to 504 inclusive.
Timestamp: 32-bit timestamp::
On IN packets, the timestamp indicates the time at which the first sample of the
packet was produced by the A/D converter(s) for that channel. On OUT packets,
the timestamp specifies the time at which the first sample in the packet should
go out the D/A converter(s) for that channel. If a packet reaches the head of
the transmit queue, and the current time is later than the timestamp, an error
is assumed to have occurred and the packet is discarded. As a special case, the
timestamp 0xffffffff is interpreted as "Now".
The time base is a free running 32-bit counter that is incremented by the A/D
sample-clock.
Payload::
Variable length field Length is specified by the Payload Len field.
Padding::
This field is 504 - Payload Len bytes long, and its content is unspecified.
This field pads the packet out to a constant 512 bytes.
===== "Data Channel" payload format
If `Chan != 0x1f`, the packet is a "data packet" and the payload is a sequence of
homogeneous samples. The format of the samples is determined by the
configuration associated with the given channel. It is often the case that the
payload field contains 32-bit complex samples, each containing 16-bit real and
imaginary components.
===== "Control Channel" payload format
If `Chan == 0x1f`, the packet is a "control packet". The control channel payload
consists of a sequence of 0 or more sub-packets.
Each sub-packet starts on a 32-bit boundary, and consists of an 8-bit Opcode
field, an 8-bit Length field, Length bytes of arguments, and 0, 1, 2 or 3 bytes
of padding to align the tail of the sub-packet to a 32-bit boundary.
Control channel packets shall be processed at the head of the queue, and shall
observe the timestamp semantics described above.
===== General sub-packet format
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+
| Opcode | Length | <length bytes> ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+
----
===== Specific sub-packet formats
RID: 6-bit Request-ID::
Copied from request sub-packet into corresponding reply sub-packet. RID allows
the host to match requests and replies.
Reg Number::
10-bit Register Number.
Ping Fixed Length::
* Opcode: OP_PING_FIXED
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Ping Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
Ping Fixed Length Reply::
* Opcode: OP_PING_FIXED_REPLY
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Ping Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
Write Register::
* Opcode: OP_WRITE_REG
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 6 | mbz | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
Write Register Masked::
Only the register bits that correspond to 1's in the mask are written
with the new value. `REG[Num] = (REG[Num] & ~Mask) | (Value & Mask)`
* Opcode: OP_WRITE_REG_MASKED
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 10 | mbz | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Mask Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
Read Register::
* Opcode: OP_READ_REG
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
Read Register Reply::
* Opcode: OP_READ_REG_REPLY
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 6 | RID | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
I2C Write::
* Opcode: OP_I2C_WRITE
* I2C Addr: 7-bit I2C address
* Data: The bytes to write to the I2C bus
* Length: Length of Data + 2
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
I2C Read::
* Opcode: OP_I2C_READ
* I2C Addr: 7-bit I2C address
* Nbytes: Number of bytes to read from I2C bus
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 3 | RID | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nbytes | unspecified padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
I2C Read Reply::
* Opcode: OP_I2C_READ_REPLY
* I2C Addr: 7-bit I2C address
* Data: Length - 2 bytes of data read from I2C bus.
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | RID | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
SPI Write::
* Opcode: OP_SPI_WRITE
* Enables: Which SPI enables to assert (mask)
* Format: Specifies format of SPI data and Opt Header Bytes
* Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format
* Data: The bytes to write to the SPI bus
* Length: Length of Data + 6
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Enables | Format | Opt Header Bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
SPI Read::
* Opcode: OP_SPI_READ
* Enables: Which SPI enables to assert (mask)
* Format: Specifies format of SPI data and Opt Header Bytes
* Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format
* Nbytes: Number of bytes to read from SPI bus.
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 7 | RID | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Enables | Format | Opt Header Bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nbytes | unspecified padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
SPI Read Reply::
* Opcode: OP_SPI_READ_REPLY
* Data: Length - 2 bytes of data read from SPI bus.
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | RID | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----
Delay::
* Opcode: OP_DELAY
* Ticks: 16-bit unsigned delay count
* Delay Ticks clock ticks before executing next operation.
----
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | Ticks |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
----

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