Compare commits

...

106 Commits

Author SHA1 Message Date
Piotr Krysik 21fc94db73 resampler: adding only one original_offset tag at given position
Change-Id: I502ca1075b4ecf37d922c72f60cca5c64fd0fdee
2019-07-18 10:33:52 +02:00
Piotr Krysik 59257a11a3 receiver: removed redundant parameter from synchronized_handler
Change-Id: I64862f60eecadd60580a117ab865ea597eaad30b
2019-07-18 10:33:49 +02:00
Piotr Krysik 9e861d7017 receiver: fixes of synchronization
Correcting and making sense of some magic constants
relating to synchronization. Some magic constants
of unknown origin still stay.

Change-Id: I5a45ce1c8bceded70d34302a5b136da5f92fd902
2019-07-18 10:33:44 +02:00
Piotr Krysik 467a7aa80f plotting: improve interface so it's possible to pass rvalues
Change-Id: I387e462a889f7e3572d8e53a241c63aafed70912
2019-07-18 10:33:41 +02:00
Piotr Krysik 0de0172afe utils/time_spec: support fractional tick_rate
If tick_rate is fractional rate_i variable is 0 and shouldn't be used in
division.

Change-Id: I660922683d837160e0546a0f339560d7d0965379
2019-07-18 10:33:37 +02:00
Piotr Krysik 43dfbdf1a9 utils/fn_time: added clock_error to fn_time_delta
Information about clock error is needed in order to transform frame number
to hardware time - which is not ideal (i.e. different than BTS time).

Change-Id: Icd77b88da0490d6c9565bf3df0342574b91aae6e
2019-07-18 10:33:32 +02:00
Piotr Krysik c5ed04572f receiver/time_sample_ref: SDR hardware time tracking
Gr-gsm's receiver needs a stream of samples with sampling frequency that
is integer multiply of gsm symbol rate. Many hardware receivers don't
support such rates, and even if they do there might be error in their
time source. Sample rate is adapted and corrected with use of a resampler.
Moreover corrections are applied in the runtime so resampler's resample
rate is changed dynamically. Those changes need to be tracked in order
to be able to track time of the hardware after resampling.

Hardware time tracking is needed to enable triggering of events in the
hardware (i.e. frequency changes, burst transmissions).

Changes were made in time_sample_ref and receiver in order to enable
SDR hardware time tracking.

Change-Id: Ide8149a5fc6c22700d3a4542115306a4dc682ff2
2019-07-18 10:33:28 +02:00
Piotr Krysik 891b2deb5f constants: added missing brackets to defined values
Change-Id: I730629a58376fb550799a566faca031fcf1cd00d
2019-07-18 10:33:23 +02:00
Piotr Krysik dc4cb98344 trx: renaming of old freq hopping block and creation of a new one
The old freq_hopper -controlling USRP frequency with use of tags -
was renamed to freq_hopper_tag. The new one - freq_hopper_msg -  that
uses uhd_source or uhd_sink control message input was created.

Change-Id: Ic867446314ef2ee6903cef85d48c280981132dae
2019-07-18 10:33:19 +02:00
Piotr Krysik cd69577a40 receiver: Added resamp_rate parameter
Change-Id: Iaffbe8fc28be9a70fbf8fa1689687fbd171f6b0e
2019-07-18 10:33:16 +02:00
Piotr Krysik f11e31dba1 Switching version to development (git) and bumping the
least significant number

Change-Id: I21d79168616d98cd2de3c6a5f597d683b34880ef
2019-07-18 10:33:09 +02:00
Vasil Velichkov f51a624ec0 Filter out SoapyAudio devices
When gr-osmosdr is compiled with SoapySDR support and SoapyAudio is
installed the audio device is picked as a first choice when detecting
devices but grgsm tools are not able to work with audio devices. So in
such cases the user has to always specify the correct SDR device in the
args parameter which is a bit inconvenient.

When args is not specified call osmosdr.device_find to get all devices
and filter out unspported ones like SoapyAudio devices. When args is
specifed just try to create osmosdr.source with whatever value has been
specified.

Add -l and --list-devices command line option that prints information
about all detected devices.

Example commands:
  grgsm_capture --list-devices
  grgsm_capture --list-devices --args=nofake
  grgsm_capture --args=uhd,type=b210 -a 111 capture.cfile
  grgsm_livemon --args=rtl
  grgsm_livemon --args=uhd,type=b210

Change-Id: Ib84081041ca6c2bc18b9da0c32bac9d3ecef65ca
2019-07-18 10:31:27 +02:00
Vasil Velichkov b615cefdee grgsm_capture: Fix device arguments
We need a space between numchan=1 and the rest of the device arguments
otherwise when --args is specified it fails with RuntimeError: bad lexical cast

$ grgsm_capture --args=soapy

Traceback (most recent call last):
  File "/usr/local/bin/grgsm_capture", line 179, in <module>
    device_args=options.device_args)
  File "/usr/local/bin/grgsm_capture", line 57, in __init__
    osmosdr.source(args="numchan=1" + device_args )
  File "/usr/local/lib64/python2.7/site-packages/osmosdr/osmosdr_swig.py", line 1170, in make
    return _osmosdr_swig.source_make(*args, **kwargs)
RuntimeError: bad lexical cast: source type value could not be interpreted as target

Change-Id: I2d1bcee835b695c91a5c44ec78a40b2d969f611c
2019-07-18 10:31:27 +02:00
Vasil Velichkov f8151a8398 Improve the TCH/H decoder logs
- Change "6,90 kbit/s" to "5.9 kbit/s"
  A typo reported in github ptrkrysik/gr-gsm#456
- Comment out the "Error! frame_nr:" message as it turns out to confuse
  users more then it actually helps debugging.
- When voice-boundary detection is enabled write the name of decoded
  control channel messages

Change-Id: I697ef944f30c4cabb62c888317dd3a7f8dcd5611
2019-07-18 10:31:27 +02:00
Piotr Krysik 252ed2feb2 Improve slightly interface of plotting so it's easier to use it
Change-Id: Ia70ab45a8beb81512a9f83e316ad2d2bc385ef19
2019-07-18 10:31:27 +02:00
Vasil Velichkov 807e06139b Fix the TCH/H decoder and demapper XML definitions
- The second argument of tch_h_decoder constructor is a string that
  accepts the value of MultiRate configuration element and not an enum
  like the tch_f_decoder decoder.
- Make the demapper's burst sink required.
- Make all parameters visible.
- Rename the TCH/H Channel parameter to Sub-channel number.
- Add the qa_tch_h_decoder test in the CMakeLists.txt
- Fix several typos

Change-Id: I92d4f49955c634df7d76f17cfb58d7106846c1bd
2019-07-18 10:31:27 +02:00
Piotr Krysik e11b1d3710 Major simplification and some argument changes in grgsm_capture
grmgs_capture tried to do too many things for a simple recorder.
It was simplified by removing the receiver and ability to
save data to bursts files.

All other stuff that is not necessary for recording signal to disk was
also removed:
-setters/getters,
-storing of parameters that never will be changed.

The 'fc' parameter name was changed to 'freq' to follow GNU Radio
guidelines.

The 'shiftoff' parameter was removed.

'bb_gain' and 'if_gain' parameters were added.

Variables specific to some of SDR's like:
-gains at different stages,
-bandwidth (not all devices can set it),
-antennas (some devices have just one, some not),
were moved to separate options group.

What is left to be exposed is:
-dc_offset_mode,
-iq_balance_mode,
-gain_mode.

Change-Id: I092a43eaddb09a99c6cc05fde13f0ae94d9e0251
2019-07-18 10:31:27 +02:00
Vasil Velichkov 8c30ba6687 grgsm_livemon: Use the osr variable in all blocks
In the GSM input adaptor and GSM Receiver blocks the osr was hardcoded
to 4 and cannot be changed using the command line parameter.

Change-Id: I4e85b898f05db636f18fdea6e5fee4fed6e0382a
2019-07-18 10:31:27 +02:00
Vasil Velichkov 4b034e7141 MacOS fixes
- Include grgsm/endian.h in tch_h_decoder_impl.cc
- Revert 0ed39fbf93 as linking with
  boost_thread is needed

Fixes GH-444

Change-Id: I00884962295082cff3eb64fa21e9f73437be0001
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 6feac435ed apps/grgsm_trx: introduce initial LMS driver support
Change-Id: Ie983b10d1814906b6e659213f865e58d0f5c08e4
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy ad0c62a41c apps/grgsm_trx: add RadioInterface driver selection argument
Change-Id: Ic11f878c176bad16b057f25725ab0cfecc01782f
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 25203809c1 trx/radio_if.py: print type of RadioInterface
Change-Id: I6c1ba8546caec122cd1ea0ed87656f691abec068
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 6ee957f904 python/trx: fork RadioInterfaceUHD from RadioInterface
Change-Id: I1644a3f6b8da0d5f2a1a133058269e5383948fda
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 8072d3acd8 trx/ctrl_if.py: use relative import for UDPLink
Change-Id: If4b00f6332461d3c37cde5a1f724906ca3d3a30f
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 0b70fcc19b trx/radio_if.py: update clock_offset_control in set_rx_freq()
Change-Id: I065bf664f775099c90c123c9ff75f262a73bf1be
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 5819e31450 trx/radio_if.py: do not print anything if freq. shift is 0
Change-Id: I100ce2ab8effc00e164e13253894445bdc816f06
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 2cbc3b5d97 python/trx: introduce and use Transceiver class
Change-Id: I6dc88edbb69a68746cc8e01206dc86f7ea2fa80f
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 04863d978f trx/ctrl_if_bb.py: reset transceiver on POWEROFF
Change-Id: I1e7f74c56388a431794a0b32b8a9537c360f5d33
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 6e7f2ce50c trx/radio_if.py: introduce a new @property 'ready'
Change-Id: I513da0f45c6a608d15fbd0e8eafe14d6af8833d7
2019-07-18 10:31:27 +02:00
Vadim Yanitskiy 7d8a1caa0f trx/radio_if.py: do not init both rx_freq and tx_freq
Change-Id: I9f251958ec90141d144fdb027aff20182131a1d1
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy d54e8fb500 python/trx: get rid of FakePM class
Change-Id: Ie96eb9735ecaa3329135c7be976ffd277a2f64f4
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy 3e6bfe799e apps/grgsm_trx: move init code from run() to __init__()
Change-Id: I1bcc9c8a2d37d156bbec93be3838200f6c4a80c2
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy 44d8c1e16b python/trx: use CamelCase naming for basic classes
Change-Id: Ica9c56d01191dda38e63b51caba2ec8c63b671c9
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy 0d0508aa86 trx/radio_if.py: use existing 'dict_toggle_sign' block
Change-Id: Ifa616644a858f493b9f8706663f9e7fed717f3d0
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy ac3fe63bad python/trx: rename 'change_sign_of_dict_elements' to 'dict_toggle_sign'
The old name was quite long, resulting into cumbersome imports:

  from change_sign_of_dict_elements import change_sign_of_dict_elements

let's use a shorter variant:

  from dict_toggle_sign import dict_toggle_sign

Change-Id: Ie75e1d6e5e74c7c1cf34154633c1472e4b85dbb6
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy 31c543f008 python/trx/__init__.py: cosmetic: add missing new line
Change-Id: I379b321866bd892a52b0c272ed50c670da380a17
2019-07-18 10:31:26 +02:00
Vasil Velichkov 4d300e9c61 Improve voice boundary detection
Decode Alerting and Progress messages and if the in-band information
flag is set start decoding the voice
2019-07-18 10:31:26 +02:00
Vasil Velichkov 2479152d5c Add TCH/H decoder tests 2019-07-18 10:31:26 +02:00
Vasil Velichkov 5968552b47 Add TCH/H decoder block with AMR multirate support
Add new TCHH channel mode
Add two new optional arguments

    -m CHAN_MODE, --mode=CHAN_MODE
                        Channel mode. Valid options are 'BCCH' (Non-combined
                        C0), 'BCCH_SDCCH4'(Combined C0), 'SDCCH8' (Stand-alone
                        control channel) 'TCHF' (Traffic Channel, Full rate),
                        'TCHH' (Traffic Channel, Half rate)

    --sub-channel=TCH_H_CHANNEL
                        TCH/H sub-channel. [default=0]
    --multi-rate=MULTI_RATE
                        The MultiRrate configuration element from the
                        Assigment Command message. Example: 28111a40. See 3GPP
                        TS 44.018 - 10.5.2.21aa MultiRate configuration

Example:
    grgsm_decode -m TCHH --sub-channel 0 --multi-rate 2811 -o voice.amr ...
2019-07-18 10:31:26 +02:00
Vasil Velichkov 9d3606b17e Add tests for the TCH/F and TCH/H Demappers 2019-07-18 10:31:26 +02:00
Vasil Velichkov 63e568dc3d Move HR demapping into a separate block 2019-07-18 10:31:26 +02:00
Andrew Artyushok 3e2f883ba0 Add HR demapping 2019-07-18 10:31:26 +02:00
Piotr Krysik 7fb1738cd8 transmitter/txtime_setter: fix error print syntax error 2019-07-18 10:31:26 +02:00
David Holm 9aadf3db64 lib/trx, lib/transmitter: Include grgsm/endian.h
MacOS X does not have endian.h and the build will fail unless
grgsm/endian.h is used.
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy fad602b2b0 trx/txtime_setter: print error if reference fn is missing
Change-Id: I2a6e3f41b6fe79b92d85ff98bc2cd9afd9bdc568
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy 06f76fb3a6 trx/txtime_setter: reduce code nesting in process_txtime_of_burst()
Change-Id: I5c334e16d6b28a5e32cd62a177ad56bfc8e748ee
2019-07-18 10:31:26 +02:00
Vadim Yanitskiy ef93382ed1 apps/grgsm_trx: (re)structurize help message
Change-Id: I612d8ae7d3ff99fee809e10d95919989bfbe0f59
2019-07-18 10:31:26 +02:00
Piotr Krysik db65883a68 apps/grgsm_trx: remove redundant unit from freq_offset variable name
Frequency is always in Hz so there is no need to add that
information in the variable name.

Change-Id: I509771c3fe072069a680f66b0763ae6825f6d529
2019-07-18 10:31:26 +02:00
Vasil Velichkov ad01342ec7 Add .gitreview similar to the other osmocom's projects
A gitreview file is required to use git review.
More information about git review
https://docs.openstack.org/infra/git-review/
https://www.mediawiki.org/wiki/Gerrit/git-review

Change-Id: Ie07446ba1a13e53c87bcc9b23e3b775803d158f4
2019-07-18 10:31:25 +02:00
Piotr Krysik 554aa905e4 receiver/time_sample_ref: improve accuracy of offset<->time conversions
Change-Id: Ie6e7ea4337c9e23a8e4445136a3adffc96b19942
2018-11-02 11:52:43 +01:00
Piotr Krysik 89dc99016e misc_utils/resampler: Add offsets of tags at the input
This change is done to make keeping track of sample counter
at the input of the resampler easier from blocks connected
to its output. Each tag leaving the resampler has accompanying
"original_offset" tag with its offset before sample rate change.

Change-Id: Iea8ca4a092029f774406dced8a97a17810f21f62
2018-11-02 11:50:14 +01:00
Piotr Krysik acd7d71db4 misc_utils/resampler: Cosmetic changes
Change variables names to more readable.
Remove old comment.

Change-Id: Ibe22677cfc4b3e1a9fb900e5055cdce0a7656a92
2018-11-02 11:31:32 +01:00
Piotr Krysik 6a0fb1a415 misc_utils/resampler: Fix repetition of tags at the output
When tag appears at the last sample of the ouput buffer
it will be added again in the next run of the work function.

Here this issue was solved by changing the codition so the
tag appearing at the end of the output buffer is not added.

Change-Id: Ia4f0923e2071184879a023d0fe01026d8e9ccbda
2018-11-02 11:26:13 +01:00
Vasil Velichkov 00a9c400a8 grgsm_channelize: Add ichar data type
This is the output format of rtl_sdr and hackrf_transfer and this would
allow direct channelization without an additional post processing.

Change-Id: Ia489eca9ec7defc3a83946c42f1ae3f136efe4e8
2018-09-13 15:35:44 +02:00
Piotr Krysik d6e8bb744e apps/grgsm_trx: remove unnecessary checks of freq_offset
Checking if freq_offset is None doesn't make sense currently
as it's always set to a float value by argparse (to 0 by default).

Change-Id: Ie8bae1ccde60d07fc25e0b874afa5aaaac04d8a7
2018-09-13 15:35:44 +02:00
Piotr Krysik 11583adb9d trx/radio_if: add freq_offset parameter to constructor
Change-Id: Ie1db02b719a0fec478b8a8b8a95643fb10fdfce5
2018-09-13 15:35:44 +02:00
Vasil Velichkov 492f48c7d3 burst_file_source: Fix reading longer bursts
- Read bursts with pmt::deserialize directly from the std::filebuf
- Remove the unused unserialized variable
- Add tests

Since df978693 when the rx_time tags are present in the incomming stream
the gsm receiver adds fm_time to the burst's PMT and the bursts that
burst file sink writes becomes longer because of the additional field.

The burst file source block was expecting all burst to be 147 bytes long
and reading files with longer bursts was failing with an unhandled exception.

terminate called after throwing an instance of 'pmt::exception'
thread[thread-per-block[5]: <block dummy_burst_filter (2)>]: pmt_cdr: wrong_type : #f
  what():  pmt::deserialize: malformed input stream, tag value = : 115

Change-Id: I989b0d6a6b214088b7880e5cbf7bb6725492dbfc
2018-09-13 15:35:44 +02:00
Piotr Krysik 262bda10e6 apps/grgsm_trx: migrate from getopt to argparse
Change-Id: I24a17b4cd44db0ce95a19d7470f4f09f3c85a26d
2018-09-13 15:35:44 +02:00
Piotr Krysik 58659a4dee trx/radio_if.py: clarify magic numbers in sample rate calculation
Change-Id: I55f283113d0324a0236b7bbf13bce5718003b857
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 562125270b apps/grgsm_trx: fix inaccurate sample rate calculation
Change-Id: I0c309588fa0f7822abfb3919327639735db07679
2018-09-13 15:35:44 +02:00
Vasil Velichkov 9e46c4807d Format the output into ostringstream and then write it at once.
This fixes the garbled output when multiple printers are used in a
flowgraph.

closes #255
closes #420

Change-Id: I1012ed26371b4c67163545652f0a1ce0f576af9e
2018-09-13 15:35:44 +02:00
Vasil Velichkov 1e04c1fd9c grgsm_scanner: Add --debug option
When set the stdout and stderr won't be redirected to /dev/null which
will facilitate resolving issues

Change-Id: I11e99facb5a1ab9c9bfee3c314a91a74f98a2523
2018-09-13 15:35:44 +02:00
Piotr Krysik ba6fd0e776 Revert "grgsm_trx: Migrated argument parsing to argparse" that shouldn't be pushed
This reverts commit c62a3d9f55.
2018-09-13 15:35:44 +02:00
Piotr Krysik fcb64a8adf grgsm_trx: Migrated argument parsing to argparse
Argparse makes adding new parameters easier and
adds consistent way of handling default values of
parameters and printing of their help info.

Change-Id: Idf99fd7a581464aa2f77fe01e721dbd162686811
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 3f9abbdf92 apps/grgsm_trx: add baseband frequency shift feature
An ability to shift the baseband frequency would allow one to
run both base stations and (OsmocomBB-based) mobile stations on
any frequency (e.g. in 2.4 GHz WiFi band)!

This is achieved by adding a given frequency offset to the
result of "ARFCN -> RX/TX frequency" calculation.

Usage example:

  grgsm_trx --freq-offset -500M

Both RX (Downlink) and TX (Uplink) frequencies will be shifted
by 500 MHz back, e.g. tuning request to ARFCN 105 would result
in tuning the radio to 456.0 MHz (instead of 956.0 MHz).

Related: OS#3520 (https://osmocom.org/versions/136)
Change-Id: I42e397e47402a87f4141ef31b25eff4c8c1267e2
2018-09-13 15:35:44 +02:00
Piotr Krysik 22cd7c973f Changed submodule URLs to point to osmocom servers 2018-09-13 15:35:44 +02:00
Piotr Krysik 6d2f53826f Increasing version number
in order to correct wrongs of previous attempt

Change-Id: Ie067b48a9c67a13e928598ec722a7e27c464de8f
2018-09-13 15:35:44 +02:00
Piotr Krysik 80ec5ba599 New gr-gsm version
Change-Id: I72eafebe892692ad0db5ad149e14f2c59b41d3d2
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 641133e5d8 apps/grgsm_trx: print bind / remote address and port
Change-Id: If750d476f3972f1ab5c5b637438d14d40b1e3d87
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 216558454d apps/grgsm_trx: introduce bind address option
The new option (-b --bind-addr) allows one to specify the bind
address for both DATA and CTRL interfaces. By default, '0.0.0.0'
is used, so there are no restrictions for the L1 source address.

Change-Id: I3339f686b53db07cfd1bff9a516f4bdc28058cd9
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy f94a51b64d gsm_trx_burst_if: allow to customize the bind address
Pleviously remote address for DATA interface was also used as the
bind address, what is definitely wrong. Let's change the API a bit
in order to allow one to specify a custom bind address.

Change-Id: I6e5f7b7119ac454217b8dd04f9ee0dd3b23972b6
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 053f9e53f0 trx/ctrl_if.py: send control responses to where commands are from
When we receive a control command, we should not simply send the
response to the default destination, but send it back to the exact
IP/prt from which the command originated.

This ensures correct routing of responses even in case multiple
programs are interfacing concurrently with a control socket.

Cherry-picked from: I24a0bba6eed059b101af95dac7d059f34dd715fc
Change-Id: I1f304ea887dc957d3ad53adb1e3c56ab27d8f196
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 8705f0e546 trx/udp_link.py: set SO_REUSEADDR socket option
Setting this option allows one to reuse existing connections,
for example, by injecting CTRL commands or DATA bursts
into existing connections.

Cherry-picked from: I0882c76affa9a668a12d10967081054d2b666ed1
Change-Id: I6d256683a7aa0419cd5bd0a3eaa97aefdf3254f9
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy ed9d7282d4 trx/udp_link.py: close socket in destructor
Previously it was required to call the UDPLink.shutdown() method
manually in order to close a socket. Let's do it automatically
using the destructor of UDPLink.

Cherry-picked from: I59c3dc61ec58cd9effeb789947d28fd602ca91f4
Change-Id: Ief7aa21d1e50682a90616833b679741957193aae
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 4cedbb530e apps/grgsm_trx: use format string for help message
Instead of using the hard-coded default values in help message,
it makes sense to use a format string, and pass the actual
values when printing help.

Change-Id: Ib1bf0ef3ded86aa92faeb9b63eb286283f5c8c3d
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 3824bbaeb1 trx/radio_if.py: drop useless import of osmosdr
Change-Id: Iacd5ca118957e3ecf05df81f511b17bb9a8982b5
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 45457e959d trx/radio_if.py: reset UHD device time at startup
Some UHD devices, such as UmTRX, require one to manually reset the
hardware clock, otherwise the burst transmission doesn't work.

Change-Id: Idddc1387199bd22342ec5af25c8635e73352e315
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy 07a2abb635 grgsm_trx: change default TRX port number to 6700
In order to avoid clashes with OsmoTRX, which may be also
running on the same host, let's use a different port range
starting from 6700 by default.

This idea was introduced as a result of OS#2984.

Change-Id: Iaf0c78733bfefcb0b0938abf6d316e27d03ecab4
2018-09-13 15:35:44 +02:00
Vadim Yanitskiy a609d2f9c9 apps/grgsm_trx: also print Piotr as a copyright holder
Despite the most part of Python code was written by Vadim, it's
heavily based on huge and impressive work done by Piotr. Let's
also print his credentials in the license header.

Change-Id: Icca7c679d84f99440ff502219f624e0f73112744
2018-09-13 15:35:44 +02:00
Piotr Krysik f0f7183cb7 tests: Fix Debian and Kali dockerfiles
Change-Id: I61d37cb6882a8ca7bbfc97412c52e745a2e5ec7c
2018-09-13 15:35:44 +02:00
Piotr Krysik 8d30757dc0 Keep the old mailing list for now
but change wiki address and installation description
addres to the new (osmocom) ones.
2018-09-13 15:35:44 +02:00
Harald Welte de798b38ca README.md: Turn URLs into hyperlinks (video, mailing list)
Change-Id: Iacf7c4b13b0e32ea7761b859f7422ab630c8616f
2018-09-13 15:35:44 +02:00
Harald Welte cd3b36f1d5 README.md: Change mailing list address to osmocom.org
Change-Id: Ie7b474138444f0ceea160f74df4b693030ea07a8
2018-09-13 15:35:44 +02:00
Piotr Krysik 6a64ce82f7 Simplify cmake checks related to libosmocore 2018-09-13 15:35:44 +02:00
Vasil Velichkov f9f7cbd56b Run tests with CTEST_OUTPUT_ON_FAILURE=1 2018-09-13 15:35:44 +02:00
Vasil Velichkov 9d211b3da3 Add LOCAL_OSMOCOM cmake option
When set the gr-gsm will be compiled and linked with the local copies of
the osmocom libraries.
2018-09-13 15:35:44 +02:00
Piotr Krysik bbc8af53e8 Placing grcc compilation utils in separate files
Avoiding increasing required cmake version with use of wrapper shell script.
2018-09-13 15:35:44 +02:00
Piotr Krysik ded359bf36 Separating libosmogsm from the rest 2018-09-13 15:35:44 +02:00
Vasil Velichkov 0658bd424d Check that pkg-config is available
It is needed to properly detect the grcc full path
See also 3f6ab15a7a
2018-09-13 15:35:44 +02:00
Vasil Velichkov b4a0acf9f5 Add DEBIAN_FRONTEND=noninteractive 2018-09-13 15:35:44 +02:00
Vasil Velichkov e9aaeb14cc Fix the parallel build with cmake 3.11
Copy UseSWIG.cmake from the gnuradio repository from commit
4433a7703fe3f5713c2200a0f7c11b13510f34cc

This macro is distributed in the Debian's gnuradio-dev package but it's
not available in Fedora/Centos gnuradio-devel package. The gnuradio's
version contains a fix for the parallel build 99a09af05fda6d0bab0cf3724a1c6bf453c71bc7
and some other improvements as well.
2018-09-13 15:35:44 +02:00
Vasil Velichkov c91b128c1d Add CentOS7 docker container 2018-09-13 15:35:44 +02:00
Vasil Velichkov f608e7c766 Add cmake options that enable/disable grcc compilation
To disable compilation of both grgsm_livemon and grgsm_livemon_headless
execute

  cmake -DENABLE_GRCC=OFF ..

To disable only one execute

  cmake -DENABLE_GRGSM_LIVEMON=OFF ..
  cmake -DENABLE_GRGSM_LIVEMON_HEADLESS=OFF ..
2018-09-13 15:35:44 +02:00
Piotr Krysik 3fbc96c5ea Changing tabs to spaces 2018-09-13 15:35:44 +02:00
Piotr Krysik 02454b32b9 Disabling installation of desktop files
Installation of dekstop files is causing problems with packaging - github issue #335.
Disabling it then.
2018-09-13 15:35:44 +02:00
Piotr Krysik 4a3a52c897 Commenting out a line causeing crash on HackRF 2018-09-13 15:35:44 +02:00
Piotr Krysik 6463dc7641 Freq hopping fixes 2018-06-18 14:43:50 +02:00
Piotr Krysik 4e4a92ea05 Added functions for arfcn conversions taken from libosmocore 2018-05-11 11:39:23 +02:00
Piotr Krysik 1a32fc42a0 Added block for doing hopping for mobile station transceiver 2018-05-11 11:38:07 +02:00
Piotr Krysik b7cce89e55 Distilled function to generate hopping from cx_channel hopper 2018-05-11 11:37:21 +02:00
Piotr Krysik fd3d883d4e Fix includes after moving trx_burst_if 2018-05-06 22:23:06 +02:00
Piotr Krysik 0aa24edf21 Added short description of grgsm_trx 2018-05-05 13:06:22 +02:00
Piotr Krysik 88a4d483c9 Updated e-mail addresses of Vadim and Vasil 2018-05-05 13:06:02 +02:00
Piotr Krysik 29dc4b3667 Fix burst_to_fn_time name in gsm_burst_to_fn_time.xml 2018-05-05 12:40:14 +02:00
Piotr Krysik 1373c32d7e Moving trx burst interface to trx directory
and to new 'Transceiver' cathegory in gnuradio-companion
2018-05-05 12:38:11 +02:00
Piotr Krysik 450ed8fbbe Fix gsm_msg_to_tag block record in grc/gsm_block_tree.xml
so it appear in gnuradio-companion
2018-05-05 12:24:33 +02:00
115 changed files with 5642 additions and 910 deletions

4
.gitmodules vendored
View File

@ -1,6 +1,6 @@
[submodule "test_data"]
path = test_data
url = https://github.com/ptrkrysik/test_data.git
url = https://git.osmocom.org/ptrkrysik/test_data
[submodule "examples"]
path = examples
url = https://github.com/ptrkrysik/examples.git
url = https://git.osmocom.org/ptrkrysik/examples

3
.gitreview Normal file
View File

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

View File

@ -60,8 +60,8 @@ endif()
# Set the version information here
set(VERSION_INFO_MAJOR_VERSION 0)
set(VERSION_INFO_API_COMPAT 41)
set(VERSION_INFO_MINOR_VERSION 4)
set(VERSION_INFO_API_COMPAT 42)
set(VERSION_INFO_MINOR_VERSION 3)
set(VERSION_INFO_MAINT_VERSION git)
include(GrVersion) #setup version info
@ -88,7 +88,7 @@ set(Boost_ADDITIONAL_VERSIONS
"1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64"
"1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69"
)
find_package(Boost "1.35" COMPONENTS filesystem system)# thread)
find_package(Boost "1.35" COMPONENTS filesystem system thread)
if(NOT Boost_FOUND)
message(FATAL_ERROR "Boost required to compile gr-gsm")
@ -136,11 +136,16 @@ find_package(Gnuradio)
find_package(Volk)
find_package(CppUnit)
#find_package(Doxygen)
option(LOCAL_OSMOCOM "Build with local osmocom libraries" OFF)
find_package(Libosmocore)
find_package(Libosmocodec)
find_package(Libosmocoding)
find_package(Libosmogsm)
if(NOT PKG_CONFIG_FOUND)
message(FATAL_ERROR "pkg-config is required to compile gr-gsm")
endif()
if(NOT GNURADIO_RUNTIME_FOUND)
message(FATAL_ERROR "GnuRadio Runtime required to compile gr-gsm")
endif()
@ -150,8 +155,13 @@ endif()
if(NOT CPPUNIT_FOUND)
message(FATAL_ERROR "CppUnit required to compile gr-gsm")
endif()
if(NOT LIBOSMOCORE_FOUND OR NOT LIBOSMOCODEC_FOUND)
message(STATUS "Compiling local version of libosmocore")
if(NOT LIBOSMOCORE_FOUND OR NOT LIBOSMOCODEC_FOUND OR NOT LIBOSMOGSM_FOUND)
set(LOCAL_OSMOCOM ON)
endif()
if(LOCAL_OSMOCOM)
message(STATUS "Compiling with local osmocom libraries")
elseif(NOT LIBOSMOCODING_FOUND)
message(STATUS "Compiling local version of libosmocoding")
endif()

View File

@ -6,7 +6,7 @@ The aim is to provide set of tools for receiving information transmitted by GSM
Installation and usage
======================
Please see project's wiki https://github.com/ptrkrysik/gr-gsm/wiki for information on [installation](https://github.com/ptrkrysik/gr-gsm/wiki/Installation) and [usage](https://github.com/ptrkrysik/gr-gsm/wiki/Usage) of gr-gsm.
Please see project's [wiki](https://osmocom.org/projects/gr-gsm/wiki/index) for information on [installation](https://osmocom.org/projects/gr-gsm/wiki/Installation) and [usage](https://github.com/ptrkrysik/gr-gsm/wiki/Usage) of gr-gsm.
Mailing list
============
@ -20,9 +20,9 @@ Mailing list is a place for general discussions, questions about the usage and i
- version of gnuradio (it can be obtained with: gnuradio-companion --version)
- error messages (in case of pybombs installation they can be obtained after switching it to verbous mode with -v option).
To join the group with any e-mail addres (google account is not required) use this link:
To join the group with any e-mail address, use this link:
https://groups.google.com/forum/#!forum/gr-gsm/join
<https://groups.google.com/forum/#!forum/gr-gsm/join>
Development
===========
@ -34,7 +34,7 @@ Videos
======
Short presentation of *Airprobe*'like application of *gr-gsm*:
https://www.youtube.com/watch?v=Eofnb7zr8QE
<https://www.youtube.com/watch?v=Eofnb7zr8QE>
Credits
=======
@ -42,9 +42,9 @@ Credits
*Roman Khassraf* \<rkhassraf (at) gmail.com\> - blocks for demultiplexing and decoding of voice channels, decryption block supporting all ciphers used in GSM, blocks for storing and reading GSM bursts, project planning and user support
*Vadim Yanitskiy* - control and data interface for the transceiver, grgsm_trx application
*Vadim Yanitskiy* \<axilirator (at) gmail.com\> - control and data interface for the transceiver, grgsm_trx application
*Vasil Velichkov* - automatic compilation of grc applications, fixes and user support
*Vasil Velichkov* \<vvvelichkov (at) gmail.com\> - automatic compilation of grc applications, fixes and user support
*Pieter Robyns* \<pieter.robyns (at) uhasselt.be\> - block reversing channel hopping

View File

@ -18,65 +18,29 @@
# Boston, MA 02110-1301, USA.
include(GrPython)
include(GrccCompile)
add_subdirectory(helpers)
add_subdirectory(apps_data)
#add_subdirectory(apps_data)
SET(PYTHONPATH
${CMAKE_SOURCE_DIR}/python
${CMAKE_SOURCE_DIR}/python/misc_utils
${CMAKE_SOURCE_DIR}/python/demapping
${CMAKE_SOURCE_DIR}/python/receiver
${CMAKE_SOURCE_DIR}/python/transmitter
${CMAKE_SOURCE_DIR}/python/trx
${CMAKE_BINARY_DIR}/swig
$ENV{PYTHONPATH}
)
string(REPLACE ";" ":" PYTHONPATH "${PYTHONPATH}")
GRCC_COMPILE(grgsm_livemon)
GRCC_COMPILE(grgsm_livemon_headless)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/grgsm_livemon
COMMAND "${CMAKE_COMMAND}"
-E env PYTHONPATH="${PYTHONPATH}" GRC_BLOCKS_PATH=${CMAKE_SOURCE_DIR}/grc
${PC_GNURADIO_RUNTIME_PREFIX}/${GR_RUNTIME_DIR}/grcc -d ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/grgsm_livemon.grc
COMMAND "${CMAKE_COMMAND}" -E rename grgsm_livemon.py grgsm_livemon
DEPENDS grgsm_livemon.grc
)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/grgsm_livemon_headless
COMMAND "${CMAKE_COMMAND}"
-E env PYTHONPATH="${PYTHONPATH}" GRC_BLOCKS_PATH=${CMAKE_SOURCE_DIR}/grc
${PC_GNURADIO_RUNTIME_PREFIX}/${GR_RUNTIME_DIR}/grcc -d ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/grgsm_livemon_headless.grc
COMMAND "${CMAKE_COMMAND}" -E rename grgsm_livemon_headless.py grgsm_livemon_headless
DEPENDS grgsm_livemon_headless.grc
)
set(grgsm_flowgraphs "")
OPTION(ENABLE_GRCC "Compile the flowgraphs with grcc" ON)
OPTION(ENABLE_GRGSM_LIVEMON "Compile grgsm_livemon" ON)
OPTION(ENABLE_GRGSM_LIVEMON_HEADLESS "Compile grgsm_livemon_headless" ON)
########################################################################
# Override the GR_UNIQUE_TARGET function to not append a hash
# to the `target` name, because we need a known name in order
# to add an explicit dependency that's needed for the parallel build
#
# The original code segment (taken from GrPython.cmake) is
#
# execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib
#unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5]
#print(re.sub('\\W', '_', '${desc} ${reldir} ' + unique))"
# OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE)
#
########################################################################
function(GR_UNIQUE_TARGET desc)
file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR})
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib
print(re.sub('\\W', '_', '${desc} ${reldir}'))"
OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE)
add_custom_target(${_target} ALL DEPENDS ${ARGN})
endfunction(GR_UNIQUE_TARGET)
if(ENABLE_GRCC AND ENABLE_GRGSM_LIVEMON)
list (APPEND grgsm_flowgraphs ${CMAKE_CURRENT_BINARY_DIR}/grgsm_livemon)
endif(ENABLE_GRCC AND ENABLE_GRGSM_LIVEMON)
if(ENABLE_GRCC AND ENABLE_GRGSM_LIVEMON_HEADLESS)
list (APPEND grgsm_flowgraphs ${CMAKE_CURRENT_BINARY_DIR}/grgsm_livemon_headless)
endif(ENABLE_GRCC AND ENABLE_GRGSM_LIVEMON_HEADLESS)
GR_PYTHON_INSTALL(
PROGRAMS
${CMAKE_CURRENT_BINARY_DIR}/grgsm_livemon
${CMAKE_CURRENT_BINARY_DIR}/grgsm_livemon_headless
${grgsm_flowgraphs}
grgsm_scanner
grgsm_decode
grgsm_trx
@ -85,7 +49,7 @@ GR_PYTHON_INSTALL(
# The add_dependencies(...) is very important for the parallel build `make -j $(nproc)`
# The `pygen_apps` target is generated in GR_PYTHON_INSTALL function which calls
# GR_UNIQUE_TARGET that we redefine above.
# GR_UNIQUE_TARGET that we redefine in GrccCompile.
add_dependencies(pygen_apps _grgsm_swig)
install(

View File

@ -31,3 +31,6 @@ There are following helper programs for grgsm_decode program:
After these changes are done, build the
grgsm_livemon_headless python code using the grcc
compiler.
* grgsm_trx - a transceiver that together with Osmocom-BB (throught trxcon application)
forms a GSM mobile station. Currently it works on non-hopping channels only.

View File

@ -40,6 +40,8 @@ class grgsm_decoder(gr.top_block):
a5=1, a5_kc=None,
speech_file=None, speech_codec=None,
enable_voice_boundary_detection=False,
tch_h_channel=0,
multi_rate=0,
verbose=False,
print_bursts=False, ppm=0):
@ -100,11 +102,16 @@ class grgsm_decoder(gr.top_block):
self.tch_f_decoder = grgsm.tch_f_decoder(speech_codec, enable_voice_boundary_detection)
self.tch_f_pdu_to_tagged_stream = blocks.pdu_to_tagged_stream(blocks.byte_t, "packet_len")
self.tch_f_file_sink = blocks.file_sink(gr.sizeof_char*1, speech_file, False)
elif self.chan_mode == 'TCHH':
self.tch_h_demapper = grgsm.tch_h_chans_demapper(self.timeslot, tch_h_channel)
self.tch_h_decoder = grgsm.tch_h_decoder(tch_h_channel, multi_rate, enable_voice_boundary_detection)
self.tch_f_pdu_to_tagged_stream = blocks.pdu_to_tagged_stream(blocks.byte_t, "packet_len")
self.tch_f_file_sink = blocks.file_sink(gr.sizeof_char*1, speech_file, False)
if self.kc != [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]:
self.decryption = grgsm.decryption(self.kc, self.a5)
self.cch_decoder_decrypted = grgsm.control_channels_decoder()
if self.chan_mode == 'TCHF':
if self.chan_mode in ('TCHF', 'TCHH'):
self.decryption_tch_sacch = grgsm.decryption(self.kc, self.a5)
self.cch_decoder = grgsm.control_channels_decoder()
@ -209,12 +216,32 @@ class grgsm_decoder(gr.top_block):
if self.verbose:
self.msg_connect(self.tch_f_decoder, "msgs", self.message_printer, "msgs")
self.msg_connect(self.cch_decoder, "msgs", self.message_printer, "msgs")
elif self.chan_mode == 'TCHH':
self.msg_connect(self.timeslot_filter, "out", self.tch_h_demapper, "bursts")
if self.kc != [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]:
self.msg_connect(self.tch_h_demapper, "acch_bursts", self.decryption_tch_sacch, "bursts")
self.msg_connect(self.tch_h_demapper, "tch_bursts", self.decryption, "bursts")
self.msg_connect(self.decryption_tch_sacch, "bursts", self.cch_decoder, "bursts")
self.msg_connect(self.decryption, "bursts", self.tch_h_decoder, "bursts")
else:
self.msg_connect(self.tch_h_demapper, "acch_bursts", self.cch_decoder, "bursts")
self.msg_connect(self.tch_h_demapper, "tch_bursts", self.tch_h_decoder, "bursts")
self.msg_connect(self.tch_h_decoder, "msgs", self.socket_pdu, "pdus")
self.msg_connect(self.cch_decoder, "msgs", self.socket_pdu, "pdus")
self.msg_connect(self.tch_h_decoder, "voice", self.tch_f_pdu_to_tagged_stream, "pdus")
self.connect((self.tch_f_pdu_to_tagged_stream, 0), (self.tch_f_file_sink, 0))
if self.verbose:
self.msg_connect(self.tch_h_decoder, "msgs", self.message_printer, "msgs")
self.msg_connect(self.cch_decoder, "msgs", self.message_printer, "msgs")
if __name__ == '__main__':
# List of channel configurations
channel_modes = ['BCCH', 'BCCH_SDCCH4', 'SDCCH8', 'TCHF']
channel_modes = ['BCCH', 'BCCH_SDCCH4', 'SDCCH8', 'TCHF', 'TCHH']
# mapping options to grgsm's enums
tch_codecs = collections.OrderedDict([
@ -265,7 +292,7 @@ if __name__ == '__main__':
type='choice', choices=channel_modes,
help="Channel mode. Valid options are 'BCCH' (Non-combined C0), "
"'BCCH_SDCCH4'(Combined C0), 'SDCCH8' (Stand-alone control channel) "
"and 'TCHF' (Traffic Channel, Full rate) ")
"'TCHF' (Traffic Channel, Full rate), 'TCHH' (Traffic Channel, Half rate) ")
parser.add_option("-t", "--timeslot", dest="timeslot", type="intx", default=0,
help="Timeslot to decode [default=%default]")
parser.add_option("-u", "--subslot", dest="subslot", type="intx",
@ -313,7 +340,12 @@ if __name__ == '__main__':
help="TCH-F speech codec [default=%default]. "
"Valid options are " + ", ".join(tch_codecs.keys()))
tch_options.add_option("-o", "--output-tch", dest="speech_output_file", default="/tmp/speech.au.gsm",
help="TCH/F speech output file [default=%default].")
help="tch/f speech output file [default=%default].")
tch_options.add_option("--sub-channel", dest="tch_h_channel", default="0", type='intx',
help="TCH/H sub-channel. [default=0]")
tch_options.add_option("--multi-rate", dest="multi_rate", default="", type='string',
help="The MultiRate configuration element from the Assignment Command message. "
"Example: 28111a40. See 3GPP TS 44.018 - 10.5.2.21aa MultiRate configuration")
tch_options.add_option("--voice-boundary", dest="enable_voice_boundary_detection", action="store_true", default=False,
help="Enable voice boundary detection for traffic channels. This can help reduce noice in the output.")
parser.add_option_group(tch_options)
@ -358,6 +390,8 @@ if __name__ == '__main__':
a5=options.a5, a5_kc=kc,
speech_file=options.speech_output_file, speech_codec=tch_codecs.get(options.speech_codec),
enable_voice_boundary_detection=options.enable_voice_boundary_detection,
tch_h_channel=options.tch_h_channel,
multi_rate=options.multi_rate,
verbose=options.verbose,
print_bursts=options.print_bursts, ppm=options.ppm)

View File

@ -933,7 +933,7 @@
</param>
<param>
<key>osr</key>
<value>4</value>
<value>osr</value>
</param>
<param>
<key>fc</key>
@ -1043,7 +1043,7 @@
</param>
<param>
<key>osr</key>
<value>4</value>
<value>osr</value>
</param>
<param>
<key>tseq_nums</key>
@ -2844,7 +2844,7 @@
</param>
<param>
<key>args</key>
<value>args</value>
<value>str(grgsm.device.get_default_args(args))</value>
</param>
<param>
<key>_enabled</key>

View File

@ -767,7 +767,7 @@
</param>
<param>
<key>osr</key>
<value>4</value>
<value>osr</value>
</param>
<param>
<key>fc</key>
@ -877,7 +877,7 @@
</param>
<param>
<key>osr</key>
<value>4</value>
<value>osr</value>
</param>
<param>
<key>tseq_nums</key>
@ -2414,7 +2414,7 @@
</param>
<param>
<key>args</key>
<value>args</value>
<value>str(grgsm.device.get_default_args(args))</value>
</param>
<param>
<key>_enabled</key>

View File

@ -36,7 +36,7 @@ import os
import osmosdr
import pmt
import time
import sys
# from wideband_receiver import *
@ -208,8 +208,9 @@ class wideband_scanner(gr.top_block):
# if no file name is given process data from rtl_sdr source
print "Args=", args
self.rtlsdr_source = osmosdr.source(args="numchan=" + str(1) + " " + args)
self.rtlsdr_source.set_min_output_buffer(int(sample_rate*rec_len))
self.rtlsdr_source = osmosdr.source(args="numchan=" + str(1) + " " +
str(grgsm.device.get_default_args(args)))
#self.rtlsdr_source.set_min_output_buffer(int(sample_rate*rec_len)) #this line causes segfaults on HackRF
self.rtlsdr_source.set_sample_rate(sample_rate)
# capture half of GSM channel lower than channel center (-0.1MHz)
@ -297,7 +298,7 @@ class channel_info(object):
return "ARFCN: %4u, Freq: %6.1fM, CID: %5u, LAC: %5u, MCC: %3u, MNC: %3u, Pwr: %3i" % (
self.arfcn, self.freq / 1e6, self.cid, self.lac, self.mcc, self.mnc, self.power)
def do_scan(samp_rate, band, speed, ppm, gain, args, prn = None):
def do_scan(samp_rate, band, speed, ppm, gain, args, prn = None, debug = False):
signallist = []
channels_num = int(samp_rate / 0.2e6)
for arfcn_range in grgsm.arfcn.get_arfcn_ranges(band):
@ -311,14 +312,15 @@ def do_scan(samp_rate, band, speed, ppm, gain, args, prn = None):
while current_freq < stop_freq:
# silence rtl_sdr output:
# open 2 fds
null_fds = [os.open(os.devnull, os.O_RDWR) for x in xrange(2)]
# save the current file descriptors to a tuple
save = os.dup(1), os.dup(2)
# put /dev/null fds on 1 and 2
os.dup2(null_fds[0], 1)
os.dup2(null_fds[1], 2)
if not debug:
# silence rtl_sdr output:
# open 2 fds
null_fds = [os.open(os.devnull, os.O_RDWR) for x in xrange(2)]
# save the current file descriptors to a tuple
save = os.dup(1), os.dup(2)
# put /dev/null fds on 1 and 2
os.dup2(null_fds[0], 1)
os.dup2(null_fds[1], 2)
# instantiate scanner and processor
scanner = wideband_scanner(rec_len=6 - speed,
@ -359,12 +361,14 @@ def do_scan(samp_rate, band, speed, ppm, gain, args, prn = None):
scanner = None
# restore file descriptors so we can print the results
os.dup2(save[0], 1)
os.dup2(save[1], 2)
# close the temporary fds
os.close(null_fds[0])
os.close(null_fds[1])
if not debug:
# restore file descriptors so we can print the results
os.dup2(save[0], 1)
os.dup2(save[1], 2)
# close the temporary fds
os.close(null_fds[0])
os.close(null_fds[1])
if prn:
prn(found_list)
signallist.extend(found_list)
@ -384,11 +388,16 @@ def argument_parser():
parser.add_option("-g", "--gain", dest="gain", type="eng_float", default=24.0,
help="Set gain [default=%default]")
parser.add_option("", "--args", dest="args", type="string", default="",
help="Set device arguments [default=%default]")
help="Set device arguments [default=%default]."
" Use --list-devices the view the available devices")
parser.add_option("-l", "--list-devices", action="store_true",
help="List available SDR devices, use --args to specify hints")
parser.add_option("--speed", dest="speed", type="intx", default=4,
help="Scan speed [default=%default]. Value range 0-5.")
parser.add_option("-v", "--verbose", action="store_true",
help="If set, verbose information output is printed: ccch configuration, cell ARFCN's, neighbour ARFCN's")
parser.add_option("-d", "--debug", action="store_true",
help="Print additional debug messages")
"""
Dont forget: sudo sysctl kernel.shmmni=32000
@ -399,6 +408,10 @@ def main(options = None):
if options is None:
(options, args) = argument_parser().parse_args()
if options.list_devices:
grgsm.device.print_devices(options.args)
sys.exit(0)
if options.band not in grgsm.arfcn.get_bands():
parser.error("Invalid GSM band\n")
@ -415,7 +428,7 @@ def main(options = None):
print info.get_verbose_info()
print ""
do_scan(options.samp_rate, options.band, options.speed,
options.ppm, options.gain, options.args, prn = printfunc)
options.ppm, options.gain, options.args, prn = printfunc, debug = options.debug)
if __name__ == '__main__':
main()

View File

@ -3,7 +3,8 @@
# GR-GSM based transceiver
#
# (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
# (C) 2016-2019 by Vadim Yanitskiy <axilirator@gmail.com>
# (C) 2017-2018 by Piotr Krysik <ptrkrysik@gmail.com>
#
# All Rights Reserved
#
@ -22,144 +23,134 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import signal
import getopt
import sys
from grgsm.trx import ctrl_if_bb
from grgsm.trx import radio_if
from grgsm.trx import fake_pm
from argparse import ArgumentParser
from argparse import ArgumentTypeError
from gnuradio import eng_notation
from grgsm.trx import RadioInterface
from grgsm.trx import Transceiver
COPYRIGHT = \
"Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
"Copyright (C) 2016-2018 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
"Copyright (C) 2017-2018 by Piotr Krysik <ptrkrysik@gmail.com>\n" \
"License GPLv2+: GNU GPL version 2 or later " \
"<http://gnu.org/licenses/gpl.html>\n" \
"This is free software: you are free to change and redistribute it.\n" \
"There is NO WARRANTY, to the extent permitted by law.\n"
class Application:
# Application variables
remote_addr = "127.0.0.1"
base_port = 5700
# PHY specific
phy_sample_rate = 4 * 1625000 / 6
phy_tx_antenna = "TX/RX"
phy_rx_antenna = "RX2"
phy_rx_gain = 30
phy_tx_gain = 10
phy_args = ""
phy_ppm = 0
def __init__(self):
self.print_copyright()
self.parse_argv()
def __init__(self, argv):
self.remote_addr = argv.remote_addr
self.bind_addr = argv.bind_addr
self.base_port = argv.base_port
self.phy_args = argv.args
self.phy_sample_rate = argv.sample_rate
self.phy_rx_gain = argv.rx_gain
self.phy_tx_gain = argv.tx_gain
self.phy_rx_antenna = argv.rx_antenna
self.phy_tx_antenna = argv.tx_antenna
self.phy_freq_offset = argv.freq_offset
self.phy_ppm = argv.ppm
# Set up signal handlers
signal.signal(signal.SIGINT, self.sig_handler)
def run(self):
if argv.driver == "uhd":
from grgsm.trx.radio_if_uhd import RadioInterfaceUHD as Radio
elif argv.driver == "lms":
from grgsm.trx.radio_if_lms import RadioInterfaceLMS as Radio
else:
raise ValueError("Unknown RadioInterface driver '%s'" % argv.driver)
# Init Radio interface
self.radio = radio_if(self.phy_args, self.phy_sample_rate,
self.radio = Radio(self.phy_args, self.phy_sample_rate,
self.phy_rx_gain, self.phy_tx_gain, self.phy_ppm,
self.phy_rx_antenna, self.phy_tx_antenna,
self.phy_freq_offset, self.bind_addr,
self.remote_addr, self.base_port)
# Power measurement emulation
# Noise: -120 .. -105
# BTS: -75 .. -50
self.pm = fake_pm(-120, -105, -75, -50)
# Init TRX CTRL interface
self.server = ctrl_if_bb(self.remote_addr,
self.base_port + 101, self.base_port + 1,
self.radio, self.pm)
# Init Transceiver
self.trx = Transceiver(self.bind_addr,
self.remote_addr, self.base_port,
radio_if = self.radio)
print("[i] Init complete")
def run(self):
# Enter main loop
while True:
self.server.loop()
self.trx.ctrl_if.loop()
def shutdown(self):
print("[i] Shutting down...")
self.server.shutdown()
self.radio.shutdown()
def print_copyright(self):
print(COPYRIGHT)
def print_help(self):
s = " Usage: " + sys.argv[0] + " [options]\n\n" \
" Some help...\n" \
" -h --help this text\n\n"
# TRX specific
s += " TRX interface specific\n" \
" -i --remote-addr Set remote address (default 127.0.0.1)\n" \
" -p --base-port Set base port number (default 5700)\n\n"
# PHY specific
s += " Radio interface specific\n" \
" -a --device-args Set device arguments\n" \
" -s --sample-rate Set sample rate\n" \
" -g --rx-gain Set RX gain (default 30)\n" \
" -G --tx-gain Set TX gain (default 10)\n" \
" --rx-antenna Set RX antenna (default RX2)\n" \
" --tx-antenna Set TX antenna (default TX/RX)\n" \
" --ppm Set frequency correction (default 0)\n"
print(s)
def parse_argv(self):
try:
opts, args = getopt.getopt(sys.argv[1:],
"i:p:a:s:g:G:h",
["help", "remote-addr=", "base-port=", "device-args=",
"sample-rate=", "rx-gain=", "tx-gain=", "ppm=",
"rx-antenna=", "tx-antenna="])
except getopt.GetoptError as err:
# Print(help and exit)
self.print_help()
print("[!] " + str(err))
sys.exit(2)
for o, v in opts:
if o in ("-h", "--help"):
self.print_help()
sys.exit(2)
# TRX specific
elif o in ("-i", "--remote-addr"):
self.remote_addr = v
elif o in ("-p", "--base-port"):
if int(v) >= 0 and int(v) <= 65535:
self.base_port = int(v)
else:
print("[!] The port number should be in range [0-65536]")
sys.exit(2)
# PHY specific
elif o in ("-a", "--device-args"):
self.phy_args = v
elif o in ("-s", "--sample-rate"):
self.phy_sample_rate = int(v)
elif o in ("-g", "--rx-gain"):
self.phy_rx_gain = int(v)
elif o in ("-G", "--tx-gain"):
self.phy_tx_gain = int(v)
elif o in ("--rx-antenna"):
self.phy_rx_antenna = v
elif o in ("--tx-antenna"):
self.phy_tx_antenna = v
elif o in ("--ppm"):
self.phy_ppm = int(v)
def sig_handler(self, signum, frame):
print("Signal %d received" % signum)
if signum is signal.SIGINT:
self.shutdown()
sys.exit(0)
def eng_float(value):
try:
return eng_notation.str_to_num(value)
except:
raise ArgumentTypeError("invalid engineering notation "
"value: {0}".format(value))
def parse_argv():
parser = ArgumentParser(prog = "grgsm_trx")
# TRX interface specific
trx_group = parser.add_argument_group("TRX interface")
trx_group.add_argument("-i", "--remote-addr",
dest = "remote_addr", type = str, default = "127.0.0.1",
help = "Set remote address (default %(default)s)")
trx_group.add_argument("-b", "--bind-addr",
dest = "bind_addr", type = str, default = "0.0.0.0",
help = "Set bind address (default %(default)s)")
trx_group.add_argument("-p", "--base_port",
dest = "base_port", type = int, default = 6700,
help = "Set base port number (default %(default)s)")
# PHY specific
phy_group = parser.add_argument_group("PHY parameters")
phy_group.add_argument("--driver",
dest = "driver", type = str, default = "uhd",
choices = ["uhd", "lms"],
help = "Set device driver (default %(default)s)")
phy_group.add_argument("-a", "--args",
dest = "args", type = str, default = "",
help = "Set device arguments")
phy_group.add_argument("-s", "--sample-rate",
dest = "sample_rate", type = eng_float,
default = RadioInterface.SAMPLE_RATE,
help = "Set samp_rate (default %(default)s)")
phy_group.add_argument("-g", "--rx-gain",
dest = "rx_gain", type = float, default = 30,
help = "Set RX gain (default %(default)s)")
phy_group.add_argument("-G", "--tx-gain",
dest = "tx_gain", type = float, default = 10,
help = "Set TX gain (default %(default)s)")
phy_group.add_argument("--rx-antenna",
dest = "rx_antenna", type = str, default = "RX2",
help = "Set RX antenna (default %(default)s)")
phy_group.add_argument("--tx-antenna",
dest = "tx_antenna", type = str, default = "TX/RX",
help = "Set TX antenna (default %(default)s)")
phy_group.add_argument("--freq-offset",
dest = "freq_offset", type = eng_float, default = 0,
help = "Shift baseband freq. (e.g. -500M)")
phy_group.add_argument("--ppm",
dest = "ppm", type = float, default = 0,
help = "Set frequency correction (default %(default)s)")
return parser.parse_args()
if __name__ == '__main__':
app = Application()
print(COPYRIGHT)
argv = parse_argv()
app = Application(argv)
app.run()

View File

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
# @file
# @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
# (C) 2019 by Piotr Krysik <ptrkrysik@gmail.com>
# @section LICENSE
#
# Gr-gsm is free software; you can redistribute it and/or modify
@ -26,217 +27,170 @@ from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filter import firdes
from math import pi
from optparse import OptionParser
import grgsm
from optparse import OptionParser, OptionGroup
import osmosdr
import pmt
import time
import grgsm
import signal
import sys
class grgsm_capture(gr.top_block):
def __init__(self, fc, gain, samp_rate, ppm, arfcn, cfile=None, burst_file=None, verbose=False, rec_length=None, args=""):
def __init__(self,
freq,
gain=30,
samp_rate=1e6,
rec_length=float('Inf'),
freq_corr=0,
output_filename=None,
bandwidth=0,
bb_gain=20,
if_gain=20,
antenna="",
device_args=""):
gr.top_block.__init__(self, "Gr-gsm Capture")
##################################################
# Parameters
##################################################
self.fc = fc
self.gain = gain
self.samp_rate = samp_rate
self.ppm = ppm
self.arfcn = arfcn
self.cfile = cfile
self.burst_file = burst_file
self.verbose = verbose
self.shiftoff = shiftoff = 400e3
self.rec_length = rec_length
##################################################
# Processing Blocks
# Setting up RF source
##################################################
self.rtlsdr_source = osmosdr.source( args="numchan=" + str(1) + " " + args )
self.rtlsdr_source.set_sample_rate(samp_rate)
self.rtlsdr_source.set_center_freq(fc - shiftoff, 0)
self.rtlsdr_source.set_freq_corr(ppm, 0)
self.rtlsdr_source.set_dc_offset_mode(2, 0)
self.rtlsdr_source.set_iq_balance_mode(2, 0)
self.rtlsdr_source.set_gain_mode(True, 0)
self.rtlsdr_source.set_gain(gain, 0)
self.rtlsdr_source.set_if_gain(20, 0)
self.rtlsdr_source.set_bb_gain(20, 0)
self.rtlsdr_source.set_antenna("", 0)
self.rtlsdr_source.set_bandwidth(250e3+abs(shiftoff), 0)
self.blocks_rotator = blocks.rotator_cc(-2*pi*shiftoff/samp_rate)
if self.rec_length is not None:
self.blocks_head_0 = blocks.head(gr.sizeof_gr_complex, int(samp_rate*rec_length))
if self.verbose or self.burst_file:
self.gsm_receiver = grgsm.receiver(4, ([self.arfcn]), ([]))
self.gsm_input = grgsm.gsm_input(
ppm=0,
osr=4,
fc=fc,
samp_rate_in=samp_rate,
)
self.gsm_clock_offset_control = grgsm.clock_offset_control(fc-shiftoff, samp_rate, osr=4)
self.sdr_source = osmosdr.source(args="numchan=" + str(1) + " " +
str(grgsm.device.get_default_args(device_args)))
if self.burst_file:
self.gsm_burst_file_sink = grgsm.burst_file_sink(self.burst_file)
self.sdr_source.set_sample_rate(samp_rate)
self.sdr_source.set_center_freq(freq, 0)
self.sdr_source.set_freq_corr(freq_corr, 0)
self.sdr_source.set_dc_offset_mode(2, 0)
self.sdr_source.set_iq_balance_mode(2, 0)
self.sdr_source.set_gain_mode(True, 0)
self.sdr_source.set_gain(gain, 0)
self.sdr_source.set_if_gain(if_gain, 0)
self.sdr_source.set_bb_gain(bb_gain, 0)
self.sdr_source.set_antenna("", 0)
if bandwidth != 0:
self.sdr_source.set_bandwidth(bandwidth, 0)
if self.cfile:
self.blocks_file_sink = blocks.file_sink(gr.sizeof_gr_complex*1, self.cfile, False)
self.blocks_file_sink.set_unbuffered(False)
##################################################
# The rest of processing blocks
##################################################
if self.verbose:
self.gsm_bursts_printer_0 = grgsm.bursts_printer(pmt.intern(""),
False, False, False, False)
if rec_length != float('Inf'):
self.head = \
blocks.head(gr.sizeof_gr_complex, int(samp_rate*rec_length))
self.file_sink = blocks.file_sink(gr.sizeof_gr_complex*1, \
output_filename, False)
self.file_sink.set_unbuffered(False)
##################################################
# Connections
##################################################
if self.rec_length is not None: #if recording length is defined connect head block after the source
self.connect((self.rtlsdr_source, 0), (self.blocks_head_0, 0))
self.connect((self.blocks_head_0, 0), (self.blocks_rotator, 0))
if rec_length != float('Inf'): #if recording length is not infinite
#connect head block after the source
self.connect((self.sdr_source, 0), (self.head, 0))
self.connect((self.head, 0), (self.file_sink, 0))
else:
self.connect((self.rtlsdr_source, 0), (self.blocks_rotator, 0))
if self.cfile:
self.connect((self.blocks_rotator, 0), (self.blocks_file_sink, 0))
if self.verbose or self.burst_file:
self.connect((self.gsm_input, 0), (self.gsm_receiver, 0))
self.connect((self.blocks_rotator, 0), (self.gsm_input, 0))
self.msg_connect(self.gsm_clock_offset_control, "ctrl", self.gsm_input, "ctrl_in")
self.msg_connect(self.gsm_receiver, "measurements", self.gsm_clock_offset_control, "measurements")
if self.burst_file:
self.msg_connect(self.gsm_receiver, "C0", self.gsm_burst_file_sink, "in")
if self.verbose:
self.msg_connect(self.gsm_receiver, "C0", self.gsm_bursts_printer_0, "bursts")
def get_fc(self):
return self.fc
def set_fc(self, fc):
self.fc = fc
if self.verbose or self.burst_file:
self.gsm_input.set_fc(self.fc)
def get_arfcn(self):
return self.arfcn
def set_arfcn(self, arfcn):
self.arfcn = arfcn
if self.verbose or self.burst_file:
self.gsm_receiver.set_cell_allocation([self.arfcn])
new_freq = grgsm.arfcn.arfcn2downlink(self.arfcn)
self.set_fc(new_freq)
def get_gain(self):
return self.gain
def set_gain(self, gain):
self.gain = gain
def get_samp_rate(self):
return self.samp_rate
def set_samp_rate(self, samp_rate):
self.samp_rate = samp_rate
self.rtlsdr_source.set_sample_rate(self.samp_rate)
if self.verbose or self.burst_file:
self.gsm_input.set_samp_rate_in(self.samp_rate)
def get_ppm(self):
return self.ppm
def set_ppm(self, ppm):
self.ppm = ppm
self.set_ppm_slider(self.ppm)
def get_rec_length(self):
return self.rec_length
def set_rec_length(self, rec_length):
self.rec_length = rec_length
self.blocks_head_0.set_length(int(self.samp_rate*self.rec_length))
self.connect((self.sdr_source, 0), (self.file_sink, 0))
if __name__ == '__main__':
parser = OptionParser(option_class=eng_option, usage="%prog [options]",
parser = OptionParser(option_class=eng_option, usage="%prog [options] output_filename",
description="RTL-SDR capturing app of gr-gsm.")
parser.add_option("-f", "--fc", dest="fc", type="eng_float",
parser.add_option("-f", "--freq", dest="freq", type="eng_float",
help="Set frequency [default=%default]")
parser.add_option("-a", "--arfcn", dest="arfcn", type="intx",
help="Set ARFCN instead of frequency (for PCS1900 add 0x8000 (2**15) to the ARFCN number)")
parser.add_option("-g", "--gain", dest="gain", type="eng_float",
parser.add_option("-a", "--arfcn", dest="arfcn", type="intx",
help="Set ARFCN instead of frequency (for PCS1900 add"
"0x8000 (2**15) to the ARFCN number)")
parser.add_option("-g", "--gain", dest="gain", type="eng_float",
default=eng_notation.num_to_str(30),
help="Set gain [default=%default]")
parser.add_option("-s", "--samp-rate", dest="samp_rate", type="eng_float",
parser.add_option("-s", "--samp-rate", dest="samp_rate", type="eng_float",
default=eng_notation.num_to_str(1000000),
help="Set samp_rate [default=%default]")
parser.add_option("-p", "--ppm", dest="ppm", type="intx", default=0,
help="Set ppm [default=%default]")
parser.add_option("-b", "--burst-file", dest="burst_file",
help="File where the captured bursts are saved")
parser.add_option("-c", "--cfile", dest="cfile",
help="File where the captured data are saved")
parser.add_option("", "--args", dest="args", type="string", default="",
help="Set device arguments [default=%default]")
parser.add_option("-v", "--verbose", action="store_true",
help="If set, the captured bursts are printed to stdout")
parser.add_option("-T", "--rec-length", dest="rec_length", type="float",
default='Inf', help="Set length of recording in seconds "
"[default=infinity]")
parser.add_option("-T", "--rec-length", dest="rec_length", type="eng_float",
help="Set length of recording in seconds [default=%default]")
parser.add_option("-p", "--freq-corr", dest="freq_corr", type="eng_float",
default="0", help="Set frequency correction in"
" ppm [default=%default]")
osmogroup = OptionGroup(parser, 'Additional osmosdr source options',
"Options specific to a subset of SDR receivers "
"supported by osmosdr source.")
osmogroup.add_option("-w", "--bandwidth", dest="bandwidth", type="eng_float",
default="0", help="Set bandwidth [default=samp_rate]")
osmogroup.add_option("", "--bb-gain", dest="bb_gain", type="eng_float",
default=eng_notation.num_to_str(20),
help="Set baseband gain [default=%default]")
osmogroup.add_option("", "--if-gain", dest="if_gain", type="eng_float",
default=eng_notation.num_to_str(20),
help="Set intermediate freque gain [default=%default]")
osmogroup.add_option("", "--ant", dest="antenna", type="string",
default="", help="Set antenna "
"[default=%default]")
osmogroup.add_option("", "--args", dest="device_args", type="string",
default="", help="Set device arguments "
"[default=%default]. Use --list-devices the view the available devices")
osmogroup.add_option("-l", "--list-devices", action="store_true",
help="List available SDR devices, use --args to specify hints")
parser.add_option_group(osmogroup)
(options, args) = parser.parse_args()
if options.cfile is None and options.burst_file is None:
parser.error("Please provide a cfile or a burst file (or both) to save the captured data\n")
if (options.fc is None and options.arfcn is None) or (options.fc is not None and options.arfcn is not None):
parser.error("You have to provide either a frequency or an ARFCN (but not both).\n")
if options.list_devices:
grgsm.device.print_devices(options.device_args)
sys.exit(0)
if not args:
parser.error("Please provide an output file name to save the captured data\n")
output_filename = args[0]
if (options.freq is None and options.arfcn is None) or \
(options.freq is not None and options.arfcn is not None):
parser.error("You have to provide either a frequency or"
"an ARFCN (but not both).\n")
arfcn = 0
fc = 939.4e6
freq = 0
if options.arfcn:
if not grgsm.arfcn.is_valid_arfcn(options.arfcn):
parser.error("ARFCN is not valid\n")
else:
arfcn = options.arfcn
fc = grgsm.arfcn.arfcn2downlink(arfcn)
elif options.fc:
fc = options.fc
arfcn = grgsm.arfcn.downlink2arfcn(options.fc)
tb = grgsm_capture(fc=fc, gain=options.gain, samp_rate=options.samp_rate,
ppm=options.ppm, arfcn=arfcn, cfile=options.cfile,
burst_file=options.burst_file, verbose=options.verbose,
rec_length=options.rec_length, args=options.args)
freq = grgsm.arfcn.arfcn2downlink(options.arfcn)
elif options.freq:
freq = options.freq
tb = grgsm_capture(freq=freq,
gain=options.gain,
freq_corr=options.freq_corr,
samp_rate=options.samp_rate,
output_filename=output_filename,
rec_length=options.rec_length,
bandwidth=options.bandwidth,
bb_gain=options.bb_gain,
if_gain=options.if_gain,
antenna=options.antenna,
device_args=options.device_args)
def signal_handler(signal, frame):
tb.stop()
tb.wait()
tb.wait()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
tb.start()

View File

@ -79,6 +79,10 @@ class grgsm_channelize(gr.top_block):
self.blocks_file_source = blocks.file_source(gr.sizeof_short, input_file, False)
self.source = blocks.interleaved_short_to_complex(False, False)
self.connect((self.blocks_file_source, 0), (self.source, 0))
elif data_type == "ichar":
self.blocks_file_source = blocks.file_source(gr.sizeof_char, input_file, False)
self.source = blocks.interleaved_char_to_complex(False)
self.connect((self.blocks_file_source, 0), (self.source, 0))
elif data_type == "complex":
self.source = blocks.file_source(gr.sizeof_gr_complex, input_file, False)
@ -114,7 +118,7 @@ if __name__ == '__main__':
help="Sample rate of the output capture files [default=%(default)s]")
parser.add_argument("-i", "--input_file", dest="input_file", type=str, required=True,
help="Path to wideband GSM capture file")
parser.add_argument("-t", "--data_type", dest="data_type", type=str, choices=["complex","ishort"], default="complex",
parser.add_argument("-t", "--data_type", dest="data_type", type=str, choices=["complex","ishort","ichar"], default="complex",
help="Type of the input file [default=%(default)s]")
parser.add_argument("-d", "--dest_dir", dest="dest_dir", type=str,
help="Destination directory - if not given defaults to input file name without extension")

View File

@ -1,7 +1,6 @@
#find_package(PkgConfig)
INCLUDE(FindPkgConfig)
pkg_check_modules(PC_libosmocore libosmocore)
pkg_check_modules(PC_libosmogsm libosmogsm)
set(LIBOSMOCORE_DEFINITIONS ${PC_LIBOSMOCORE_CFLAGS_OTHER})
find_path(
@ -25,18 +24,8 @@ find_library(
/usr/lib
)
find_library(
LIBOSMOCORE_GSM_LIBRARY
NAMES libosmogsm osmogsm
HINTS ${PC_libosmocore_LIBDIR}
${PC_libosmocore_LIBRARY_DIRS}
${CMAKE_INSTALL_PREFIX}/lib/
${CMAKE_INSTALL_PREFIX}/lib64/
PATHS /usr/local/lib
/usr/lib
)
set(LIBOSMOCORE_LIBRARIES ${LIBOSMOCORE_LIBRARY} ${LIBOSMOCORE_GSM_LIBRARY})
set(LIBOSMOCORE_LIBRARIES ${LIBOSMOCORE_LIBRARY})
set(LIBOSMOCORE_INCLUDE_DIRS ${LIBOSMOCORE_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)

View File

@ -0,0 +1,32 @@
INCLUDE(FindPkgConfig)
pkg_check_modules(PC_libosmogsm libosmogsm)
set(LIBOSMOGSM_DEFINITIONS ${PC_LIBOSMOGSM_CFLAGS_OTHER})
find_path(
LIBOSMOGSM_INCLUDE_DIR
NAMES osmocom/gsm/gsm_utils.h
HINTS ${PC_libosmogsm_INCLUDEDIR}
${PC_libosmogsm_INCLUDE_DIRS}
${CMAKE_INSTALL_PREFIX}/include
PATHS /usr/local/include
/usr/include
)
find_library(
LIBOSMOGSM_LIBRARY
NAMES libosmogsm osmogsm
HINTS ${PC_libosmogsm_LIBDIR}
${PC_libosmogsm_LIBRARY_DIRS}
${CMAKE_INSTALL_PREFIX}/lib/
${CMAKE_INSTALL_PREFIX}/lib64/
PATHS /usr/local/lib
/usr/lib
)
set(LIBOSMOGSM_LIBRARIES ${LIBOSMOGSM_LIBRARY})
set(LIBOSMOGSM_INCLUDE_DIRS ${LIBOSMOGSM_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(libosmogsm DEFAULT_MSG LIBOSMOGSM_LIBRARY LIBOSMOGSM_INCLUDE_DIR)
mark_as_advanced(LIBOSMOGSM_INCLUDE_DIR LIBOSMOGSM_LIBRARY )

View File

@ -0,0 +1,73 @@
# Author (C) 2018 by Piotr Krysik <ptrkrysik@gmail.com>
# Author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
#
# 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
SET(PYTHONPATH
${CMAKE_SOURCE_DIR}/python
${CMAKE_SOURCE_DIR}/python/misc_utils
${CMAKE_SOURCE_DIR}/python/demapping
${CMAKE_SOURCE_DIR}/python/receiver
${CMAKE_SOURCE_DIR}/python/transmitter
${CMAKE_SOURCE_DIR}/python/trx
${CMAKE_BINARY_DIR}/swig
$ENV{PYTHONPATH}
)
string(REPLACE ";" ":" PYTHONPATH "${PYTHONPATH}")
macro(GRCC_COMPILE file_name)
if(${CMAKE_VERSION} VERSION_LESS "3.2.0") #use wrapper script to set the environment on systems without cmake 3.2
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${file_name}
COMMAND /bin/sh ${CMAKE_SOURCE_DIR}/cmake/Modules/GrccCompileWrapper.sh "${PYTHONPATH}" "${CMAKE_SOURCE_DIR}/grc" "${PC_GNURADIO_RUNTIME_PREFIX}/${GR_RUNTIME_DIR}/grcc -d ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${file_name}.grc"
COMMAND "${CMAKE_COMMAND}" -E rename ${CMAKE_CURRENT_BINARY_DIR}/${file_name}.py ${CMAKE_CURRENT_BINARY_DIR}/${file_name}
DEPENDS ${file_name}.grc
)
else() #for the rest use new/more portable way
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${file_name}
COMMAND "${CMAKE_COMMAND}"
-E env PYTHONPATH="${PYTHONPATH}" GRC_BLOCKS_PATH=${CMAKE_SOURCE_DIR}/grc
${PC_GNURADIO_RUNTIME_PREFIX}/${GR_RUNTIME_DIR}/grcc -d ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/${file_name}.grc
COMMAND "${CMAKE_COMMAND}" -E rename ${CMAKE_CURRENT_BINARY_DIR}/${file_name}.py ${CMAKE_CURRENT_BINARY_DIR}/${file_name}
DEPENDS ${file_name}.grc
)
endif()
endmacro(GRCC_COMPILE)
########################################################################
# Override the GR_UNIQUE_TARGET function to not append a hash
# to the `target` name, because we need a known name in order
# to add an explicit dependency that's needed for the parallel build
#
# The original code segment (taken from GrPython.cmake) is
#
# execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib
#unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5]
#print(re.sub('\\W', '_', '${desc} ${reldir} ' + unique))"
# OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE)
#
########################################################################
function(GR_UNIQUE_TARGET desc)
file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR})
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib
print(re.sub('\\W', '_', '${desc} ${reldir}'))"
OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE)
add_custom_target(${_target} ALL DEPENDS ${ARGN})
endfunction(GR_UNIQUE_TARGET)

View File

@ -0,0 +1,3 @@
export PYTHONPATH="$1"
export GRC_BLOCKS_PATH="$2"
eval "$3"

305
cmake/Modules/UseSWIG.cmake Normal file
View File

@ -0,0 +1,305 @@
# - SWIG module for CMake
# Defines the following macros:
# SWIG_ADD_MODULE(name language [ files ])
# - Define swig module with given name and specified language
# SWIG_LINK_LIBRARIES(name [ libraries ])
# - Link libraries to swig module
# All other macros are for internal use only.
# To get the actual name of the swig module,
# use: ${SWIG_MODULE_${name}_REAL_NAME}.
# Set Source files properties such as CPLUSPLUS and SWIG_FLAGS to specify
# special behavior of SWIG. Also global CMAKE_SWIG_FLAGS can be used to add
# special flags to all swig calls.
# Another special variable is CMAKE_SWIG_OUTDIR, it allows one to specify
# where to write all the swig generated module (swig -outdir option)
# The name-specific variable SWIG_MODULE_<name>_EXTRA_DEPS may be used
# to specify extra dependencies for the generated modules.
# If the source file generated by swig need some special flag you can use
# set_source_files_properties( ${swig_generated_file_fullname}
# PROPERTIES COMPILE_FLAGS "-bla")
#=============================================================================
# Copyright 2004-2009 Kitware, Inc.
# Copyright 2009 Mathieu Malaterre <mathieu.malaterre@gmail.com>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
set(SWIG_CXX_EXTENSION "cxx")
set(SWIG_EXTRA_LIBRARIES "")
set(SWIG_PYTHON_EXTRA_FILE_EXTENSION "py")
#
# For given swig module initialize variables associated with it
#
macro(SWIG_MODULE_INITIALIZE name language)
string(TOUPPER "${language}" swig_uppercase_language)
string(TOLOWER "${language}" swig_lowercase_language)
set(SWIG_MODULE_${name}_LANGUAGE "${swig_uppercase_language}")
set(SWIG_MODULE_${name}_SWIG_LANGUAGE_FLAG "${swig_lowercase_language}")
set(SWIG_MODULE_${name}_REAL_NAME "${name}")
if("${SWIG_MODULE_${name}_LANGUAGE}" STREQUAL "UNKNOWN")
message(FATAL_ERROR "SWIG Error: Language \"${language}\" not found")
elseif("${SWIG_MODULE_${name}_LANGUAGE}" STREQUAL "PYTHON")
# when swig is used without the -interface it will produce in the module.py
# a 'import _modulename' statement, which implies having a corresponding
# _modulename.so (*NIX), _modulename.pyd (Win32).
set(SWIG_MODULE_${name}_REAL_NAME "_${name}")
elseif("${SWIG_MODULE_${name}_LANGUAGE}" STREQUAL "PERL")
set(SWIG_MODULE_${name}_EXTRA_FLAGS "-shadow")
endif()
endmacro()
#
# For a given language, input file, and output file, determine extra files that
# will be generated. This is internal swig macro.
#
macro(SWIG_GET_EXTRA_OUTPUT_FILES language outfiles generatedpath infile)
set(${outfiles} "")
get_source_file_property(SWIG_GET_EXTRA_OUTPUT_FILES_module_basename
${infile} SWIG_MODULE_NAME)
if(SWIG_GET_EXTRA_OUTPUT_FILES_module_basename STREQUAL "NOTFOUND")
get_filename_component(SWIG_GET_EXTRA_OUTPUT_FILES_module_basename "${infile}" NAME_WE)
endif()
foreach(it ${SWIG_${language}_EXTRA_FILE_EXTENSION})
set(${outfiles} ${${outfiles}}
"${generatedpath}/${SWIG_GET_EXTRA_OUTPUT_FILES_module_basename}.${it}")
endforeach()
endmacro()
#
# Take swig (*.i) file and add proper custom commands for it
#
macro(SWIG_ADD_SOURCE_TO_MODULE name outfiles infile)
set(swig_full_infile ${infile})
get_filename_component(swig_source_file_path "${infile}" PATH)
get_filename_component(swig_source_file_name_we "${infile}" NAME_WE)
get_source_file_property(swig_source_file_generated ${infile} GENERATED)
get_source_file_property(swig_source_file_cplusplus ${infile} CPLUSPLUS)
get_source_file_property(swig_source_file_flags ${infile} SWIG_FLAGS)
if("${swig_source_file_flags}" STREQUAL "NOTFOUND")
set(swig_source_file_flags "")
endif()
set(swig_source_file_fullname "${infile}")
if(${swig_source_file_path} MATCHES "^${CMAKE_CURRENT_SOURCE_DIR}")
string(REGEX REPLACE
"^${CMAKE_CURRENT_SOURCE_DIR}" ""
swig_source_file_relative_path
"${swig_source_file_path}")
else()
if(${swig_source_file_path} MATCHES "^${CMAKE_CURRENT_BINARY_DIR}")
string(REGEX REPLACE
"^${CMAKE_CURRENT_BINARY_DIR}" ""
swig_source_file_relative_path
"${swig_source_file_path}")
set(swig_source_file_generated 1)
else()
set(swig_source_file_relative_path "${swig_source_file_path}")
if(swig_source_file_generated)
set(swig_source_file_fullname "${CMAKE_CURRENT_BINARY_DIR}/${infile}")
else()
set(swig_source_file_fullname "${CMAKE_CURRENT_SOURCE_DIR}/${infile}")
endif()
endif()
endif()
set(swig_generated_file_fullname
"${CMAKE_CURRENT_BINARY_DIR}")
if(swig_source_file_relative_path)
set(swig_generated_file_fullname
"${swig_generated_file_fullname}/${swig_source_file_relative_path}")
endif()
# If CMAKE_SWIG_OUTDIR was specified then pass it to -outdir
if(CMAKE_SWIG_OUTDIR)
set(swig_outdir ${CMAKE_SWIG_OUTDIR})
else()
set(swig_outdir ${CMAKE_CURRENT_BINARY_DIR})
endif()
SWIG_GET_EXTRA_OUTPUT_FILES(${SWIG_MODULE_${name}_LANGUAGE}
swig_extra_generated_files
"${swig_outdir}"
"${infile}")
set(swig_generated_file_fullname
"${swig_generated_file_fullname}/${swig_source_file_name_we}")
# add the language into the name of the file (i.e. TCL_wrap)
# this allows for the same .i file to be wrapped into different languages
set(swig_generated_file_fullname
"${swig_generated_file_fullname}${SWIG_MODULE_${name}_LANGUAGE}_wrap")
if(swig_source_file_cplusplus)
set(swig_generated_file_fullname
"${swig_generated_file_fullname}.${SWIG_CXX_EXTENSION}")
else()
set(swig_generated_file_fullname
"${swig_generated_file_fullname}.c")
endif()
# Shut up some warnings from poor SWIG code generation that we
# can do nothing about, when this flag is available
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-Wno-unused-but-set-variable" HAVE_WNO_UNUSED_BUT_SET_VARIABLE)
if(HAVE_WNO_UNUSED_BUT_SET_VARIABLE)
set_source_files_properties(${swig_generated_file_fullname}
PROPERTIES COMPILE_FLAGS "-Wno-unused-but-set-variable")
endif(HAVE_WNO_UNUSED_BUT_SET_VARIABLE)
get_directory_property(cmake_include_directories INCLUDE_DIRECTORIES)
list(REMOVE_DUPLICATES cmake_include_directories)
set(swig_include_dirs)
foreach(it ${cmake_include_directories})
set(swig_include_dirs ${swig_include_dirs} "-I${it}")
endforeach()
set(swig_special_flags)
# default is c, so add c++ flag if it is c++
if(swig_source_file_cplusplus)
set(swig_special_flags ${swig_special_flags} "-c++")
endif()
set(swig_extra_flags)
if(SWIG_MODULE_${name}_EXTRA_FLAGS)
set(swig_extra_flags ${swig_extra_flags} ${SWIG_MODULE_${name}_EXTRA_FLAGS})
endif()
# hack to work around CMake bug in add_custom_command with multiple OUTPUT files
file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR})
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib
unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5]
print(re.sub('\\W', '_', '${name} ${reldir} ' + unique))"
OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE
)
file(
WRITE ${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp.in
"int main(void){return 0;}\n"
)
# create dummy dependencies
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp.in
${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp
DEPENDS "${swig_source_file_fullname}" ${SWIG_MODULE_${name}_EXTRA_DEPS}
COMMENT ""
)
# create the dummy target
add_executable(${_target} ${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp)
# add a custom command to the dummy target
add_custom_command(
TARGET ${_target}
# Let's create the ${swig_outdir} at execution time, in case dir contains $(OutDir)
COMMAND ${CMAKE_COMMAND} -E make_directory ${swig_outdir}
COMMAND "${SWIG_EXECUTABLE}"
ARGS "-${SWIG_MODULE_${name}_SWIG_LANGUAGE_FLAG}"
${swig_source_file_flags}
${CMAKE_SWIG_FLAGS}
-outdir ${swig_outdir}
${swig_special_flags}
${swig_extra_flags}
${swig_include_dirs}
-o "${swig_generated_file_fullname}"
"${swig_source_file_fullname}"
COMMENT "Swig source"
)
#add dummy independent dependencies from the _target to each file
#that will be generated by the SWIG command above
set(${outfiles} "${swig_generated_file_fullname}" ${swig_extra_generated_files})
foreach(swig_gen_file ${${outfiles}})
add_custom_command(
OUTPUT ${swig_gen_file}
COMMAND "${CMAKE_COMMAND}" -E touch_nocreate "${swig_gen_file}"
DEPENDS ${_target}
COMMENT "dummy command to show ${_target} dependency of ${swig_gen_file}"
)
endforeach()
set_source_files_properties(
${outfiles} PROPERTIES GENERATED 1
)
endmacro()
#
# Create Swig module
#
macro(SWIG_ADD_MODULE name language)
SWIG_MODULE_INITIALIZE(${name} ${language})
set(swig_dot_i_sources)
set(swig_other_sources)
foreach(it ${ARGN})
if(${it} MATCHES ".*\\.i$")
set(swig_dot_i_sources ${swig_dot_i_sources} "${it}")
else()
set(swig_other_sources ${swig_other_sources} "${it}")
endif()
endforeach()
set(swig_generated_sources)
foreach(it ${swig_dot_i_sources})
SWIG_ADD_SOURCE_TO_MODULE(${name} swig_generated_source ${it})
set(swig_generated_sources ${swig_generated_sources} "${swig_generated_source}")
endforeach()
get_directory_property(swig_extra_clean_files ADDITIONAL_MAKE_CLEAN_FILES)
set_directory_properties(PROPERTIES
ADDITIONAL_MAKE_CLEAN_FILES "${swig_extra_clean_files};${swig_generated_sources}")
add_library(${SWIG_MODULE_${name}_REAL_NAME}
MODULE
${swig_generated_sources}
${swig_other_sources})
string(TOLOWER "${language}" swig_lowercase_language)
if ("${swig_lowercase_language}" STREQUAL "java")
if (APPLE)
# In java you want:
# System.loadLibrary("LIBRARY");
# then JNI will look for a library whose name is platform dependent, namely
# MacOS : libLIBRARY.jnilib
# Windows: LIBRARY.dll
# Linux : libLIBRARY.so
set_target_properties (${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES SUFFIX ".jnilib")
endif ()
endif ()
if ("${swig_lowercase_language}" STREQUAL "python")
# this is only needed for the python case where a _modulename.so is generated
set_target_properties(${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES PREFIX "")
# Python extension modules on Windows must have the extension ".pyd"
# instead of ".dll" as of Python 2.5. Older python versions do support
# this suffix.
# http://docs.python.org/whatsnew/ports.html#SECTION0001510000000000000000
# <quote>
# Windows: .dll is no longer supported as a filename extension for extension modules.
# .pyd is now the only filename extension that will be searched for.
# </quote>
if(WIN32 AND NOT CYGWIN)
set_target_properties(${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES SUFFIX ".pyd")
endif()
endif ()
endmacro()
#
# Like TARGET_LINK_LIBRARIES but for swig modules
#
macro(SWIG_LINK_LIBRARIES name)
if(SWIG_MODULE_${name}_REAL_NAME)
target_link_libraries(${SWIG_MODULE_${name}_REAL_NAME} ${ARGN})
else()
message(SEND_ERROR "Cannot find Swig library \"${name}\".")
endif()
endmacro()

View File

@ -24,6 +24,7 @@ add_subdirectory(receiver)
add_subdirectory(flow_control)
add_subdirectory(misc_utils)
add_subdirectory(transmitter)
add_subdirectory(trx)
install(FILES
gsm_block_tree.xml DESTINATION share/gnuradio/grc/blocks
)

View File

@ -19,5 +19,7 @@
install(FILES
gsm_control_channels_decoder.xml
gsm_tch_f_decoder.xml DESTINATION share/gnuradio/grc/blocks
gsm_tch_f_decoder.xml
gsm_tch_h_decoder.xml
DESTINATION share/gnuradio/grc/blocks
)

View File

@ -0,0 +1,70 @@
<?xml version="1.0"?>
<block>
<name>TCH/H decoder</name>
<key>gsm_tch_h_decoder</key>
<import>import grgsm</import>
<make>grgsm.tch_h_decoder($sub_channel, $multi_rate, $boundary_check)</make>
<param>
<name>Sub-channel number</name>
<key>sub_channel</key>
<value>0</value>
<type>int</type>
<hide>none</hide>
<option>
<name>0</name>
<key>0</key>
</option>
<option>
<name>1</name>
<key>1</key>
</option>
</param>
<param>
<name>MultiRate configuration</name>
<key>multi_rate</key>
<type>string</type>
</param>
<param>
<name>Voice boundary detection</name>
<key>boundary_check</key>
<value>False</value>
<type>bool</type>
<option>
<name>False</name>
<key>False</key>
</option>
<option>
<name>True</name>
<key>True</key>
</option>
</param>
<check>$sub_channel() &gt; -1 and $sub_channel() &lt; 2</check>
<sink>
<name>bursts</name>
<type>message</type>
</sink>
<source>
<name>msgs</name>
<type>message</type>
<optional>1</optional>
</source>
<source>
<name>voice</name>
<type>message</type>
<optional>1</optional>
</source>
<doc>
The MultiRate configuration string should contains the hex string from the
MultiRate configuration element from the Assignment Command message.
Example: 28111a40.
See 3GPP TS 44.018 - 10.5.2.21aa MultiRate configuratio
If "Voice boundary detection" is enabled, then only bursts are decoded as voice where
- the framenumber is greater then the framenumber of a received "Connect" or "Connect Acknowlegde" message, and
- the framenumber is less then the framenumber of a "Release" message
</doc>
</block>

View File

@ -22,5 +22,7 @@ install(FILES
gsm_bcch_ccch_demapper.xml
gsm_bcch_ccch_sdcch4_demapper.xml
gsm_sdcch8_demapper.xml
gsm_tch_f_chans_demapper.xml DESTINATION share/gnuradio/grc/blocks
gsm_tch_f_chans_demapper.xml
gsm_tch_h_chans_demapper.xml
DESTINATION share/gnuradio/grc/blocks
)

View File

@ -6,7 +6,7 @@
<make>grgsm.tch_f_chans_demapper($timeslot_nr)</make>
<param>
<name>timeslot_nr</name>
<name>Timeslot</name>
<key>timeslot_nr</key>
<value>2</value>
<type>int</type>

View File

@ -0,0 +1,47 @@
<?xml version="1.0"?>
<block>
<name>TCH/H Demapper</name>
<key>gsm_tch_h_chans_demapper</key>
<import>import grgsm</import>
<make>grgsm.tch_h_chans_demapper($timeslot_nr, $tch_h_channel)</make>
<param>
<name>Timeslot</name>
<key>timeslot_nr</key>
<value>2</value>
<type>int</type>
<hide>none</hide>
</param>
<param>
<name>Sub-channel number</name>
<key>tch_h_channel</key>
<value>0</value>
<type>int</type>
<hide>none</hide>
<option>
<name>0</name>
<key>0</key>
</option>
<option>
<name>1</name>
<key>1</key>
</option>
</param>
<check>$tch_h_channel() &gt; -1 and $tch_h_channel() &lt; 2</check>
<sink>
<name>bursts</name>
<type>message</type>
</sink>
<source>
<name>tch_bursts</name>
<type>message</type>
<optional>1</optional>
</source>
<source>
<name>acch_bursts</name>
<type>message</type>
<optional>1</optional>
</source>
</block>

View File

@ -29,6 +29,12 @@
<block>gsm_preprocess_tx_burst</block>
<block>gsm_gen_test_ab</block>
</cat>
<cat>
<name>Transceiver</name>
<block>gsm_trx_burst_if</block>
<block>gsm_freq_hopper_tag</block>
<block>gsm_freq_hopper_msg</block>
</cat>
<cat>
<name>Logical channels demapping</name>
<block>gsm_universal_ctrl_chans_demapper</block>
@ -36,6 +42,7 @@
<block>gsm_bcch_ccch_sdcch4_demapper</block>
<block>gsm_sdcch8_demapper</block>
<block>gsm_tch_f_chans_demapper</block>
<block>gsm_tch_h_chans_demapper</block>
</cat>
<cat>
<name>Decryption</name>
@ -45,6 +52,7 @@
<name>Decoding</name>
<block>gsm_control_channels_decoder</block>
<block>gsm_tch_f_decoder</block>
<block>gsm_tch_h_decoder</block>
</cat>
<cat>
<name>Flow control</name>
@ -73,9 +81,8 @@
<block>gsm_controlled_fractional_resampler_cc</block>
<block>gsm_message_printer</block>
<block>gsm_clock_offset_corrector_tagged</block>
<block>gsm_msg_to_tag.xml</block>
<block>gsm_msg_to_tag</block>
<block>gsm_tmsi_dumper</block>
<block>gsm_trx_burst_if</block>
<block>gsm_burst_to_fn_time</block>
</cat>
</cat>

View File

@ -32,7 +32,6 @@ install(FILES
gsm_burst_file_source.xml
gsm_message_file_sink.xml
gsm_message_file_source.xml
gsm_trx_burst_if.xml
gsm_msg_to_tag.xml
gsm_controlled_fractional_resampler_cc.xml
gsm_burst_to_fn_time.xml

View File

@ -3,7 +3,7 @@
<name>Burst to FN time</name>
<key>gsm_burst_to_fn_time</key>
<import>import grgsm</import>
<make>grgsm.gsm_burst_to_fn_time()</make>
<make>grgsm.burst_to_fn_time()</make>
<sink>
<name>bursts_in</name>

View File

@ -3,7 +3,7 @@
<name>GSM Receiver</name>
<key>gsm_receiver</key>
<import>import grgsm</import>
<make>grgsm.receiver($osr, $cell_allocation, $tseq_nums, False)</make>
<make>grgsm.receiver($osr, $cell_allocation, $tseq_nums, $resamp_rate, False)</make>
<param>
<name>Oversampling ratio</name>
@ -28,6 +28,14 @@
<hide>part</hide>
</param>
<param>
<name>Resamp rate</name>
<key>resamp_rate</key>
<value>1.0</value>
<type>float</type>
<hide>part</hide>
</param>
<param>
<name>Num Streams</name>
<key>num_streams</key>
@ -36,7 +44,7 @@
<hide>part</hide>
</param>
<check>$num_streams &gt;= 0</check>
<sink>
<name>in</name>
<type>complex</type>

View File

@ -3,7 +3,7 @@
<name>GSM Receiver (with uplink)</name>
<key>gsm_receiver_with_uplink</key>
<import>import grgsm</import>
<make>grgsm.receiver($osr, $cell_allocation, $tseq_nums, True)</make>
<make>grgsm.receiver($osr, $cell_allocation, $tseq_nums, $resamp_rate, True)</make>
<param>
<name>Oversampling ratio</name>
@ -28,6 +28,14 @@
<hide>part</hide>
</param>
<param>
<name>Resamp rate</name>
<key>resamp_rate</key>
<value>1.0</value>
<type>float</type>
<hide>part</hide>
</param>
<param>
<name>Num Streams</name>
<key>num_streams</key>

25
grc/trx/CMakeLists.txt Normal file
View File

@ -0,0 +1,25 @@
# Copyright 2011,2012 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
install(FILES
gsm_trx_burst_if.xml
gsm_freq_hopper_tag.xml
gsm_freq_hopper_msg.xml
DESTINATION share/gnuradio/grc/blocks
)

View File

@ -0,0 +1,81 @@
<?xml version="1.0"?>
<block>
<name>freq_hopper_msg</name>
<key>gsm_freq_hopper_msg</key>
<import>import grgsm</import>
<make>grgsm.freq_hopper_msg($samp_rate, $start_fc_rx, $start_fc_tx, $rx_hopping, $tx_hopping, $freq_change_period)</make>
<!-- $hopping_cmd-->
<!-- <callback>set_fn_time_reference($init_fn, $init_time_secs, $init_time_fracs)</callback>-->
<!-- <param>-->
<!-- <name>Initial hopping command</name>-->
<!-- <key>hopping_cmd</key>-->
<!-- <value>pmt.to_pmt({'cmd': 'start', 'hopping_params': {'hsn': 1, 'maio': 0, 'ma': [1,2,3,4]} })</value>-->
<!-- <type>raw</type>-->
<!-- <hide>part</hide>-->
<!-- </param>-->
<param>
<name>Sample rate</name>
<key>samp_rate</key>
<value>samp_rate</value>
<type>float</type>
<hide>part</hide>
</param>
<param>
<name>start_fc_rx</name>
<key>start_fc_rx</key>
<value>fc</value>
<type>float</type>
<hide>part</hide>
</param>
<param>
<name>start_fc_tx</name>
<key>start_fc_tx</key>
<value>fc</value>
<type>float</type>
<hide>part</hide>
</param>
<param>
<name>rx_hopping</name>
<key>rx_hopping</key>
<value>True</value>
<type>float</type>
<hide>part</hide>
</param>
<param>
<name>tx_hopping</name>
<key>tx_hopping</key>
<value>True</value>
<type>float</type>
<hide>part</hide>
</param>
<param>
<name>freq_change_period</name>
<key>freq_change_period</key>
<value>0.0046</value>
<type>float</type>
<hide>part</hide>
</param>
<sink>
<name>in</name>
<type>complex</type>
</sink>
<!-- <sink>-->
<!-- <name>hopping_cmd</name>-->
<!-- <type>message</type>-->
<!-- <optional>1</optional>-->
<!-- </sink>-->
<source>
<name>control</name>
<type>message</type>
<optional>1</optional>
</source>
</block>

View File

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<block>
<name>freq_hopper_tag</name>
<key>gsm_freq_hopper_tag</key>
<import>import grgsm</import>
<make>grgsm.freq_hopper_tag($hopping_cmd)</make>
<!-- <callback>set_fn_time_reference($init_fn, $init_time_secs, $init_time_fracs)</callback>-->
<param>
<name>Initial hopping command</name>
<key>hopping_cmd</key>
<value>pmt.to_pmt({'cmd': 'start', 'hopping_params': {'hsn': 1, 'maio': 0, 'ma': [1,2,3,4]} })</value>
<type>raw</type>
<hide>part</hide>
</param>
<sink>
<name>hopping_cmd</name>
<type>message</type>
<optional>1</optional>
</sink>
<sink>
<name>bursts_in</name>
<type>message</type>
<optional>1</optional>
</sink>
<source>
<name>bursts_out</name>
<type>message</type>
<optional>1</optional>
</source>
</block>

View File

@ -3,7 +3,7 @@
<name>TRX Burst Interface</name>
<key>gsm_trx_burst_if</key>
<import>import grgsm</import>
<make>grgsm.trx_burst_if($remote_addr, $base_port)</make>
<make>grgsm.trx_burst_if($bind_addr, $remote_addr, $base_port)</make>
<param>
<name>base_port</name>
@ -12,6 +12,13 @@
<type>string</type>
</param>
<param>
<name>bind_addr</name>
<key>bind_addr</key>
<value>0.0.0.0</value>
<type>string</type>
</param>
<param>
<name>remote_addr</name>
<key>remote_addr</key>

View File

@ -37,3 +37,4 @@ add_subdirectory(misc_utils)
add_subdirectory(qa_utils)
add_subdirectory(flow_control)
add_subdirectory(transmitter)
add_subdirectory(trx)

View File

@ -22,5 +22,7 @@
########################################################################
install(FILES
control_channels_decoder.h
tch_f_decoder.h DESTINATION include/grgsm/decoding
tch_f_decoder.h
tch_h_decoder.h
DESTINATION include/grgsm/decoding
)

View File

@ -41,7 +41,8 @@ namespace gr {
TCH_AFS5_15,
TCH_AFS4_75,
TCH_FS,
TCH_EFR
TCH_EFR,
TCH_HS,
};
/*!

View File

@ -0,0 +1,59 @@
/* -*- c++ -*- */
/*
* @file
* @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_GSM_TCH_H_DECODER_H
#define INCLUDED_GSM_TCH_H_DECODER_H
#include <grgsm/api.h>
#include <gnuradio/block.h>
namespace gr {
namespace gsm {
/*!
* \brief <+description of block+>
* \ingroup gsm
*
*/
class GRGSM_API tch_h_decoder : virtual public gr::block
{
public:
typedef boost::shared_ptr<tch_h_decoder> sptr;
/*!
* \brief Return a shared_ptr to a new instance of gsm::tch_h_decoder.
*
* To avoid accidental use of raw pointers, gsm::tch_h_decoder's
* constructor is in a private implementation
* class. gsm::tch_h_decoder::make is the public interface for
* creating new instances.
*/
static sptr make(unsigned int sub_channel, std::string multi_rate, bool boundary_check=false);
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_TCH_H_DECODER_H */

View File

@ -22,5 +22,7 @@
########################################################################
install(FILES
universal_ctrl_chans_demapper.h
tch_f_chans_demapper.h DESTINATION include/grgsm/demapping
tch_f_chans_demapper.h
tch_h_chans_demapper.h
DESTINATION include/grgsm/demapping
)

View File

@ -0,0 +1,59 @@
/* -*- c++ -*- */
/*
* @file
* @author (C) 2018 by Andrew Artyushok <loony.developer@gmail.com>
* @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_GSM_TCH_H_CHANS_DEMAPPER_H
#define INCLUDED_GSM_TCH_H_CHANS_DEMAPPER_H
#include <grgsm/api.h>
#include <gnuradio/block.h>
namespace gr {
namespace gsm {
/*!
* \brief <+description of block+>
* \ingroup gsm
*
*/
class GRGSM_API tch_h_chans_demapper : virtual public gr::block
{
public:
typedef boost::shared_ptr<tch_h_chans_demapper> sptr;
/*!
* \brief Return a shared_ptr to a new instance of gsm::tch_h_chans_demapper.
*
* To avoid accidental use of raw pointers, gsm::tch_h_chans_demapper's
* constructor is in a private implementation
* class. gsm::tch_h_chans_demapper::make is the public interface for
* creating new instances.
*/
static sptr make(unsigned int timeslot_nr, unsigned int tch_h_channel);
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_TCH_H_CHANS_DEMAPPER_H */

View File

@ -30,7 +30,7 @@
#define TAIL_BITS 3
#define GUARD_BITS 8
#define GUARD_FRACTIONAL 0.25 //fractional part of guard period
#define GUARD_PERIOD GUARD_BITS + GUARD_FRACTIONAL
#define GUARD_PERIOD (GUARD_BITS + GUARD_FRACTIONAL)
#define DATA_BITS 57 //size of 1 data block in normal burst
#define STEALING_BIT 1
#define N_TRAIN_BITS 26
@ -39,7 +39,7 @@
#define FCCH_BITS USEFUL_BITS
#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
#define ACCESS_BURST_SIZE 88
#define PROCESSED_CHUNK BURST_SIZE+2*GUARD_PERIOD
#define PROCESSED_CHUNK (BURST_SIZE+2*GUARD_PERIOD)
#define SCH_DATA_LEN 39
#define TS_BITS (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS) //a full TS (156 bits)

View File

@ -35,7 +35,6 @@ install(FILES
message_printer.h
tmsi_dumper.h
msg_to_tag.h
trx_burst_if.h
burst_to_fn_time.h
controlled_fractional_resampler_cc.h
time_spec.h

View File

@ -46,7 +46,7 @@ namespace gr {
typedef std::pair<unsigned long long, double> time_format;
GRGSM_API time_format fn_time_delta_cpp(uint32_t fn_ref, time_format time_ref, uint32_t fn_x,
time_format time_hint, uint32_t ts_num, uint32_t ts_ref);
time_format time_hint, uint32_t ts_num, uint32_t ts_ref, double clock_error=0.0);
} // namespace gsm
} // namespace gr

View File

@ -53,8 +53,9 @@ namespace gr {
public:
udp_socket(
const std::string &remote_addr,
const std::string &bind_addr,
const std::string &src_port,
const std::string &remote_addr,
const std::string &dst_port,
size_t mtu);
~udp_socket();

View File

@ -31,7 +31,7 @@
boost::shared_ptr<Gnuplot> current_figure;
void imagesc(arma::mat & x){
void imagesc(const arma::mat & x){
Gnuplot gp;
gp << "set palette rgb 3,2,2;";
gp << "plot ";
@ -39,7 +39,7 @@ void imagesc(arma::mat & x){
gp << std::endl;
}
void plot(arma::cx_mat & x, std::string title){
void plot(const arma::cx_mat & x, std::string title){
arma::mat y = arma::abs(x);
if(current_figure.get()==NULL){
current_figure = boost::make_shared<Gnuplot>();
@ -50,7 +50,7 @@ void plot(arma::cx_mat & x, std::string title){
(*current_figure) << std::endl;
}
void replot(arma::cx_mat & x, std::string title){
void replot(const arma::cx_mat & x, std::string title){
arma::mat y = arma::abs(x);
if(current_figure.get()==NULL){
current_figure = boost::make_shared<Gnuplot>();
@ -61,19 +61,19 @@ void replot(arma::cx_mat & x, std::string title){
}
template<typename T>
void plot(std::vector<T> & x){
void plot(const std::vector<T> & x){
arma::cx_mat y = arma::conv_to<arma::cx_mat>::from(x);
plot(y,"");
}
template<typename T>
void plot(std::vector<T> & x, std::string title){
void plot(const std::vector<T> & x, std::string title){
arma::cx_mat y = arma::conv_to<arma::cx_mat>::from(x);
plot(y,title);
}
template<typename T>
void replot(std::vector<T> & x, std::string title){
void replot(const std::vector<T> & x, std::string title){
arma::cx_mat y = arma::conv_to<arma::cx_mat>::from(x);
replot(y,title);
}

View File

@ -50,7 +50,13 @@ namespace gr {
* class. gsm::receiver::make is the public interface for
* creating new instances.
*/
static sptr make(int osr, const std::vector<int> &cell_allocation, const std::vector<int> &seq_nums, bool process_uplink=false);
static sptr make(
int osr,
const std::vector<int> &cell_allocation,
const std::vector<int> &tseq_nums,
double resamp_rate=1,
bool process_uplink=false
);
virtual void set_cell_allocation(const std::vector<int> &cell_allocation) = 0;
virtual void set_tseq_nums(const std::vector<int> & tseq_nums) = 0;

View File

@ -0,0 +1,28 @@
# Copyright 2011,2012 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
########################################################################
# Install public header files
########################################################################
install(FILES
freq_hopper_tag.h
freq_hopper_msg.h
trx_burst_if.h
DESTINATION include/grgsm/trx
)

View File

@ -0,0 +1,74 @@
/* -*- c++ -*- */
/* @file
* @author Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef INCLUDED_GSM_FREQ_HOPPER_MSG_H
#define INCLUDED_GSM_FREQ_HOPPER_MSG_H
#include <grgsm/api.h>
#include <gnuradio/sync_block.h>
namespace gr {
namespace gsm {
/*!
* \brief <+description of block+>
* \ingroup gsm
*
*/
class GRGSM_API freq_hopper_msg : virtual public sync_block
{
public:
typedef boost::shared_ptr<freq_hopper_msg> sptr;
static sptr make(
double samp_rate = 1e6,
double start_fc_rx = 1e9,
double start_fc_tx = 1e9,
bool rx_hopping=true,
bool tx_hopping=true,
double freq_change_period=0.0046);
/*!
* hopping_cmd is a pmt dictionary with following fields:
* "cmd": string with type of command, possible values:
* -"start" - immediately start hopping or if there is "fn"
* field defined start from frame number defined
* by that field
* -"stop" - immediately stop hopping or if there is "fn"
* field defined start from frame number defined
* by that field
* -"discard" - discard all queued "start_fn" and "stop_fn"
* (this command doesn't require hopping parameters or frame number)
* "hopping_params": dictionary with hopping parameters in following form:
* {"hsn": hopping_sequence_number(uint64),
* "ma": mobile_allocation(list of arfcns),
* "maio": mobile_allocation_index_offset(uint64),
* "n_arfcn": number_of_arfcns_in_ma}
* "fn": frame number when to start or stop hopping
*/
// virtual void add_hopping_cmd(pmt::pmt_t hopping_cmd=pmt::PMT_NIL) = 0;
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_FREQ_HOPPER_MSG_H */

View File

@ -0,0 +1,67 @@
/* -*- c++ -*- */
/* @file
* @author Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef INCLUDED_GSM_FREQ_HOPPER_TAG_H
#define INCLUDED_GSM_FREQ_HOPPER_TAG_H
#include <grgsm/api.h>
#include <gnuradio/block.h>
namespace gr {
namespace gsm {
/*!
* \brief <+description of block+>
* \ingroup gsm
*
*/
class GRGSM_API freq_hopper_tag : virtual public gr::block
{
public:
typedef boost::shared_ptr<freq_hopper_tag> sptr;
/*!
* hopping_cmd is a pmt dictionary with following fields:
* "cmd": string with type of command, possible values:
* -"start" - immediately start hopping or if there is "fn"
* field defined start from frame number defined
* by that field
* -"stop" - immediately stop hopping or if there is "fn"
* field defined start from frame number defined
* by that field
* -"discard" - discard all queued "start_fn" and "stop_fn"
* (this command doesn't require hopping parameters or frame number)
* "hopping_params": dictionary with hopping parameters in following form:
* {"hsn": hopping_sequence_number(uint64),
* "ma": mobile_allocation(list of arfcns),
* "maio": mobile_allocation_index_offset(uint64),
* "n_arfcn": number_of_arfcns_in_ma}
* "fn": frame number when to start or stop hopping
*/
static sptr make(pmt::pmt_t hopping_cmd);
virtual void add_hopping_cmd(pmt::pmt_t hopping_cmd=pmt::PMT_NIL) = 0;
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_FREQ_HOPPER_TAG_H */

View File

@ -48,6 +48,7 @@ namespace gr {
* creating new instances.
*/
static sptr make(
const std::string &bind_addr,
const std::string &remote_addr,
const std::string &base_port);
};

View File

@ -71,6 +71,7 @@ add_subdirectory(misc_utils)
add_subdirectory(qa_utils)
add_subdirectory(receiver)
add_subdirectory(transmitter)
add_subdirectory(trx)
########################################################################
# Setup library
@ -87,15 +88,13 @@ if(WIN32)
list (APPEND grgsm_link_libraries ws2_32)
endif()
if(LIBOSMOCORE_FOUND AND LIBOSMOCODEC_FOUND)
list (APPEND grgsm_link_libraries ${LIBOSMOCORE_LIBRARIES} ${LIBOSMOCODEC_LIBRARIES})
if(NOT LOCAL_OSMOCOM)
list (APPEND grgsm_link_libraries ${LIBOSMOCORE_LIBRARIES} ${LIBOSMOCODEC_LIBRARIES} ${LIBOSMOGSM_LIBRARY})
if(LIBOSMOCODING_FOUND)
list (APPEND grgsm_link_libraries ${LIBOSMOCODING_LIBRARIES})
endif()
endif()
add_library(grgsm SHARED ${grgsm_sources})
target_link_libraries(grgsm ${grgsm_link_libraries}
# libraries required by plotting.h - have troubles to be installed by pybombs

View File

@ -17,7 +17,7 @@
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
if(NOT LIBOSMOCORE_FOUND OR NOT LIBOSMOCODEC_FOUND)
if(LOCAL_OSMOCOM)
add_subdirectory(osmocom/core)
add_subdirectory(osmocom/codec)
add_subdirectory(osmocom/gsm)
@ -31,6 +31,7 @@ add_subdirectory(openbts)
add_sources(
control_channels_decoder_impl.cc
tch_f_decoder_impl.cc
tch_h_decoder_impl.cc
sch.c
)

View File

@ -2,5 +2,6 @@ add_sources(
a5.c
auth_core.c
gsm48_ie.c
gsm_utils.c
kasumi.c
)

View File

@ -0,0 +1,956 @@
/*! \file gsm_utils.c */
/*
* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
* (C) 2009,2013 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2012 by Nico Golde <nico@ngolde.de>
*
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*! \mainpage libosmogsm Documentation
*
* \section sec_intro Introduction
* This library is a collection of common code used in various
* GSM related sub-projects inside the Osmocom family of projects. It
* includes A5/1 and A5/2 ciphers, COMP128v1, a LAPDm implementation,
* a GSM TLV parser, SMS utility routines as well as
* protocol definitions for a series of protocols:
* * Um L2 (04.06)
* * Um L3 (04.08)
* * A-bis RSL (08.58)
* * A-bis OML (08.59, 12.21)
* * A (08.08)
* \n\n
* Please note that C language projects inside Osmocom are typically
* single-threaded event-loop state machine designs. As such,
* routines in libosmogsm are not thread-safe. If you must use them in
* a multi-threaded context, you have to add your own locking.
*
* libosmogsm is developed as part of the Osmocom (Open Source Mobile
* Communications) project, a community-based, collaborative development
* project to create Free and Open Source implementations of mobile
* communications systems. For more information about Osmocom, please
* see https://osmocom.org/
*
* \section sec_copyright Copyright and License
* Copyright © 2008-2011 - Harald Welte, Holger Freyther and contributors\n
* All rights reserved. \n\n
* The source code of libosmogsm is licensed under the terms of the GNU
* General Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option) any later
* version.\n
* See <http://www.gnu.org/licenses/> or COPYING included in the source
* code package istelf.\n
* The information detailed here is provided AS IS with NO WARRANTY OF
* ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE.
* \n\n
*
* \section sec_tracker Homepage + Issue Tracker
* libosmogsm is distributed as part of libosmocore and shares its
* project page at http://osmocom.org/projects/libosmocore
*
* An Issue Tracker can be found at
* https://osmocom.org/projects/libosmocore/issues
*
* \section sec_contact Contact and Support
* Community-based support is available at the OpenBSC mailing list
* <http://lists.osmocom.org/mailman/listinfo/openbsc>\n
* Commercial support options available upon request from
* <http://sysmocom.de/>
*/
//#include <openbsc/gsm_data.h>
#include <osmocom/core/utils.h>
/*#include <osmocom/core/bitvec.h>*/
#include <osmocom/gsm/gsm_utils.h>
/*#include <osmocom/gsm/meas_rep.h>*/
#include <osmocom/gsm/protocol/gsm_04_08.h>
/*#include <stdlib.h>*/
/*#include <stdint.h>*/
/*#include <string.h>*/
/*#include <stdbool.h>*/
/*#include <stdio.h>*/
#include <errno.h>
#include <ctype.h>
/*#include <inttypes.h>*/
/*#include <time.h>*/
/*#include <unistd.h>*/
/*#include "../../config.h"*/
/* FIXME: this can be removed once we bump glibc requirements to 2.25: *
#if defined(__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 25)
#include <sys/random.h>
#elif HAVE_DECL_SYS_GETRANDOM
#include <sys/syscall.h>
#ifndef GRND_NONBLOCK
#define GRND_NONBLOCK 0x0001
#endif
#endif
#if (USE_GNUTLS)
#pragma message ("including GnuTLS for getrandom fallback.")
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#endif
*/
/* ETSI GSM 03.38 6.2.1 and 6.2.1.1 default alphabet
* Greek symbols at hex positions 0x10 and 0x12-0x1a
* left out as they can't be handled with a char and
* since most phones don't display or write these
* characters this would only needlessly make the code
* more complex.
*
* Note that this table contains the latin1->7bit mapping _and_ has
* been merged with the reverse mapping (7bit->latin1) for the
* extended characters at offset 0x7f.
*
static unsigned char gsm_7bit_alphabet[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0xff, 0xff, 0x0d, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
0x3c, 0x3d, 0x3e, 0x3f, 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x3c, 0x2f, 0x3e, 0x14, 0x11, 0xff, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x28, 0x40, 0x29, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0xff, 0x01, 0xff,
0x03, 0xff, 0x7b, 0x7d, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5c, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x7e, 0x5d, 0xff, 0x7c, 0xff, 0xff, 0xff,
0xff, 0x5b, 0x0e, 0x1c, 0x09, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5d,
0xff, 0xff, 0xff, 0xff, 0x5c, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff, 0x1e, 0x7f,
0xff, 0xff, 0xff, 0x7b, 0x0f, 0x1d, 0xff, 0x04, 0x05, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff,
0xff, 0x7d, 0x08, 0xff, 0xff, 0xff, 0x7c, 0xff, 0x0c, 0x06, 0xff, 0xff, 0x7e, 0xff, 0xff
};
/* GSM 03.38 6.2.1 Character lookup for decoding *
static int gsm_septet_lookup(uint8_t ch)
{
int i = 0;
for (; i < sizeof(gsm_7bit_alphabet); i++) {
if (gsm_7bit_alphabet[i] == ch)
return i;
}
return -1;
}
/*! \brife Compute number of octets from number of septets,
* for instance: 47 septets needs 41,125 = 42 octets
* \param[in sept_len Number of Septets
* \returns Number of octets required *
uint8_t gsm_get_octet_len(const uint8_t sept_len){
int octet_len = (sept_len * 7) / 8;
if ((sept_len * 7) % 8 != 0)
octet_len++;
return octet_len;
}
/*! TS 03.38 7-bit Character unpacking (6.2.1)
* \param[out] text Caller-provided output text buffer
* \param[in] n Length of \a text
* \param[in] user_data Input Data (septets)
* \param[in] septet_l Number of septets in \a user_data
* \param[in] ud_hdr_ind User Data Header present in data
* \returns number of bytes written to \a text *
int gsm_7bit_decode_n_hdr(char *text, size_t n, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind)
{
unsigned shift = 0;
uint8_t c7, c8, next_is_ext = 0, lu, ru;
const uint8_t maxlen = gsm_get_octet_len(septet_l);
const char *text_buf_begin = text;
const char *text_buf_end = text + n;
OSMO_ASSERT (n > 0);
/* skip the user data header *
if (ud_hdr_ind) {
/* get user data header length + 1 (for the 'user data header length'-field) *
shift = ((user_data[0] + 1) * 8) / 7;
if ((((user_data[0] + 1) * 8) % 7) != 0)
shift++;
septet_l = septet_l - shift;
}
unsigned i, l, r;
for (i = 0; i < septet_l && text != text_buf_end - 1; i++) {
l = ((i + shift) * 7 + 7) >> 3;
r = ((i + shift) * 7) >> 3;
/* the left side index is always >= right side index
sometimes it even gets beyond array boundary
check for that explicitly and force 0 instead
*
if (l >= maxlen)
lu = 0;
else
lu = user_data[l] << (7 - (((i + shift) * 7 + 7) & 7));
ru = user_data[r] >> (((i + shift) * 7) & 7);
c7 = (lu | ru) & 0x7f;
if (next_is_ext) {
/* this is an extension character *
next_is_ext = 0;
c8 = gsm_7bit_alphabet[0x7f + c7];
} else if (c7 == 0x1b && i + 1 < septet_l) {
next_is_ext = 1;
continue;
} else {
c8 = gsm_septet_lookup(c7);
}
*(text++) = c8;
}
*text = '\0';
return text - text_buf_begin;
}
/*! Decode 7bit GSM Alphabet *
int gsm_7bit_decode_n(char *text, size_t n, const uint8_t *user_data, uint8_t septet_l)
{
return gsm_7bit_decode_n_hdr(text, n, user_data, septet_l, 0);
}
/*! Decode 7bit GSM Alphabet (USSD) *
int gsm_7bit_decode_n_ussd(char *text, size_t n, const uint8_t *user_data, uint8_t length)
{
int nchars;
nchars = gsm_7bit_decode_n_hdr(text, n, user_data, length, 0);
/* remove last <CR>, if it fits up to the end of last octet *
if (nchars && (user_data[gsm_get_octet_len(length) - 1] >> 1) == '\r')
text[--nchars] = '\0';
return nchars;
}
/*! Encode a ASCII characterrs as 7-bit GSM alphabet (TS 03.38)
*
* This function converts a zero-terminated input string \a data from
* ASCII into octet-aligned 7-bit GSM characters. No packing is
* performed.
*
* \param[out] result caller-allocated output buffer
* \param[in] data input data, ASCII
* \returns number of octets used in \a result *
int gsm_septet_encode(uint8_t *result, const char *data)
{
int i, y = 0;
uint8_t ch;
for (i = 0; i < strlen(data); i++) {
ch = data[i];
switch(ch){
/* fall-through for extension characters *
case 0x0c:
case 0x5e:
case 0x7b:
case 0x7d:
case 0x5c:
case 0x5b:
case 0x7e:
case 0x5d:
case 0x7c:
result[y++] = 0x1b;
default:
result[y] = gsm_7bit_alphabet[ch];
break;
}
y++;
}
return y;
}
/*! GSM Default Alphabet 7bit to octet packing
* \param[out] result Caller-provided output buffer
* \param[in] rdata Input data septets
* \param[in] septet_len Length of \a rdata
* \param[in] padding padding bits at start
* \returns number of bytes used in \a result *
int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding)
{
int i = 0, z = 0;
uint8_t cb, nb;
int shift = 0;
uint8_t *data = calloc(septet_len + 1, sizeof(uint8_t));
if (padding) {
shift = 7 - padding;
/* the first zero is needed for padding *
memcpy(data + 1, rdata, septet_len);
septet_len++;
} else
memcpy(data, rdata, septet_len);
for (i = 0; i < septet_len; i++) {
if (shift == 7) {
/*
* special end case with the. This is necessary if the
* last septet fits into the previous octet. E.g. 48
* non-extension characters:
* ....ag ( a = 1100001, g = 1100111)
* result[40] = 100001 XX, result[41] = 1100111 1 *
if (i + 1 < septet_len) {
shift = 0;
continue;
} else if (i + 1 == septet_len)
break;
}
cb = (data[i] & 0x7f) >> shift;
if (i + 1 < septet_len) {
nb = (data[i + 1] & 0x7f) << (7 - shift);
cb = cb | nb;
}
result[z++] = cb;
shift++;
}
free(data);
return z;
}
/*! GSM 7-bit alphabet TS 03.38 6.2.1 Character packing
* \param[out] result Caller-provided output buffer
* \param[in] n Maximum length of \a result in bytes
* \param[in] data octet-aligned string
* \param[out] octets Number of octets encoded
* \returns number of septets encoded *
int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets)
{
int y = 0;
int o;
size_t max_septets = n * 8 / 7;
/* prepare for the worst case, every character expanding to two bytes *
uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t));
y = gsm_septet_encode(rdata, data);
if (y > max_septets) {
/*
* Limit the number of septets to avoid the generation
* of more than n octets.
*
y = max_septets;
}
o = gsm_septets2octets(result, rdata, y, 0);
if (octets)
*octets = o;
free(rdata);
/*
* We don't care about the number of octets, because they are not
* unique. E.g.:
* 1.) 46 non-extension characters + 1 extension character
* => (46 * 7 bit + (1 * (2 * 7 bit))) / 8 bit = 42 octets
* 2.) 47 non-extension characters
* => (47 * 7 bit) / 8 bit = 41,125 = 42 octets
* 3.) 48 non-extension characters
* => (48 * 7 bit) / 8 bit = 42 octects
*
return y;
}
/*! Encode according to GSM 7-bit alphabet (TS 03.38 6.2.1) for USSD
* \param[out] result Caller-provided output buffer
* \param[in] n Maximum length of \a result in bytes
* \param[in] data octet-aligned string
* \param[out] octets Number of octets encoded
* \returns number of septets encoded *
int gsm_7bit_encode_n_ussd(uint8_t *result, size_t n, const char *data, int *octets)
{
int y;
y = gsm_7bit_encode_n(result, n, data, octets);
/* if last octet contains only one bit, add <CR> *
if (((y * 7) & 7) == 1)
result[(*octets) - 1] |= ('\r' << 1);
/* if last character is <CR> and completely fills last octet, add
* another <CR>. *
if (y && ((y * 7) & 7) == 0 && (result[(*octets) - 1] >> 1) == '\r' && *octets < n - 1) {
result[(*octets)++] = '\r';
y++;
}
return y;
}
/*! Generate random identifier
* We use /dev/urandom (default when GRND_RANDOM flag is not set).
* Both /dev/(u)random numbers are coming from the same CSPRNG anyway (at least on GNU/Linux >= 4.8).
* See also RFC4086.
* \param[out] out Buffer to be filled with random data
* \param[in] len Number of random bytes required
* \returns 0 on success, or a negative error code on error.
*
int osmo_get_rand_id(uint8_t *out, size_t len)
{
int rc = -ENOTSUP;
/* this function is intended for generating short identifiers only, not arbitrary-length random data *
if (len > OSMO_MAX_RAND_ID_LEN)
return -E2BIG;
#if defined(__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 25)
rc = getrandom(out, len, GRND_NONBLOCK);
#elif HAVE_DECL_SYS_GETRANDOM
#pragma message ("Using direct syscall access for getrandom(): consider upgrading to glibc >= 2.25")
/* FIXME: this can be removed once we bump glibc requirements to 2.25: *
rc = syscall(SYS_getrandom, out, len, GRND_NONBLOCK);
#endif
/* getrandom() failed entirely: *
if (rc < 0) {
#if (USE_GNUTLS)
#pragma message ("Secure random failed: using GnuTLS fallback.")
return gnutls_rnd(GNUTLS_RND_RANDOM, out, len);
#endif
return -errno;
}
/* getrandom() failed partially due to signal interruption:
this should never happen (according to getrandom(2)) as long as OSMO_MAX_RAND_ID_LEN < 256
because we do not set GRND_RANDOM but it's better to be paranoid and check anyway *
if (rc != len)
return -EAGAIN;
return 0;
}
/*! Build the RSL uplink measurement IE (3GPP TS 08.58 § 9.3.25)
* \param[in] mru Unidirectional measurement report structure
* \param[in] dtxd_used Indicates if DTXd was used during measurement report
* period
* \param[out] buf Pre-allocated bufer for storing IE
* \returns Number of bytes filled in buf
*
size_t gsm0858_rsl_ul_meas_enc(struct gsm_meas_rep_unidir *mru, bool dtxd_used,
uint8_t *buf)
{
buf[0] = dtxd_used ? (1 << 6) : 0;
buf[0] |= (mru->full.rx_lev & 0x3f);
buf[1] = (mru->sub.rx_lev & 0x3f);
buf[2] = ((mru->full.rx_qual & 7) << 3) | (mru->sub.rx_qual & 7);
return 3;
}
/*! Convert power class to dBm according to GSM TS 05.05
* \param[in] band GSM frequency band
* \param[in] class GSM power class
* \returns maximum transmit power of power class in dBm *
unsigned int ms_class_gmsk_dbm(enum gsm_band band, int class)
{
switch (band) {
case GSM_BAND_450:
case GSM_BAND_480:
case GSM_BAND_750:
case GSM_BAND_900:
case GSM_BAND_810:
case GSM_BAND_850:
if (class == 1)
return 43; /* 20W *
if (class == 2)
return 39; /* 8W *
if (class == 3)
return 37; /* 5W *
if (class == 4)
return 33; /* 2W *
if (class == 5)
return 29; /* 0.8W *
break;
case GSM_BAND_1800:
if (class == 1)
return 30; /* 1W *
if (class == 2)
return 24; /* 0.25W *
if (class == 3)
return 36; /* 4W *
break;
case GSM_BAND_1900:
if (class == 1)
return 30; /* 1W *
if (class == 2)
return 24; /* 0.25W *
if (class == 3)
return 33; /* 2W *
break;
}
return -EINVAL;
}
/*! determine power control level for given dBm value, as indicated
* by the tables in chapter 4.1.1 of GSM TS 05.05
* \param[in] GSM frequency band
* \param[in] dbm RF power value in dBm
* \returns TS 05.05 power control level *
int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
{
switch (band) {
case GSM_BAND_450:
case GSM_BAND_480:
case GSM_BAND_750:
case GSM_BAND_900:
case GSM_BAND_810:
case GSM_BAND_850:
if (dbm >= 39)
return 0;
else if (dbm < 5)
return 19;
else {
/* we are guaranteed to have (5 <= dbm < 39) *
return 2 + ((39 - dbm) / 2);
}
break;
case GSM_BAND_1800:
if (dbm >= 36)
return 29;
else if (dbm >= 34)
return 30;
else if (dbm >= 32)
return 31;
else if (dbm == 31)
return 0;
else {
/* we are guaranteed to have (0 <= dbm < 31) *
return (30 - dbm) / 2;
}
break;
case GSM_BAND_1900:
if (dbm >= 33)
return 30;
else if (dbm >= 32)
return 31;
else if (dbm == 31)
return 0;
else {
/* we are guaranteed to have (0 <= dbm < 31) *
return (30 - dbm) / 2;
}
break;
}
return -EINVAL;
}
/*! Convert TS 05.05 power level to absolute dBm value
* \param[in] band GSM frequency band
* \param[in] lvl TS 05.05 power control level
* \returns RF power level in dBm *
int ms_pwr_dbm(enum gsm_band band, uint8_t lvl)
{
lvl &= 0x1f;
switch (band) {
case GSM_BAND_450:
case GSM_BAND_480:
case GSM_BAND_750:
case GSM_BAND_900:
case GSM_BAND_810:
case GSM_BAND_850:
if (lvl < 2)
return 39;
else if (lvl < 20)
return 39 - ((lvl - 2) * 2) ;
else
return 5;
break;
case GSM_BAND_1800:
if (lvl < 16)
return 30 - (lvl * 2);
else if (lvl < 29)
return 0;
else
return 36 - ((lvl - 29) * 2);
break;
case GSM_BAND_1900:
if (lvl < 16)
return 30 - (lvl * 2);
else if (lvl < 30)
return -EINVAL;
else
return 33 - (lvl - 30);
break;
}
return -EINVAL;
}
/*! Convert TS 05.08 RxLev to dBm (TS 05.08 Chapter 8.1.4)
* \param[in] rxlev TS 05.08 RxLev value
* \returns Received RF power in dBm *
int rxlev2dbm(uint8_t rxlev)
{
if (rxlev > 63)
rxlev = 63;
return -110 + rxlev;
}
/*! Convert RF signal level in dBm to TS 05.08 RxLev (TS 05.08 Chapter 8.1.4)
* \param[in] dbm RF signal level in dBm
* \returns TS 05.08 RxLev value *
uint8_t dbm2rxlev(int dbm)
{
int rxlev = dbm + 110;
if (rxlev > 63)
rxlev = 63;
else if (rxlev < 0)
rxlev = 0;
return rxlev;
}
/*! Return string name of a given GSM Band */
const char *gsm_band_name(enum gsm_band band)
{
switch (band) {
case GSM_BAND_450:
return "GSM450";
case GSM_BAND_480:
return "GSM480";
case GSM_BAND_750:
return "GSM750";
case GSM_BAND_810:
return "GSM810";
case GSM_BAND_850:
return "GSM850";
case GSM_BAND_900:
return "GSM900";
case GSM_BAND_1800:
return "DCS1800";
case GSM_BAND_1900:
return "PCS1900";
}
return "invalid";
}
/*! Parse string name of a GSM band */
enum gsm_band gsm_band_parse(const char* mhz)
{
while (*mhz && !isdigit((unsigned char)*mhz))
mhz++;
if (*mhz == '\0')
return -EINVAL;
switch (strtol(mhz, NULL, 10)) {
case 450:
return GSM_BAND_450;
case 480:
return GSM_BAND_480;
case 750:
return GSM_BAND_750;
case 810:
return GSM_BAND_810;
case 850:
return GSM_BAND_850;
case 900:
return GSM_BAND_900;
case 1800:
return GSM_BAND_1800;
case 1900:
return GSM_BAND_1900;
default:
return -EINVAL;
}
}
/*! Resolve GSM band from ARFCN
* In Osmocom, we use the highest bit of the \a arfcn to indicate PCS
* \param[in] arfcn Osmocom ARFCN, highest bit determines PCS mode
* \returns GSM Band */
enum gsm_band gsm_arfcn2band(uint16_t arfcn)
{
int is_pcs = arfcn & ARFCN_PCS;
arfcn &= ~ARFCN_FLAG_MASK;
if (is_pcs)
return GSM_BAND_1900;
else if (arfcn <= 124)
return GSM_BAND_900;
else if (arfcn >= 955 && arfcn <= 1023)
return GSM_BAND_900;
else if (arfcn >= 128 && arfcn <= 251)
return GSM_BAND_850;
else if (arfcn >= 512 && arfcn <= 885)
return GSM_BAND_1800;
else if (arfcn >= 259 && arfcn <= 293)
return GSM_BAND_450;
else if (arfcn >= 306 && arfcn <= 340)
return GSM_BAND_480;
else if (arfcn >= 350 && arfcn <= 425)
return GSM_BAND_810;
else if (arfcn >= 438 && arfcn <= 511)
return GSM_BAND_750;
else
return GSM_BAND_1800;
}
struct gsm_freq_range {
uint16_t arfcn_first;
uint16_t arfcn_last;
uint16_t freq_ul_first;
uint16_t freq_dl_offset;
uint16_t flags;
};
static struct gsm_freq_range gsm_ranges[] = {
{ 512, 810, 18502, 800, ARFCN_PCS }, /* PCS 1900 */
{ 0, 124, 8900, 450, 0 }, /* P-GSM + E-GSM ARFCN 0 */
{ 955, 1023, 8762, 450, 0 }, /* E-GSM + R-GSM */
{ 128, 251, 8242, 450, 0 }, /* GSM 850 */
{ 512, 885, 17102, 950, 0 }, /* DCS 1800 */
{ 259, 293, 4506, 100, 0 }, /* GSM 450 */
{ 306, 340, 4790, 100, 0 }, /* GSM 480 */
{ 350, 425, 8060, 450, 0 }, /* GSM 810 */
{ 438, 511, 7472, 300, 0 }, /* GSM 750 */
{ /* Guard */ }
};
/*! Convert an ARFCN to the frequency in MHz * 10
* \param[in] arfcn GSM ARFCN to convert
* \param[in] uplink Uplink (1) or Downlink (0) frequency
* \returns Frequency in units of 1/10ths of MHz (100kHz) */
uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink)
{
struct gsm_freq_range *r;
uint16_t flags = arfcn & ARFCN_FLAG_MASK;
uint16_t freq10_ul = 0xffff;
uint16_t freq10_dl = 0xffff;
arfcn &= ~ARFCN_FLAG_MASK;
for (r=gsm_ranges; r->freq_ul_first>0; r++) {
if ((flags == r->flags) &&
(arfcn >= r->arfcn_first) &&
(arfcn <= r->arfcn_last))
{
freq10_ul = r->freq_ul_first + 2 * (arfcn - r->arfcn_first);
freq10_dl = freq10_ul + r->freq_dl_offset;
break;
}
}
return uplink ? freq10_ul : freq10_dl;
}
/*! Convert a Frequency in MHz * 10 to ARFCN
* \param[in] freq10 Frequency in units of 1/10ths of MHz (100kHz)
* \param[in] uplink Frequency is Uplink (1) or Downlink (0)
* \returns ARFCN in case of success; 0xffff on error */
uint16_t gsm_freq102arfcn(uint16_t freq10, int uplink)
{
struct gsm_freq_range *r;
uint16_t freq10_lo, freq10_hi;
uint16_t arfcn = 0xffff;
for (r=gsm_ranges; r->freq_ul_first>0; r++) {
/* Generate frequency limits */
freq10_lo = r->freq_ul_first;
freq10_hi = freq10_lo + 2 * (r->arfcn_last - r->arfcn_first);
if (!uplink) {
freq10_lo += r->freq_dl_offset;
freq10_hi += r->freq_dl_offset;
}
/* Check if this fits */
if (freq10 >= freq10_lo && freq10 <= freq10_hi) {
arfcn = r->arfcn_first + ((freq10 - freq10_lo) >> 1);
arfcn |= r->flags;
break;
}
}
if (uplink)
arfcn |= ARFCN_UPLINK;
return arfcn;
}
/*! Parse GSM Frame Number into struct \ref gsm_time
* \param[out] time Caller-provided memory for \ref gsm_time
* \param[in] fn GSM Frame Number *
void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn)
{
time->fn = fn;
time->t1 = time->fn / (26*51);
time->t2 = time->fn % 26;
time->t3 = time->fn % 51;
time->tc = (time->fn / 51) % 8;
}
/*! Parse GSM Frame Number into printable string
* \param[in] fn GSM Frame Number
* \returns pointer to printable string *
char *gsm_fn_as_gsmtime_str(uint32_t fn)
{
struct gsm_time time;
gsm_fn2gsmtime(&time, fn);
return osmo_dump_gsmtime(&time);
}
/*! Encode decoded \ref gsm_time to Frame Number
* \param[in] time GSM Time in decoded structure
* \returns GSM Frame Number *
uint32_t gsm_gsmtime2fn(struct gsm_time *time)
{
/* TS 05.02 Chapter 4.3.3 TDMA frame number *
return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1));
}
char *osmo_dump_gsmtime(const struct gsm_time *tm)
{
static char buf[64];
snprintf(buf, sizeof(buf), "%06"PRIu32"/%02"PRIu16"/%02"PRIu8"/%02"PRIu8"/%02"PRIu8,
tm->fn, tm->t1, tm->t2, tm->t3, (uint8_t)tm->fn%52);
buf[sizeof(buf)-1] = '\0';
return buf;
}
/*! append range1024 encoded data to bit vector
* \param[out] bv Caller-provided output bit-vector
* \param[in] r Input Range1024 sructure *
void bitvec_add_range1024(struct bitvec *bv, const struct gsm48_range_1024 *r)
{
bitvec_set_uint(bv, r->w1_hi, 2);
bitvec_set_uint(bv, r->w1_lo, 8);
bitvec_set_uint(bv, r->w2_hi, 8);
bitvec_set_uint(bv, r->w2_lo, 1);
bitvec_set_uint(bv, r->w3_hi, 7);
bitvec_set_uint(bv, r->w3_lo, 2);
bitvec_set_uint(bv, r->w4_hi, 6);
bitvec_set_uint(bv, r->w4_lo, 2);
bitvec_set_uint(bv, r->w5_hi, 6);
bitvec_set_uint(bv, r->w5_lo, 2);
bitvec_set_uint(bv, r->w6_hi, 6);
bitvec_set_uint(bv, r->w6_lo, 2);
bitvec_set_uint(bv, r->w7_hi, 6);
bitvec_set_uint(bv, r->w7_lo, 2);
bitvec_set_uint(bv, r->w8_hi, 6);
bitvec_set_uint(bv, r->w8_lo, 1);
bitvec_set_uint(bv, r->w9, 7);
bitvec_set_uint(bv, r->w10, 7);
bitvec_set_uint(bv, r->w11_hi, 1);
bitvec_set_uint(bv, r->w11_lo, 6);
bitvec_set_uint(bv, r->w12_hi, 2);
bitvec_set_uint(bv, r->w12_lo, 5);
bitvec_set_uint(bv, r->w13_hi, 3);
bitvec_set_uint(bv, r->w13_lo, 4);
bitvec_set_uint(bv, r->w14_hi, 4);
bitvec_set_uint(bv, r->w14_lo, 3);
bitvec_set_uint(bv, r->w15_hi, 5);
bitvec_set_uint(bv, r->w15_lo, 2);
bitvec_set_uint(bv, r->w16, 6);
}
/*! Determine GPRS TLLI Type (TS 23.003 Chapter 2.6) *
int gprs_tlli_type(uint32_t tlli)
{
if ((tlli & 0xc0000000) == 0xc0000000)
return TLLI_LOCAL;
else if ((tlli & 0xc0000000) == 0x80000000)
return TLLI_FOREIGN;
else if ((tlli & 0xf8000000) == 0x78000000)
return TLLI_RANDOM;
else if ((tlli & 0xf8000000) == 0x70000000)
return TLLI_AUXILIARY;
else if ((tlli & 0xf0000000) == 0x00000000)
return TLLI_G_RNTI;
else if ((tlli & 0xf0000000) == 0x10000000)
return TLLI_RAND_G_RNTI;
return TLLI_RESERVED;
}
/*! Determine TLLI from P-TMSI
* \param[in] p_tmsi P-TMSI
* \param[in] type TLLI Type we want to derive from \a p_tmsi
* \returns TLLI of given type *
uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type)
{
uint32_t tlli;
switch (type) {
case TLLI_LOCAL:
tlli = p_tmsi | 0xc0000000;
break;
case TLLI_FOREIGN:
tlli = (p_tmsi & 0x3fffffff) | 0x80000000;
break;
default:
tlli = 0;
break;
}
return tlli;
}
/* Wrappers for deprecated functions: *
int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t septet_l)
{
gsm_7bit_decode_n(text, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
user_data, septet_l);
/* Mimic the original behaviour. *
return septet_l;
}
int gsm_7bit_decode_ussd(char *text, const uint8_t *user_data, uint8_t length)
{
return gsm_7bit_decode_n_ussd(text, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
user_data, length);
}
int gsm_7bit_encode(uint8_t *result, const char *data)
{
int out;
return gsm_7bit_encode_n(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
data, &out);
}
int gsm_7bit_encode_ussd(uint8_t *result, const char *data, int *octets)
{
return gsm_7bit_encode_n_ussd(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
data, octets);
}
int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets)
{
return gsm_7bit_encode_n(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
data, octets);
}*/

View File

@ -0,0 +1,238 @@
/*! \file gsm_utils.h
* GSM utility functions, e.g. coding and decoding. */
/*
* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <osmocom/core/defs.h>
#include <osmocom/core/utils.h>
#define ADD_MODULO(sum, delta, modulo) do { \
if ((sum += delta) >= modulo) \
sum -= modulo; \
} while (0)
#define GSM_MAX_FN (26*51*2048)
/* Max length of random identifier which can be requested via osmo_get_rand_id() */
#define OSMO_MAX_RAND_ID_LEN 16
//struct gsm_time {
// uint32_t fn; /* FN count */
// uint16_t t1; /* FN div (26*51) */
// uint8_t t2; /* FN modulo 26 */
// uint8_t t3; /* FN modulo 51 */
// uint8_t tc;
//};
enum gsm_band {
GSM_BAND_850 = 1,
GSM_BAND_900 = 2,
GSM_BAND_1800 = 4,
GSM_BAND_1900 = 8,
GSM_BAND_450 = 0x10,
GSM_BAND_480 = 0x20,
GSM_BAND_750 = 0x40,
GSM_BAND_810 = 0x80,
};
const char *gsm_band_name(enum gsm_band band);
enum gsm_band gsm_band_parse(const char *mhz);
//int osmo_get_rand_id(uint8_t *out, size_t len);
/*!
* Decode a sequence of GSM 03.38 encoded 7 bit characters.
*
* \param decoded The destination buffer for the decoded characters.
* \param n A maximum of n chars is written (incl. terminating \0).
* Requires n >= 1.
* \param user_data A pointer to the start of the packed 7bit character
* sequence.
* \param length The length of the input sequence in septets, for
* example pass octet_length*8/7.
*
* \returns the number of (8 bit) chars written excluding the terminating \0.
* This is the same like strlen(decoded).
*/
//int gsm_7bit_decode_n(char *decoded, size_t n, const uint8_t *user_data, uint8_t length);
///*!
// * Decode a sequence of 7 bit characters (USSD encoding).
// *
// * \see gsm_7bit_encode_n()
// */
//int gsm_7bit_decode_n_ussd(char *decoded, size_t n, const uint8_t *user_data, uint8_t length);
///*!
// * Encode a text string into GSM 03.38 encoded 7 bit characters.
// *
// * \param result The destination buffer for the packed 7 bit sequence.
// * \param n A maximum of n octets is written.
// * \param data A pointer to the start of the \0 terminated 8 bit character
// * string.
// * \param octets_written Iff not NULL, *octets_written will be set to the
// * number of octets written to the result buffer.
// *
// * \returns the number of septets that have been created.
// */
//int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets_written);
///*!
// * Encode a text string into GSM 03.38 encoded 7 bit characters (USSD encoding).
// *
// * \see gsm_7bit_decode_n()
// */
//int gsm_7bit_encode_n_ussd(uint8_t *result, size_t n, const char *data, int *octets_written);
///* the four functions below are helper functions and here for the unit test */
//int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding);
//int gsm_septet_encode(uint8_t *result, const char *data);
//uint8_t gsm_get_octet_len(const uint8_t sept_len);
//int gsm_7bit_decode_n_hdr(char *decoded, size_t n, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
//unsigned int ms_class_gmsk_dbm(enum gsm_band band, int ms_class);
//int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
//int ms_pwr_dbm(enum gsm_band band, uint8_t lvl);
///* According to TS 05.08 Chapter 8.1.4 */
//int rxlev2dbm(uint8_t rxlev);
//uint8_t dbm2rxlev(int dbm);
///* According to GSM 04.08 Chapter 10.5.1.6 */
//static inline int ms_cm2_a5n_support(uint8_t *cm2, unsigned n) {
// switch (n) {
// case 0: return 1;
// case 1: return (cm2[0] & (1<<3)) ? 0 : 1;
// case 2: return (cm2[2] & (1<<0)) ? 1 : 0;
// case 3: return (cm2[2] & (1<<1)) ? 1 : 0;
// default:
// return 0;
// }
//}
///* According to GSM 04.08 Chapter 10.5.1.7 */
//static inline int ms_cm3_a5n_support(uint8_t *cm3, unsigned n) {
// switch (n) {
// case 4: return (cm3[0] & (1<<0)) ? 1 : 0;
// case 5: return (cm3[0] & (1<<1)) ? 1 : 0;
// case 6: return (cm3[0] & (1<<2)) ? 1 : 0;
// case 7: return (cm3[0] & (1<<3)) ? 1 : 0;
// default:
// return 0;
// }
//}
///* According to GSM 04.08 Chapter 10.5.2.29 */
//static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
//static inline int rach_max_trans_raw2val(int raw) {
// const int tbl[4] = { 1, 2, 4, 7 };
// return tbl[raw & 3];
//}
#define ARFCN_PCS 0x8000
#define ARFCN_UPLINK 0x4000
#define ARFCN_FLAG_MASK 0xf000 /* Reserve the upper 5 bits for flags */
enum gsm_band gsm_arfcn2band(uint16_t arfcn);
/* Convert an ARFCN to the frequency in MHz * 10 */
uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink);
/* Convert a Frequency in MHz * 10 to ARFCN */
uint16_t gsm_freq102arfcn(uint16_t freq10, int uplink);
/* Convert from frame number to GSM time */
//void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
/* Parse GSM Frame Number into printable string */
//char *gsm_fn_as_gsmtime_str(uint32_t fn);
/* Convert from GSM time to frame number */
//uint32_t gsm_gsmtime2fn(struct gsm_time *time);
/* Returns static buffer with string representation of a GSM Time */
//char *osmo_dump_gsmtime(const struct gsm_time *tm);
/* GSM TS 03.03 Chapter 2.6 */
//enum gprs_tlli_type {
// TLLI_LOCAL,
// TLLI_FOREIGN,
// TLLI_RANDOM,
// TLLI_AUXILIARY,
// TLLI_RESERVED,
// TLLI_G_RNTI,
// TLLI_RAND_G_RNTI,
//};
/* TS 03.03 Chapter 2.6 */
//int gprs_tlli_type(uint32_t tlli);
//uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type);
///* Osmocom internal, not part of any gsm spec */
//enum gsm_phys_chan_config {
// GSM_PCHAN_NONE,
// GSM_PCHAN_CCCH,
// GSM_PCHAN_CCCH_SDCCH4,
// GSM_PCHAN_TCH_F,
// GSM_PCHAN_TCH_H,
// GSM_PCHAN_SDCCH8_SACCH8C,
// GSM_PCHAN_PDCH, /* GPRS PDCH */
// GSM_PCHAN_TCH_F_PDCH, /* TCH/F if used, PDCH otherwise */
// GSM_PCHAN_UNKNOWN,
// GSM_PCHAN_CCCH_SDCCH4_CBCH,
// GSM_PCHAN_SDCCH8_SACCH8C_CBCH,
// GSM_PCHAN_TCH_F_TCH_H_PDCH,
// _GSM_PCHAN_MAX
//};
///* Osmocom internal, not part of any gsm spec */
//enum gsm_chan_t {
// GSM_LCHAN_NONE,
// GSM_LCHAN_SDCCH,
// GSM_LCHAN_TCH_F,
// GSM_LCHAN_TCH_H,
// GSM_LCHAN_UNKNOWN,
// GSM_LCHAN_CCCH,
// GSM_LCHAN_PDTCH,
// GSM_LCHAN_CBCH,
// _GSM_LCHAN_MAX
//};
//extern const struct value_string gsm_chan_t_names[];
///* Deprectated functions */
///* Limit encoding and decoding to use no more than this amount of buffer bytes */
//#define GSM_7BIT_LEGACY_MAX_BUFFER_SIZE 0x10000
//int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length) OSMO_DEPRECATED("Use gsm_7bit_decode_n() instead");
//int gsm_7bit_decode_ussd(char *decoded, const uint8_t *user_data, uint8_t length) OSMO_DEPRECATED("Use gsm_7bit_decode_n_ussd() instead");
//int gsm_7bit_encode(uint8_t *result, const char *data) OSMO_DEPRECATED("Use gsm_7bit_encode_n() instead");
//int gsm_7bit_encode_ussd(uint8_t *result, const char *data, int *octets_written) OSMO_DEPRECATED("Use gsm_7bit_encode_n_ussd() instead");
//int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets_written) OSMO_DEPRECATED("Use gsm_7bit_encode_n() instead");

View File

@ -0,0 +1,386 @@
/* -*- c++ -*- */
/*
* @file
* @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <grgsm/gsmtap.h>
#include <grgsm/endian.h>
#include "tch_h_decoder_impl.h"
extern "C" {
#include "osmocom/gsm/protocol/gsm_04_08.h"
#include "osmocom/coding/gsm0503_coding.h"
}
namespace gr {
namespace gsm {
static int ubits2sbits(ubit_t *ubits, sbit_t *sbits, int count)
{
int i;
for (i = 0; i < count; i++) {
if (*ubits == 0x23) {
ubits++;
sbits++;
continue;
}
if ((*ubits++) & 1)
*sbits++ = -127;
else
*sbits++ = 127;
}
return count;
}
tch_h_decoder::sptr
tch_h_decoder::make(unsigned int sub_channel, std::string multi_rate, bool boundary_check)
{
return gnuradio::get_initial_sptr
(new tch_h_decoder_impl(sub_channel, multi_rate, boundary_check));
}
/*
* Constructor
*/
tch_h_decoder_impl::tch_h_decoder_impl(unsigned int sub_channel, std::string multi_rate, bool boundary_check)
: gr::block("tch_h_decoder",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_collected_bursts_num(0),
d_tch_mode(TCH_HS),
d_sub_channel(sub_channel),
d_boundary_check(boundary_check),
d_boundary_decode(false),
d_header_sent(false),
d_ft(0),
d_cmr(0)
{
//setup input/output ports
message_port_register_in(pmt::mp("bursts"));
set_msg_handler(pmt::mp("bursts"), boost::bind(&tch_h_decoder_impl::decode, this, _1));
message_port_register_out(pmt::mp("msgs"));
message_port_register_out(pmt::mp("voice"));
if(multi_rate.length())
{
std::cout<<"multi_rate configuration: "<<multi_rate<<std::endl;
if (multi_rate.length() < 4 || multi_rate.length() % 2)
{
throw std::invalid_argument("Invalid multi_rate hexstring");
}
std::vector<uint8_t> binary;
for (std::string::const_iterator it = multi_rate.begin();
it != multi_rate.end(); it += 2)
{
std::string byte(it, it + 2);
char* end = NULL;
errno = 0;
uint8_t b = strtoul(byte.c_str(), &end, 16);
if (errno != 0 || *end != '\0')
{
throw std::invalid_argument("Invalid multi_rate hexstring");
}
binary.push_back(b);
}
if (binary.size() < 2) {
throw std::invalid_argument("The multi_rate is too short");
}
//GSM A-I/F DTAP - Assignment Command
// Protocol Discriminator: Radio Resources Management messages (6)
// DTAP Radio Resources Management Message Type: Assignment Command (0x2e)
// Channel Description 2 - Description of the First Channel, after time
// Power Command
// Channel Mode - Mode of the First Channel(Channel Set 1)
// MultiRate configuration
// Element ID: 0x03
// Length: 4
// 001. .... = Multirate speech version: Adaptive Multirate speech version 1 (1)
// ...0 .... = NSCB: Noise Suppression Control Bit: Noise Suppression can be used (default) (0)
// .... 1... = ICMI: Initial Codec Mode Indicator: The initial codec mode is defined by the Start Mode field (1)
// .... ..00 = Start Mode: 0
// 0... .... = 12,2 kbit/s codec rate: is not part of the subset
// .0.. .... = 10,2 kbit/s codec rate: is not part of the subset
// ..0. .... = 7,95 kbit/s codec rate: is not part of the subset
// ...1 .... = 7,40 kbit/s codec rate: is part of the subset
// .... 0... = 6,70 kbit/s codec rate: is not part of the subset
// .... .0.. = 5,90 kbit/s codec rate: is not part of the subset
// .... ..0. = 5,15 kbit/s codec rate: is not part of the subset
// .... ...1 = 4,75 kbit/s codec rate: is part of the subset
// ..01 1010 = AMR Threshold: 13.0 dB (26)
// 0100 .... = AMR Hysteresis: 2.0 dB (4)
const uint8_t first = binary[0];
uint8_t multirate_speech_ver = (first >> 5) & 0x07;
if (multirate_speech_ver == 1)
{
d_tch_mode = TCH_AFS4_75;
}
else if (multirate_speech_ver == 2)
{
throw std::invalid_argument("Adaptive Multirate speech version 2 is not supported");
}
else
{
throw std::invalid_argument("Multirate speech version");
}
bool ncsb = (first >> 4) & 0x01;
bool icmi = (first >> 3) & 0x01;
uint8_t start = first & 0x03;
const uint8_t codecs = binary[1];
for (int i = 0; i < 8; i++)
{
if ((codecs >> i) & 1)
{
d_multi_rate_codes.push_back(i);
}
}
std::cout<<"Enabled AMR Codecs:"<<std::endl;
for(std::vector<uint8_t>::const_iterator it = d_multi_rate_codes.begin();
it != d_multi_rate_codes.end();
it ++)
{
switch(*it)
{
case 0:
std::cout<<"4,75 kbit/s codec rate: is part of the subset"<<std::endl;
break;
case 1:
std::cout<<"5,15 kbit/s codec rate: is part of the subset"<<std::endl;
break;
case 2:
std::cout<<"5,90 kbit/s codec rate: is part of the subset"<<std::endl;
break;
case 3:
std::cout<<"6,70 kbit/s codec rate: is part of the subset"<<std::endl;
break;
case 4:
std::cout<<"7,40 kbit/s codec rate: is part of the subset"<<std::endl;
break;
case 5:
std::cout<<"7,95 kbit/s codec rate: is part of the subset"<<std::endl;
break;
case 6:
std::cout<<"12,2 kbit/s codec rate: is part of the subset"<<std::endl;
}
}
if (d_multi_rate_codes.size() > 4) {
throw std::invalid_argument("More then 4 multirate codes");
}
}
}
tch_h_decoder_impl::~tch_h_decoder_impl()
{
}
void tch_h_decoder_impl::decode(pmt::pmt_t msg)
{
d_bursts[d_collected_bursts_num++] = msg;
if (d_collected_bursts_num <= 7)
{
return;
}
gsmtap_hdr* header = (gsmtap_hdr*)(pmt::blob_data(pmt::cdr(msg)));
uint32_t frame_nr = be32toh(header->frame_number);
bool uplink_burst = (be16toh(header->arfcn) & 0x4000) ? true : false;
//TODO: Check in 3gpp specs which frames could contains facch/h frames
//and replace this ugly formula with table
int fn_is_odd = (((frame_nr - (uplink_burst ? 10 : 15)) % 26) >> 2) & 1;
ubit_t bursts_u[116 * 6] = {0}; //facch/h is 6 bursts
//reorganize data
for (int ii = 0; ii < 8; ii++)
{
//skip the 4th and 5th bursts
if (ii == 4 || ii == 5) continue;
int8_t* burst_bits = (int8_t*)(pmt::blob_data(pmt::cdr(d_bursts[ii])))+sizeof(gsmtap_hdr);
//copy 6th and 7th burst to 4th and 5th position
int n = ii < 6 ? ii : ii - 2;
memcpy(&bursts_u[n*116], &burst_bits[3],58);
memcpy(&bursts_u[n*116+58], &burst_bits[3+57+1+26],58);
}
//Convert to sbits
sbit_t bursts_s[116 * 6] = {0};
ubits2sbits(bursts_u, bursts_s, 116 * 6);
//Prepare burst for the next iteration by shifting them by 4
for (int ii = 0; ii < 4; ii++) {
d_bursts[ii] = d_bursts[ii + 4];
}
d_collected_bursts_num = 4;
uint8_t frameBuffer[64];
int frameLength = -1;
int n_errors, n_bits_total;
if (d_tch_mode == TCH_HS)
{
frameLength = gsm0503_tch_hr_decode(frameBuffer, bursts_s, fn_is_odd, &n_errors, &n_bits_total);
}
else
{
frameLength = gsm0503_tch_ahs_decode(frameBuffer, bursts_s, fn_is_odd,
fn_is_odd, //int codec_mode_req,
&d_multi_rate_codes.front(), d_multi_rate_codes.size(),
&d_ft,
&d_cmr,
&n_errors, &n_bits_total);
}
if (frameLength < 12)
{
#if 0
if (!d_boundary_check || d_boundary_decode) {
std::cerr<<"Error! frame_nr:"<<frame_nr<<" mod26:"<<frame_nr%26
<<" fn_is_odd:"<<fn_is_odd<<" length:"<<frameLength<<std::endl;
}
#endif
return;
}
else if (frameLength == GSM_MACBLOCK_LEN) //FACCH/H
{
pmt::pmt_t first_header_plus_burst = pmt::cdr(d_bursts[0]);
gsmtap_hdr* header = (gsmtap_hdr *)pmt::blob_data(first_header_plus_burst);
int8_t header_plus_data[sizeof(gsmtap_hdr)+frameLength];
memcpy(header_plus_data, header, sizeof(gsmtap_hdr));
memcpy(header_plus_data+sizeof(gsmtap_hdr), frameBuffer, frameLength);
((gsmtap_hdr*)header_plus_data)->type = GSMTAP_TYPE_UM;
pmt::pmt_t msg_binary_blob = pmt::make_blob(header_plus_data, frameLength + sizeof(gsmtap_hdr));
pmt::pmt_t msg_out = pmt::cons(pmt::PMT_NIL, msg_binary_blob);
message_port_pub(pmt::mp("msgs"), msg_out);
// if d_boundary_check is enabled, we set d_boundary_decode to true, when a
// "Connect" or "Connect Acknowledge" message is received, and
// we set d_boundary_decode back to false, when "Release" message is received
if (d_boundary_check)
{
// check if this is a call control message
if ((frameBuffer[3] & 0x0f) == 0x03)
{
// Alerting
if ((frameBuffer[4] & 0x3f) == 0x01)
{
if ((frameBuffer[5] == 0x1e) && //element id
(frameBuffer[6] == 2) && //length
((frameBuffer[8] & 0x7f) == 0x08))
{
std::cout << "(CC) Alerting with In-band information" << std::endl;
//.000 1000 = Progress description: In-band information or appropriate pattern now available (8)
d_boundary_decode = true;
}
}
// Progress
else if ((frameBuffer[4] & 0x3f) == 0x03)
{
if ((frameBuffer[5] == 2) && //length
(frameBuffer[7] & 0x7f) == 0x08)
{
std::cout << "(CC) Progress with In-band information" << std::endl;
//.000 1000 = Progress description: In-band information or appropriate pattern now available (8)
d_boundary_decode = true;
}
}
// Connect specified in GSM 04.08, 9.3.5
else if ((frameBuffer[4] & 0x3f) == 0x07)
{
std::cout << "(CC) Connect" << std::endl;
d_boundary_decode = true;
}
// Connect Acknowledge specified in GSM 04.08, 9.3.6
else if ((frameBuffer[4] & 0x3f) == 0x0f)
{
std::cout << "(CC) Connect Acknowledge" << std::endl;
d_boundary_decode = true;
}
// Release specified in GSM 04.08, 9.3.18
else if ((frameBuffer[4] & 0x3f) == 0x2d)
{
std::cout << "(CC) Release" << std::endl;
d_boundary_decode = false;
}
}
}
return;
}
if (!d_header_sent && d_tch_mode != TCH_HS)
{
const unsigned char amr_nb_magic[7] = "#!AMR\n";
message_port_pub(pmt::mp("voice"), pmt::cons(pmt::PMT_NIL, pmt::make_blob(amr_nb_magic, 6)));
d_header_sent = true;
}
if (!n_errors && (!d_boundary_check || d_boundary_decode))
{
//std::cerr<<"Voice frame_nr:"<<frame_nr<<" mod26:"<<frame_nr%26<<" is_odd:"<<fn_is_odd
// <<" type:"<<(uint32_t)d_ft<<" cmr:"<<(uint32_t)d_cmr
// <<" errors:"<<n_errors<<std::endl;
if (d_tch_mode != TCH_HS)
{
//Move one byte to make space for the header
memmove(frameBuffer + 1, frameBuffer, frameLength);
//Add the AMR header
switch(frameLength)
{
case 12: frameBuffer[0] = (0 << 3); break; //TCH/AHS4.75
case 13: frameBuffer[0] = (1 << 3); break; //TCH/AHS5.15
case 15: frameBuffer[0] = (2 << 3); break; //TCH/AHS5.9
case 17: frameBuffer[0] = (3 << 3); break; //TCH/AHS6.7
case 19: frameBuffer[0] = (4 << 3); break; //TCH/AHS7.4
case 20: frameBuffer[0] = (5 << 3); break; //TCH/AHS7.95
default: std::cerr<<"Unexpected voice frame length:"<<frameLength<<std::endl; return;
}
frameLength += 1;
}
//std::ostringstream out;
//out << "voice frame: ";
//for (int i = 0; i < frameLength; i++)
// out << " " << (std::hex) << std::setw(2) << std::setfill('0') << (uint32_t)*(frameBuffer + i);
//std::cerr << out.str() << std::endl;
message_port_pub(pmt::mp("voice"), pmt::cons(pmt::PMT_NIL, pmt::make_blob(frameBuffer, frameLength)));
}
}
} /* namespace gsm */
} /* namespace gr */

View File

@ -0,0 +1,60 @@
/* -*- c++ -*- */
/*
* @file
* @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_GSM_TCH_H_DECODER_IMPL_H
#define INCLUDED_GSM_TCH_H_DECODER_IMPL_H
#include <grgsm/decoding/tch_h_decoder.h>
#include "tch_f_decoder_impl.h"
namespace gr {
namespace gsm {
class tch_h_decoder_impl : public tch_h_decoder
{
private:
unsigned int d_collected_bursts_num;
pmt::pmt_t d_bursts[8];
enum tch_mode d_tch_mode;
unsigned int d_sub_channel;
std::vector<uint8_t> d_multi_rate_codes;
bool d_boundary_check;
bool d_boundary_decode;
bool d_header_sent;
uint8_t d_ft;
uint8_t d_cmr;
void decode(pmt::pmt_t msg);
public:
tch_h_decoder_impl(unsigned int sub_channel, std::string multi_rate, bool boundary_check=false);
~tch_h_decoder_impl();
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_TCH_H_DECODER_IMPL_H */

View File

@ -19,6 +19,7 @@
add_sources(
tch_f_chans_demapper_impl.cc
tch_h_chans_demapper_impl.cc
universal_ctrl_chans_demapper_impl.cc
)

View File

@ -109,7 +109,7 @@ namespace gr {
}
}
void tch_f_chans_demapper_impl::sacch_tch_demapper(uint32_t fn_mod13, uint32_t fn_mod26, uint32_t frame_nr,
void tch_f_chans_demapper_impl::sacch_tch_demapper(uint32_t fn_mod13, u_int32_t fn_mod26, uint32_t frame_nr,
pmt::pmt_t *d_bursts_sacch,
uint32_t *d_frame_numbers_sacch, pmt::pmt_t d_bursts[3][8],
uint32_t d_frame_numbers[3][8], pmt::pmt_t msg_out)

View File

@ -0,0 +1,279 @@
/* -*- c++ -*- */
/*
* @file
* @author (C) 2018 by Andrew Artyushok <loony.developer@gmail.com>
* @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include "tch_h_chans_demapper_impl.h"
#include <grgsm/endian.h>
#include <grgsm/gsmtap.h>
#define BURST_SIZE 148
namespace gr {
namespace gsm {
tch_h_chans_demapper::sptr
tch_h_chans_demapper::make(unsigned int timeslot_nr, unsigned int tch_h_channel)
{
return gnuradio::get_initial_sptr
(new tch_h_chans_demapper_impl(timeslot_nr, tch_h_channel));
}
/*
* The private constructor
*
*/
tch_h_chans_demapper_impl::tch_h_chans_demapper_impl(unsigned int timeslot_nr, unsigned int tch_h_channel)
: gr::block("tch_h_chans_demapper",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_timeslot(timeslot_nr),
d_tch_h_channel(tch_h_channel)
{
//std::cout << "d_tch_type is " << d_tch_type << ", tch_h_channel is " << tch_h_channel << std::endl;
message_port_register_in(pmt::mp("bursts"));
set_msg_handler(pmt::mp("bursts"), boost::bind(&tch_h_chans_demapper_impl::filter_tch_chans, this, _1));
message_port_register_out(pmt::mp("tch_bursts"));
message_port_register_out(pmt::mp("acch_bursts"));
}
/*
* Our virtual destructor.
*/
tch_h_chans_demapper_impl::~tch_h_chans_demapper_impl()
{
}
void tch_h_chans_demapper_impl::filter_tch_chans(pmt::pmt_t msg)
{
pmt::pmt_t header_plus_burst = pmt::cdr(msg);
gsmtap_hdr * header = (gsmtap_hdr *)pmt::blob_data(header_plus_burst);
if(header->timeslot != d_timeslot) {
return;
}
uint32_t frame_nr = be32toh(header->frame_number);
uint32_t fn_mod26 = frame_nr % 26;
uint32_t fn_mod13 = frame_nr % 13;
int8_t* burst_bits = (int8_t *)(pmt::blob_data(header_plus_burst)) + sizeof(gsmtap_hdr);
int8_t new_msg[sizeof(gsmtap_hdr)+BURST_SIZE];
gsmtap_hdr * new_hdr = (gsmtap_hdr*)new_msg;
memcpy(new_msg, header, sizeof(gsmtap_hdr)+BURST_SIZE);
new_hdr->sub_type = (fn_mod13 == 12 ? GSMTAP_CHANNEL_ACCH : 0) | GSMTAP_CHANNEL_TCH_H;
new_hdr->sub_slot = d_tch_h_channel;
pmt::pmt_t msg_binary_blob = pmt::make_blob(new_msg,sizeof(gsmtap_hdr)+BURST_SIZE);
pmt::pmt_t msg_out = pmt::cons(pmt::PMT_NIL, msg_binary_blob);
//distinguishing uplink and downlink bursts
bool uplink_burst = (be16toh(header->arfcn) & 0x4000) ? true : false;
if(uplink_burst)
{
sacch_tch_demapper(fn_mod13, fn_mod26, frame_nr, d_bursts_sacch_ul,
d_frame_numbers_sacch_ul, d_bursts_ul, d_frame_numbers_ul, msg_out);
}
else
{
sacch_tch_demapper(fn_mod13, fn_mod26, frame_nr, d_bursts_sacch_dl,
d_frame_numbers_sacch_dl, d_bursts_dl, d_frame_numbers_dl, msg_out);
}
}
void tch_h_chans_demapper_impl::sacch_tch_demapper(uint32_t fn_mod13, u_int32_t fn_mod26, uint32_t frame_nr,
pmt::pmt_t *d_bursts_sacch,
uint32_t *d_frame_numbers_sacch, pmt::pmt_t d_bursts[3][8],
uint32_t d_frame_numbers[3][8], pmt::pmt_t msg_out)
{
bool frames_are_consecutive = true;
if (fn_mod13 == 12)
{
// position of SACCH burst based on timeslot
// see specification gsm 05.02
uint32_t index;
bool is_sacch = false;
if (d_tch_h_channel == 0 && fn_mod26 == 12)
{
index = (((frame_nr - 12) / 26) - (d_timeslot / 2)) % 4;
is_sacch = true;
}
else if (d_tch_h_channel == 1 && fn_mod26 == 25)
{
index = (((frame_nr - 25) / 26) - (d_timeslot / 2)) % 4;
is_sacch = true;
}
if (is_sacch)
{
d_bursts_sacch[index] = msg_out;
d_frame_numbers_sacch[index] = frame_nr;
if (index == 3)
{
//check for a situation where some bursts were lost
//in this situation frame numbers won't be consecutive
frames_are_consecutive = true;
for(int jj=1; jj<4; jj++)
{
if((d_frame_numbers_sacch[jj]-d_frame_numbers_sacch[jj-1]) != 26)
{
frames_are_consecutive = false;
}
}
if(frames_are_consecutive)
{
//send bursts to the output
for(int jj=0; jj<4; jj++)
{
message_port_pub(pmt::mp("acch_bursts"), d_bursts_sacch[jj]);
}
}
}
}
}
else
{
bool our_sub=false;
// So, here I change the fn_mod13, with this changes I can
// process both subslot (d_tch_h_channel) with the same code.
//
// Theory about this on github
//
// Example:
//
// For subslot=0 our burst is 0,2,4,6 etc
// For subslot=1 it 1,3,5 etc
// But if we change it with this code,
// both will be 0,1,2,3,4 etc
// And we can proced two subslot like one.
if(fn_mod13%2==d_tch_h_channel) {
if(d_tch_h_channel==0) {
fn_mod13=fn_mod13/2;
}else{
fn_mod13-=1;
fn_mod13=fn_mod13/2;
}
// We work only with our subslot
our_sub=true;
}
if(our_sub) {
if (fn_mod13 <= 1)
{
// add to b1 and b3
d_bursts[0][fn_mod13] = msg_out;
d_bursts[2][fn_mod13+2] = msg_out;
d_frame_numbers[0][fn_mod13] = frame_nr;
d_frame_numbers[2][fn_mod13+2] = frame_nr;
}
else if (fn_mod13 >=2 && fn_mod13 <=3)
{
// add to b1 and b2
d_bursts[0][fn_mod13] = msg_out;
d_bursts[1][fn_mod13-2] = msg_out;
d_frame_numbers[0][fn_mod13] = frame_nr;
d_frame_numbers[1][fn_mod13-2] = frame_nr;
//d_frame_numbers[1][fn_mod13 - 4] = frame_nr;
}
else if (fn_mod13 >=4 && fn_mod13 <=5)
{
// add to b1 and b2
d_bursts[1][fn_mod13-2] = msg_out;
d_bursts[2][fn_mod13-4] = msg_out;
d_frame_numbers[1][fn_mod13-2] = frame_nr;
d_frame_numbers[2][fn_mod13-4] = frame_nr;
//d_frame_numbers[1][fn_mod13 - 4] = frame_nr;
}
// send burst 1 or burst 2 to output
//if ( fn_mod26 == 2 || fn_mod26 == 5 || fn_mod26 == 9)
if(fn_mod13 == 1 || fn_mod13 == 3|| fn_mod13 == 5)
{
int tch_burst_nr = 0;
if(fn_mod13==5) {
tch_burst_nr = 1;
}
if(fn_mod13==1) {
tch_burst_nr = 2;
}
//check for a situation where some bursts were lost
//in this situation frame numbers won't be consecutive
frames_are_consecutive = true;
// std::cout<<"br="<<tch_burst_nr<<" Array: ";
// std::cout<<d_frame_numbers[tch_burst_nr][0]<<" ";
bool one_tree=true;
for(int jj=1; jj<4; jj++)
{
if (((d_frame_numbers[tch_burst_nr][jj] - d_frame_numbers[tch_burst_nr][jj-1]) != 2) && frames_are_consecutive)
{
frames_are_consecutive = false;
if (tch_burst_nr == 2 && jj == 2
&& d_frame_numbers[tch_burst_nr][jj] - d_frame_numbers[tch_burst_nr][jj - 1] == 3)
{
// std::cout<<" YEAH ";
frames_are_consecutive = true;
}
}
//}
}
// std::cout<<std::endl;
if(frames_are_consecutive)
{
for(int jj=0; jj<4; jj++)
{
message_port_pub(pmt::mp("tch_bursts"), d_bursts[tch_burst_nr][jj]);
}
// useless
// d_bursts_stolen[tch_burst_nr] = false;
}
}
}
}
}
} /* namespace gsm */
} /* namespace gr */

View File

@ -0,0 +1,65 @@
/* -*- c++ -*- */
/*
* @file
* @author (C) 2018 by Andrew Artyushok <loony.developer@gmail.com>
* @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_GSM_TCH_H_CHANS_DEMAPPER_IMPL_H
#define INCLUDED_GSM_TCH_H_CHANS_DEMAPPER_IMPL_H
#include <grgsm/demapping/tch_h_chans_demapper.h>
namespace gr {
namespace gsm {
class tch_h_chans_demapper_impl : public tch_h_chans_demapper
{
private:
unsigned int d_timeslot;
unsigned int d_tch_h_channel;
// Downlink
uint32_t d_frame_numbers_dl[3][8]; // for checking consecutive frame numbers of tch
uint32_t d_frame_numbers_sacch_dl[4]; // for checking consecutive frame numbers of sacch
pmt::pmt_t d_bursts_dl[3][8]; // for tch output headers+bursts
pmt::pmt_t d_bursts_sacch_dl[4]; // for sacch output bursts
// Uplink
uint32_t d_frame_numbers_ul[3][8]; // for checking consecutive frame numbers of tch
uint32_t d_frame_numbers_sacch_ul[4]; // for checking consecutive frame numbers of sacch
pmt::pmt_t d_bursts_ul[3][8]; // for tch output headers+bursts
pmt::pmt_t d_bursts_sacch_ul[4]; // for sacch output bursts
void sacch_tch_demapper(uint32_t fn_mod13, u_int32_t fn_mod26, uint32_t frame_nr, pmt::pmt_t *d_bursts_sacch,
uint32_t *d_frame_numbers_sacch, pmt::pmt_t d_bursts[3][8],
uint32_t d_frame_numbers[3][8], pmt::pmt_t msg_out);
void filter_tch_chans(pmt::pmt_t msg);
public:
tch_h_chans_demapper_impl(unsigned int timeslot_nr, unsigned int tch_h_channel);
~tch_h_chans_demapper_impl();
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_TCH_H_CHANS_DEMAPPER_IMPL_H */

View File

@ -35,8 +35,8 @@ add_sources(
tmsi_dumper_impl.cc
time_spec.cc
fn_time.cc
freq_hopping_utils.cc
udp_socket.cc
trx_burst_if_impl.cc
burst_to_fn_time_impl.cc
)

View File

@ -28,8 +28,6 @@
#include "burst_file_source_impl.h"
#include "stdio.h"
#define PMT_SIZE 174
namespace gr {
namespace gsm {
@ -86,16 +84,13 @@ namespace gr {
void burst_file_source_impl::run()
{
char *unserialized = (char*)malloc(sizeof(char) * PMT_SIZE);
while (d_input_file.read(unserialized, PMT_SIZE) && !d_finished)
std::filebuf* pbuf = d_input_file.rdbuf();
while (!d_finished)
{
if (d_input_file.bad())
{
pmt::pmt_t burst = pmt::deserialize(*pbuf);
if (pmt::is_eof_object(burst)) {
break;
}
std::string s(unserialized, PMT_SIZE);
pmt::pmt_t burst = pmt::deserialize_str(s);
message_port_pub(pmt::mp("out"), burst);
}
d_input_file.close();

View File

@ -66,48 +66,50 @@ namespace gr {
return;
}
std::cout << d_prepend_string;
std::ostringstream out;
out << d_prepend_string;
if (d_prepend_fnr)
{
std::cout << frame_nr;
out << frame_nr;
}
if (d_prepend_fnr && d_prepend_frame_count)
{
std::cout << " ";
out << " ";
}
if (d_prepend_frame_count)
{
// calculate fn count using libosmogsm
std::cout << osmo_a5_fn_count(frame_nr);
out << osmo_a5_fn_count(frame_nr);
}
if (d_prepend_fnr || d_prepend_frame_count)
{
std::cout << ": ";
out << ": ";
}
if (d_print_payload_only)
{
for (int ii=0; ii<57; ii++)
{
std::cout << std::setprecision(1) << static_cast<int>(burst[ii + 3]);
out << std::setprecision(1) << static_cast<int>(burst[ii + 3]);
}
for (int ii=0; ii<57; ii++)
{
std::cout << std::setprecision(1) << static_cast<int>(burst[ii + 88]);
out << std::setprecision(1) << static_cast<int>(burst[ii + 88]);
}
}
else
{
for(int ii=0; ii<burst_len; ii++)
{
std::cout << std::setprecision(1) << static_cast<int>(burst[ii]);
out << std::setprecision(1) << static_cast<int>(burst[ii]);
}
}
std::cout << std::endl;
out << std::endl;
std::cout << out.str() << std::flush;
}
bool bursts_printer_impl::is_dummy_burst(int8_t *burst, size_t burst_len)

View File

@ -44,7 +44,8 @@ namespace gr {
io_signature::make(1, 1, sizeof(gr_complex)),
io_signature::make(1, 1, sizeof(gr_complex))),
d_mu(phase_shift), d_mu_inc(resamp_ratio),
d_resamp(new mmse_fir_interpolator_cc())
d_resamp(new mmse_fir_interpolator_cc()),
d_last_original_offset(0)
{
this->set_tag_propagation_policy(TPP_DONT);
if(resamp_ratio <= 0)
@ -79,61 +80,72 @@ namespace gr {
{
const gr_complex *in = (const gr_complex*)input_items[0];
gr_complex *out = (gr_complex*)output_items[0];
uint64_t processed_in = 0; //input samples processed in the last call to resample function
uint64_t processed_in_sum = 0; //input samples processed during a whole call to general_work function
uint64_t produced_out_sum = 0; //output samples produced during a whole call to general_work function
std::vector<tag_t> set_resamp_ratio_tags;
std::vector<tag_t> tags;
pmt::pmt_t key = pmt::string_to_symbol("set_resamp_ratio");
get_tags_in_window(set_resamp_ratio_tags, 0, 0, ninput_items[0]);
bool all_output_samples_produced = false;
for(std::vector<tag_t>::iterator i_tag = set_resamp_ratio_tags.begin(); i_tag < set_resamp_ratio_tags.end(); i_tag++)
get_tags_in_range(tags, 0, nitems_read(0), nitems_read(0)+ninput_items[0]);
bool out_buffer_full = false;
for(std::vector<tag_t>::iterator i_tag = tags.begin(); i_tag != tags.end(); i_tag++)
{
uint64_t tag_offset_rel = i_tag->offset - nitems_read(0);
if(pmt::symbol_to_string(i_tag->key) == "set_resamp_ratio")
{
uint64_t samples_to_produce = static_cast<uint64_t>(round(static_cast<double>(tag_offset_rel-processed_in_sum)/d_mu_inc)); //tu może być problem - bo to jest głupota przy d_mu_inc różnym od 1.0
if( (samples_to_produce + produced_out_sum) > noutput_items)
uint64_t samples_to_produce = static_cast<uint64_t>(round(static_cast<double>(tag_offset_rel-processed_in_sum)/d_mu_inc));
if((samples_to_produce + produced_out_sum) > noutput_items)
{
samples_to_produce = noutput_items - produced_out_sum;
all_output_samples_produced = true;
out_buffer_full = true;
}
processed_in = resample(in, processed_in_sum, out, produced_out_sum, samples_to_produce);
processed_in_sum = processed_in_sum + processed_in;
produced_out_sum = produced_out_sum + samples_to_produce;
if(all_output_samples_produced)
if(out_buffer_full)
{
break;
} else {
add_item_tag(0, produced_out_sum + nitems_written(0), i_tag->key, i_tag->value);
set_resamp_ratio(pmt::to_double(i_tag->value));
set_resamp_ratio(pmt::to_double(i_tag->value));
tag_t original_offset_tag;
uint64_t offset = produced_out_sum + nitems_written(0);
if(d_last_original_offset != offset){ //prevent from sending multiple "original_offset" tags for the same offset
add_item_tag(0, offset, pmt::mp("original_offset"), pmt::from_uint64(i_tag->offset));
d_last_original_offset = offset;
}
add_item_tag(0, offset, i_tag->key, i_tag->value);
}
} else {
uint64_t out_samples_to_tag = round(static_cast<double>(tag_offset_rel-processed_in_sum)/d_mu_inc);
if( (out_samples_to_tag + produced_out_sum) <= noutput_items)
if( (out_samples_to_tag + produced_out_sum) < noutput_items)
{
add_item_tag(0, produced_out_sum + out_samples_to_tag + nitems_written(0), i_tag->key, i_tag->value);
uint64_t offset = produced_out_sum + out_samples_to_tag + nitems_written(0);
if(d_last_original_offset != offset){
add_item_tag(0, offset, pmt::mp("original_offset"), pmt::from_uint64(i_tag->offset));
d_last_original_offset = offset;
}
add_item_tag(0, offset, i_tag->key, i_tag->value);
}
}
}
if(!all_output_samples_produced)
if(!out_buffer_full)
{
processed_in = resample(in, processed_in_sum, out, produced_out_sum, (noutput_items-produced_out_sum));
processed_in_sum = processed_in_sum + processed_in;
}
consume_each(processed_in_sum);
return noutput_items;
}
inline uint64_t
controlled_fractional_resampler_cc_impl::resample(const gr_complex *in, uint64_t first_in_sample, gr_complex *out, uint64_t first_out_sample, uint64_t samples_to_produce)
{
@ -142,7 +154,7 @@ namespace gr {
while(oo < (first_out_sample+samples_to_produce)) //produce samples_to_produce number of samples
{
out[oo++] = d_resamp->interpolate(&in[ii], d_mu);
double s = d_mu + d_mu_inc;
double f = floor(s);
int incr = (int)f;

View File

@ -37,6 +37,7 @@ namespace gr {
float d_mu;
float d_mu_inc;
mmse_fir_interpolator_cc *d_resamp;
uint64_t d_last_original_offset;
inline uint64_t resample(const gr_complex *in,
uint64_t first_in_sample,

View File

@ -29,7 +29,7 @@
#define GSM_SYM_RATE (13.0e6 / 48.0)
#define GSM_TS_PERIOD (156.25 / GSM_SYM_RATE)
#define GSM_FN_PERIOD (8 * GSM_TS_PERIOD)
#define GSM_FRAME_PERIOD (8 * GSM_TS_PERIOD)
namespace gr {
namespace gsm {
@ -62,7 +62,7 @@ namespace gr {
time_spec_t time_diff_hint)
{
int frames_diff, fn_delta;
frames_diff = int(round(time_diff_hint.get_real_secs() / GSM_FN_PERIOD));
frames_diff = int(round(time_diff_hint.get_real_secs() / GSM_FRAME_PERIOD));
fn_delta = fnmod_delta(fn, fn_ref + frames_diff) + frames_diff;
return fn_delta;
@ -80,11 +80,14 @@ namespace gr {
* @return difference between fn_ref and fn
*/
time_format fn_time_delta_cpp(uint32_t fn_ref, time_format time_ref, uint32_t fn_x,
time_format time_hint, uint32_t ts_num, uint32_t ts_ref)
time_format time_hint, uint32_t ts_num, uint32_t ts_ref, double clock_error)
{
time_spec_t time_diff_hint = time_spec_t(time_hint.first, time_hint.second) - time_spec_t(time_ref.first, time_ref.second);
time_spec_t time_diff_hint = time_spec_t(time_hint.first, time_hint.second)
- time_spec_t(time_ref.first, time_ref.second);
int fn_delta = fn_time_diff_delta(fn_x, fn_ref, time_diff_hint);
time_spec_t time_x_precise = fn_delta * GSM_FN_PERIOD + time_spec_t(time_ref.first, time_ref.second) + (static_cast<int>(ts_num) - static_cast<int>(ts_ref)) * GSM_TS_PERIOD;
time_spec_t time_x_precise = (fn_delta * GSM_FRAME_PERIOD)*(1.0-clock_error)
+ time_spec_t(time_ref.first, time_ref.second)
+ (static_cast<int>(ts_num) - static_cast<int>(ts_ref)) * GSM_TS_PERIOD;
return time_format(time_x_precise.get_full_secs(), time_x_precise.get_frac_secs());
}

View File

@ -0,0 +1,70 @@
/* -*- c++ -*- */
/* @file
* @author Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
//#include "grgsm/misc_utils/freq_hopping_utils.h"
#include <cmath>
namespace gr {
namespace gsm {
unsigned char RNTABLE[114] = {
48, 98, 63, 1, 36, 95, 78, 102, 94, 73, \
0, 64, 25, 81, 76, 59, 124, 23, 104, 100, \
101, 47, 118, 85, 18, 56, 96, 86, 54, 2, \
80, 34, 127, 13, 6, 89, 57, 103, 12, 74, \
55, 111, 75, 38, 109, 71, 112, 29, 11, 88, \
87, 19, 3, 68, 110, 26, 33, 31, 8, 45, \
82, 58, 40, 107, 32, 5, 106, 92, 62, 67, \
77, 108, 122, 37, 60, 66, 121, 42, 51, 126, \
117, 114, 4, 90, 43, 52, 53, 113, 120, 72, \
16, 49, 7, 79, 119, 61, 22, 84, 9, 97, \
91, 15, 21, 24, 46, 39, 93, 105, 65, 70, \
125, 99, 17, 123 \
};
int calculate_ma_sfh(int maio, int hsn, int n, int fn)
{
int mai = 0;
int s = 0;
int nbin = floor(log2(n) + 1);
int t1 = fn / 1326;
int t2 = fn % 26;
int t3 = fn % 51;
if (hsn == 0){
mai = (fn + maio) % n;
} else {
int t1r = t1 % 64;
int m = t2 + RNTABLE[(hsn ^ t1r) + t3];
int mprim = m % (1 << nbin);
int tprim = t3 % (1 << nbin);
if (mprim < n){
s = mprim;
} else {
s = (mprim + tprim) % n;
}
mai = (s + maio) % n;
}
return (mai);
}
} // namespace gsm
} // namespace gr

View File

@ -0,0 +1,36 @@
/* -*- c++ -*- */
/* @file
* @author Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef INCLUDED_FREQ_HOPPING_UTILS_H
#define INCLUDED_FREQ_HOPPING_UTILS_H
namespace gr {
namespace gsm {
/*
* Slow Frequency Hopping (SFH) MAI calculation based
* on airprobe-hopping by Bogdan Diaconescu.
*/
int calculate_ma_sfh(int maio, int hsn, int n, int fn);
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_FREQ_HOPPING_UTILS_H */

View File

@ -46,26 +46,27 @@ namespace gr {
gsmtap_hdr * header = (gsmtap_hdr *)message_plus_header;
uint32_t frame_nr = be32toh(header->frame_number);
std::cout << d_prepend_string;
std::ostringstream out;
out << d_prepend_string;
if (d_prepend_fnr)
{
std::cout << frame_nr;
out << frame_nr;
}
if (d_prepend_fnr && d_prepend_frame_count)
{
std::cout << " ";
out << " ";
}
if (d_prepend_frame_count)
{
// calculate fn count using libosmogsm
std::cout << osmo_a5_fn_count(frame_nr);
out << osmo_a5_fn_count(frame_nr);
}
if (d_prepend_fnr || d_prepend_frame_count)
{
std::cout << ": ";
out << ": ";
}
int start_index = sizeof(gsmtap_hdr);
@ -77,9 +78,11 @@ namespace gr {
for(int ii=start_index; ii<message_plus_header_len; ii++)
{
printf(" %02x", message_plus_header[ii]);
out<<" "<<(std::hex)<<std::setw(2)<<std::setfill('0')<<(uint32_t)message_plus_header[ii];
}
std::cout << std::endl;
out << std::endl;
std::cout << out.str() << std::flush;
}
message_printer::sptr

View File

@ -59,7 +59,7 @@ namespace gr {
time_spec_t time_spec_t::from_ticks(long long ticks, double tick_rate){
const long long rate_i = (long long)(tick_rate);
const double rate_f = tick_rate - rate_i;
const time_t secs_full = time_t(ticks/rate_i);
const time_t secs_full = (rate_i != 0 ? time_t(ticks/rate_i) : time_t(0));
const long long ticks_error = ticks - (secs_full*rate_i);
const double ticks_frac = ticks_error - secs_full*rate_f;
return time_spec_t(secs_full, ticks_frac/tick_rate);

View File

@ -30,7 +30,7 @@
#include <pmt/pmt.h>
#include <boost/lexical_cast.hpp>
#include "udp_socket.h"
#include "grgsm/misc_utils/udp_socket.h"
using boost::asio::ip::udp;
@ -38,8 +38,9 @@ namespace gr {
namespace gsm {
udp_socket::udp_socket(
const std::string &remote_addr,
const std::string &bind_addr,
const std::string &src_port,
const std::string &remote_addr,
const std::string &dst_port,
size_t mtu)
{
@ -50,7 +51,7 @@ namespace gr {
udp::resolver resolver(d_io_service);
udp::resolver::query rx_query(
udp::v4(), remote_addr, src_port,
udp::v4(), bind_addr, src_port,
boost::asio::ip::resolver_query_base::passive);
udp::resolver::query tx_query(
udp::v4(), remote_addr, dst_port,

View File

@ -31,8 +31,6 @@
#include <grgsm/gsmtap.h>
#include <grgsm/endian.h>
#define PMT_SIZE 174
namespace gr {
namespace gsm {
@ -110,8 +108,6 @@ namespace gr {
void burst_source_impl::run()
{
char *unserialized = (char*)malloc(sizeof(char) * PMT_SIZE);
for (int i=0; i<d_burst_data.size(); i++)
{
if (d_burst_data[i].length() == BURST_SIZE &&

View File

@ -29,6 +29,7 @@
#include <grgsm/endian.h>
#include <boost/algorithm/clamp.hpp>
#include "cx_channel_hopper_impl.h"
#include "../misc_utils/freq_hopping_utils.h"
namespace gr {
namespace gsm {
@ -82,57 +83,6 @@ namespace gr {
{
}
/**
* Random number table used for calculating the
* hopping sequence. Defined in GSM 05.02.
*/
unsigned char RNTABLE[114] = {
48, 98, 63, 1, 36, 95, 78, 102, 94, 73, \
0, 64, 25, 81, 76, 59, 124, 23, 104, 100, \
101, 47, 118, 85, 18, 56, 96, 86, 54, 2, \
80, 34, 127, 13, 6, 89, 57, 103, 12, 74, \
55, 111, 75, 38, 109, 71, 112, 29, 11, 88, \
87, 19, 3, 68, 110, 26, 33, 31, 8, 45, \
82, 58, 40, 107, 32, 5, 106, 92, 62, 67, \
77, 108, 122, 37, 60, 66, 121, 42, 51, 126, \
117, 114, 4, 90, 43, 52, 53, 113, 120, 72, \
16, 49, 7, 79, 119, 61, 22, 84, 9, 97, \
91, 15, 21, 24, 46, 39, 93, 105, 65, 70, \
125, 99, 17, 123 \
};
/*
* Slow Frequency Hopping (SFH) MAI calculation based
* on airprobe-hopping by Bogdan Diaconescu.
*/
int cx_channel_hopper_impl::calculate_ma_sfh(int maio, int hsn, int n, int fn)
{
int mai = 0;
int s = 0;
int nbin = floor(log2(n) + 1);
int t1 = fn / 1326;
int t2 = fn % 26;
int t3 = fn % 51;
if (hsn == 0)
mai = (fn + maio) % n;
else {
int t1r = t1 % 64;
int m = t2 + RNTABLE[(hsn ^ t1r) + t3];
int mprim = m % (1 << nbin);
int tprim = t3 % (1 << nbin);
if (mprim < n)
s = mprim;
else
s = (mprim + tprim) % n;
mai = (s + maio) % n;
}
return (mai);
}
/**
* Given MA, MAIO, HSN, and FN, decide which frames
* to forward to the demapper.
@ -147,10 +97,10 @@ namespace gr {
//in order to leave only ARFCN number
int mai = calculate_ma_sfh(d_maio, d_hsn, d_narfcn, frame_nr);
/*
if(d_ma[mai] == (int)frame_ca) {
message_port_pub(pmt::mp("bursts"), msg);
}
}*/
}
} /* namespace gsm */

View File

@ -28,7 +28,6 @@
namespace gr {
namespace gsm {
class cx_channel_hopper_impl : public cx_channel_hopper
{
private:
@ -37,7 +36,6 @@ namespace gr {
int d_hsn; // Hopping Sequence Number
int d_narfcn; // Length of d_ma
int calculate_ma_sfh(int maio, int hsn, int n, int fn);
void assemble_bursts(pmt::pmt_t msg);
public:

View File

@ -43,12 +43,13 @@
#if 0
/* Files included for debuging */
#include "plotting/plotting.hpp"
#include "grgsm/plotting.hpp"
#include <pthread.h>
#include <iomanip>
#endif
#define SYNC_SEARCH_RANGE 30
#define SYNC_SEARCH_RANGE 50
#define SYNC_START (BURST_SIZE/2-(SYNC_SEARCH_RANGE/2)-(N_SYNC_BITS/2) + 8)
namespace gr
{
@ -58,24 +59,36 @@ namespace gr
/* The public constructor */
receiver::sptr
receiver::make(
int osr, const std::vector<int> &cell_allocation,
const std::vector<int> &tseq_nums, bool process_uplink)
int osr,
const std::vector<int> &cell_allocation,
const std::vector<int> &tseq_nums,
double resamp_rate,
bool process_uplink
)
{
return gnuradio::get_initial_sptr
(new receiver_impl(osr, cell_allocation,
tseq_nums, process_uplink));
return gnuradio::get_initial_sptr(
new receiver_impl(
osr,
cell_allocation,
tseq_nums,
resamp_rate,
process_uplink)
);
}
/* The private constructor */
receiver_impl::receiver_impl(
int osr, const std::vector<int> &cell_allocation,
const std::vector<int> &tseq_nums, bool process_uplink
int osr,
const std::vector<int> &cell_allocation,
const std::vector<int> &tseq_nums,
double resamp_rate,
bool process_uplink
) : gr::sync_block("receiver",
gr::io_signature::make(1, -1, sizeof(gr_complex)),
gr::io_signature::make(0, 0, 0)),
d_samples_consumed(0),
d_rx_time_received(false),
d_time_samp_ref(GSM_SYMBOL_RATE * osr),
d_time_samp_ref(osr*GSM_SYMBOL_RATE, resamp_rate),
d_OSR(osr),
d_process_uplink(process_uplink),
d_chan_imp_length(CHAN_IMP_RESP_LENGTH),
@ -140,34 +153,16 @@ namespace gr
gr_vector_void_star &output_items)
{
gr_complex *input = (gr_complex *) input_items[0];
uint64_t start = nitems_read(0);
uint64_t stop = start + noutput_items;
/* Frequency correction loop */ //TODO: move this to FCCH burst processing
d_freq_offset_tag_in_fcch = false;
#if 0
/* FIXME: jak zrobić to rzutowanie poprawnie */
std::vector<const gr_complex *> iii =
(std::vector<const gr_complex *>) input_items;
#endif
/* Time synchronization loop */
float current_time =
static_cast<float>(start / (GSM_SYMBOL_RATE * d_OSR));
if ((current_time - d_last_time) > 0.1) {
pmt::pmt_t msg = pmt::make_tuple(pmt::mp("current_time"),
pmt::from_double(current_time));
message_port_pub(pmt::mp("measurements"), msg);
d_last_time = current_time;
}
/* Frequency correction loop */
std::vector<tag_t> freq_offset_tags;
pmt::pmt_t key = pmt::string_to_symbol("setting_freq_offset");
get_tags_in_range(freq_offset_tags, 0, start, stop, key);
get_tags_in_window(freq_offset_tags, 0, 0, noutput_items, key);
if (!freq_offset_tags.empty()) {
tag_t freq_offset_tag = freq_offset_tags[0];
uint64_t tag_offset = freq_offset_tag.offset - start;
uint64_t tag_offset = freq_offset_tag.offset - nitems_read(0);
d_freq_offset_setting = pmt::to_double(freq_offset_tag.value);
burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr);
@ -177,10 +172,6 @@ namespace gr
d_freq_offset_tag_in_fcch = tag_offset < last_sample_nr;
}
}
/* Obtaining current time with use of rx_time tag provided i.e. by UHD devices */
/* And storing it in time_sample_ref for sample number to time conversion */
std::vector<tag_t> rx_time_tags;
/* Main state machine */
d_samples_consumed = 0;
@ -192,21 +183,47 @@ namespace gr
sch_search_handler(input, noutput_items);
break;
case synchronized:
synchronized_handler(input, input_items, noutput_items);
synchronized_handler(input_items, noutput_items);
break;
}
get_tags_in_window(rx_time_tags, 0, 0, d_samples_consumed, pmt::string_to_symbol("rx_time"));
if(!rx_time_tags.empty()){
d_rx_time_received = true;
tag_t rx_time_tag = *(rx_time_tags.begin());
/* Time synchronization */
/* Obtaining current time with use of rx_time tag provided i.e. by UHD devices */
/* and storing it in time_sample_ref for sample number to time conversion */
uint64_t rx_time_full_part = to_uint64(tuple_ref(rx_time_tag.value,0));
double rx_time_frac_part = to_double(tuple_ref(rx_time_tag.value,1));
time_spec_t current_rx_time = time_spec_t(rx_time_full_part, rx_time_frac_part);
uint64_t current_start_offset = rx_time_tag.offset;
d_time_samp_ref.update(current_rx_time, current_start_offset);
typedef std::vector<tag_t>::iterator tag_iter;
std::vector<tag_t> all_tags;
get_tags_in_window(all_tags, 0, 0, d_samples_consumed);
for(tag_iter itag = all_tags.begin(); itag != all_tags.end(); itag++){
if(pmt::eqv(itag->key, pmt::mp("set_resamp_ratio"))){
double resamp_rate = pmt::to_double(itag->value);
uint64_t N = get_offset_before_resampler(itag->offset);
d_time_samp_ref.update(itag->offset, N, resamp_rate);
}
else if(pmt::eqv(itag->key, pmt::mp("rx_time"))){
d_rx_time_received = true;
uint64_t N = get_offset_before_resampler(itag->offset);
time_spec_t rx_time = time_spec_t(
pmt::to_uint64(tuple_ref(itag->value,0)),
pmt::to_double(tuple_ref(itag->value,1))
);
d_time_samp_ref.update(itag->offset, N, rx_time);
}
// else if(pmt::eqv(itag->key, pmt::mp("rx_rate"))){ //to jest źle TODO extra: zamienić to na update samp_rate dal clock_offset_controllera, a on następnie ustawiałby resamp_rate w sterowanym resamplerze
// d_rx_time_received = true;
// d_time_samp_ref.set_samp_rate(pmt::to_double(itag->value));
// }
}
/* Send updates of time for clock offset controller every 0.1 second */
time_spec_t current_time = d_time_samp_ref.convert_M_to_ideal_t(nitems_read(0));
if ((current_time - d_last_time).get_real_secs() > 0.1) {
pmt::pmt_t msg = pmt::make_tuple(pmt::mp("current_time"),
pmt::from_double(current_time.get_real_secs())); //TODO: przerobić to na parę pmt i w bloku controllera przerabiać to na time_spec_t
message_port_pub(pmt::mp("measurements"), msg);
d_last_time = current_time;
}
return d_samples_consumed;
@ -269,7 +286,7 @@ namespace gr
d_burst_nr++;
/* Consume samples up to the next guard period */
unsigned int to_consume = burst_start + BURST_SIZE * d_OSR + 4 * d_OSR;
unsigned int to_consume = burst_start + BURST_SIZE * d_OSR + 4 * d_OSR + 2 * d_OSR + 1; //TODO: figure out where + 2 * d_OSR + 1 comes from
// consume_each(to_consume);
d_samples_consumed += to_consume;
@ -278,13 +295,14 @@ namespace gr
}
void
receiver_impl::synchronized_handler(gr_complex *input,
gr_vector_const_void_star &input_items, int noutput_items)
receiver_impl::synchronized_handler(gr_vector_const_void_star &input_items,
int noutput_items)
{
/**
* In this state receiver is synchronized and it processes
* bursts according to burst type for given burst number
*/
gr_complex *input = (gr_complex *) input_items[0];
std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH * d_OSR);
size_t inputs_to_process = d_cell_allocation.size();
unsigned char output_binary[BURST_SIZE];
@ -325,7 +343,7 @@ namespace gr
/* Extract frequency offset */
const unsigned first_sample =
ceil((GUARD_PERIOD + 2 * TAIL_BITS) * d_OSR) + 1;
ceil((GUARD_PERIOD/2 + TAIL_BITS) * d_OSR) + 1;
const unsigned last_sample =
first_sample + USEFUL_BITS * d_OSR - TAIL_BITS * d_OSR;
double freq_offset_tmp =
@ -367,25 +385,23 @@ namespace gr
pmt::from_double(0.0),pmt::mp("sync_loss"));
message_port_pub(pmt::mp("measurements"), msg);
}
} else {
/* Compose a message with GSMTAP header and bits */
send_burst(d_burst_nr, output_binary,
GSMTAP_BURST_SCH, input_nr, d_c0_burst_start);
break;
/**
* Decoding was successful, now
* compute offset from burst_start,
* burst should start after a guard period.
*/
offset = d_c0_burst_start - 2 * d_OSR; //TODO: figure out the -2 * d_OSR part
to_consume += offset;
d_failed_sch = 0;
}
/* Compose a message with GSMTAP header and bits */
send_burst(d_burst_nr, output_binary,
GSMTAP_BURST_SCH, input_nr, d_c0_burst_start);
/**
* Decoding was successful, now
* compute offset from burst_start,
* burst should start after a guard period.
*/
offset = d_c0_burst_start - floor((GUARD_PERIOD) * d_OSR);
to_consume += offset;
d_failed_sch = 0;
break;
}
break;
case normal_burst:
{
@ -769,8 +785,10 @@ namespace gr
int burst_start;
float energy = 0;
int len = (SYNC_POS + SYNC_SEARCH_RANGE) * d_OSR;
for (int ii = SYNC_POS * d_OSR; ii < len; ii++) {
int len = (SYNC_SEARCH_RANGE) * d_OSR;
int start = (SYNC_START) * d_OSR;
int stop = start + len;
for (int ii = start; ii < stop; ii++) {
gr_complex correlation = correlate_sequence(&d_sch_training_seq[5],
N_SYNC_BITS - 10, &input[ii]);
correlation_buffer.push_back(correlation);
@ -823,7 +841,7 @@ namespace gr
}
burst_start = strongest_window_nr + chan_imp_resp_center
- 48 * d_OSR - 2 * d_OSR + 2 + SYNC_POS * d_OSR;
- 48 * d_OSR - 2 * d_OSR + 2 + SYNC_START * d_OSR;
return burst_start;
}
@ -1047,12 +1065,15 @@ namespace gr
pmt::pmt_t pdu_header = pmt::make_dict();
/* Add timestamp of the first sample - if available */
if(d_rx_time_received) {
time_spec_t time_spec_of_first_sample = d_time_samp_ref.offset_to_time(nitems_read(0)+burst_start);
if(d_rx_time_received && burst_start != -1) {
time_spec_t time_spec_of_first_sample =
d_time_samp_ref.convert_M_to_t(nitems_read(0) + burst_start);
uint64_t full = time_spec_of_first_sample.get_full_secs();
double frac = time_spec_of_first_sample.get_frac_secs();
pdu_header =
pmt::dict_add(pdu_header, pmt::mp("fn_time"),
int64_t N = d_time_samp_ref.convert_M_to_N(nitems_read(0) + burst_start);
time_spec_t ref_t = d_time_samp_ref.get_ref_time();
pdu_header =
pmt::dict_add(pdu_header, pmt::mp("fn_time"),
pmt::cons(
pmt::cons(pmt::from_uint64(be32toh(frame_number)), pmt::from_uint64(tn)),
pmt::cons(pmt::from_uint64(full), pmt::from_double(frac))));

View File

@ -54,7 +54,7 @@ namespace gr {
gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
float d_last_time;
time_spec_t d_last_time;
/** Counts samples consumed by the receiver
*
@ -209,11 +209,29 @@ namespace gr {
/* State machine handlers */
void fcch_search_handler(gr_complex *input, int noutput_items);
void sch_search_handler(gr_complex *input, int noutput_items);
void synchronized_handler(gr_complex *input,
gr_vector_const_void_star &input_items, int noutput_items);
void synchronized_handler(gr_vector_const_void_star &input_items, int noutput_items);
uint64_t get_offset_before_resampler(uint64_t offset){
std::vector<tag_t> original_ofsets;
get_tags_in_window(original_ofsets, 0, offset, offset+1, pmt::intern("original_offset"));
uint64_t offset_recovered = d_time_samp_ref.convert_M_to_N(offset);
uint64_t offset_before_resampler = 0;
if(!original_ofsets.empty()){
offset_before_resampler = pmt::to_uint64(original_ofsets[0].value);
} else {
offset_before_resampler = offset_recovered;
}
return offset_before_resampler;
};
public:
receiver_impl(int osr, const std::vector<int> &cell_allocation, const std::vector<int> &tseq_nums, bool process_uplink);
receiver_impl(
int osr,
const std::vector<int> &cell_allocation,
const std::vector<int> &tseq_nums,
double resamp_rate,
bool process_uplink
);
~receiver_impl();
int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items);

View File

@ -25,35 +25,17 @@
namespace gr {
namespace gsm {
time_sample_ref::time_sample_ref(double samp_rate): d_samp_rate(samp_rate)
time_sample_ref::time_sample_ref(double samp_rate, double resamp_rate):
d_samp_rate(samp_rate),
d_resamp_rate(resamp_rate),
d_ref_time(0.0),
d_ref_offset(0),
d_original_ref_offset(0)
{
}
time_sample_ref::~time_sample_ref()
{
}
void time_sample_ref::update(time_spec_t last_rx_time, uint64_t current_start_offset)
{
d_last_rx_time = last_rx_time;
d_current_start_offset = current_start_offset;
}
time_spec_t time_sample_ref::offset_to_time(uint64_t offset)
{
uint64_t samples_from_last_rx_time = offset - d_current_start_offset;
time_spec_t time = time_spec_t(static_cast<double>(samples_from_last_rx_time)/d_samp_rate) + d_last_rx_time;
return time;
}
uint64_t time_sample_ref::time_to_offset(time_spec_t time)
time_sample_ref::~time_sample_ref()
{
double samples_since_last_rx_time_tag = (time-d_last_rx_time).get_real_secs()*d_samp_rate;
// double fractional_part = round(samples_since_last_rx_time_tag) - samples_since_last_rx_time_tag;
uint64_t offset = static_cast<uint64_t>(round(samples_since_last_rx_time_tag)) + d_current_start_offset;
return offset;
}
} // namespace gsm
} // namespace gr

View File

@ -29,20 +29,79 @@
namespace gr {
namespace gsm {
/*
Class for storing time reference and for conversions time<->sample number
Class for storing time reference and for conversions time<->sample number.
N - number of sample before the resampler
M - number of sample after the resampler
t - time before the resampler
*/
class time_sample_ref
{
private:
double d_samp_rate;
time_spec_t d_last_rx_time;
uint64_t d_current_start_offset;
double d_samp_rate;
double d_resamp_rate;
time_spec_t d_ref_time;
uint64_t d_ref_offset;/**< sample offset of the reference point */
uint64_t d_original_ref_offset;/**< sample offset of the
reference point before resampler */
public:
time_sample_ref(double samp_rate);
~time_sample_ref();
void update(time_spec_t last_rx_time, uint64_t current_start_offset);
time_spec_t offset_to_time(uint64_t offset);
uint64_t time_to_offset(time_spec_t time);
time_sample_ref(double samp_rate, double resamp_rate = 1.0);
~time_sample_ref();
void update(uint64_t M, uint64_t N){
d_ref_time = time_spec_t::from_ticks(N - d_original_ref_offset, d_samp_rate*d_resamp_rate) + d_ref_time;
d_original_ref_offset = N;
d_ref_offset = M;
}
void update(uint64_t M, uint64_t N, double resamp_rate){
// std::cout << "M:" << M << " N:" << N << " resamp_rate:" << resamp_rate << std::endl;
update(M, N);
d_resamp_rate = resamp_rate;
}
void update(uint64_t M, uint64_t N, time_spec_t ref_time){
// std::cout << "M:" << M << " N:" << N << " ref_time" << ref_time.get_real_secs() << std::endl;
update(M, N);
d_ref_time = ref_time;
}
void set_samp_rate(double samp_rate){
d_samp_rate = samp_rate;
}
uint64_t convert_M_to_N(uint64_t M){
time_spec_t new_N_tspec =
time_spec_t::from_ticks(M - d_ref_offset, 1.0/d_resamp_rate) +
time_spec_t::from_ticks(d_original_ref_offset, 1.0);
uint64_t new_N = (round(new_N_tspec.get_real_secs()));
// std::cout << "d_ref_offset:" << d_ref_offset << " d_resamp_rate:" << d_resamp_rate << " d_ref_offset" << d_ref_offset << " d_original_ref_offset:" << d_original_ref_offset << std::endl;
return new_N;
}
time_spec_t convert_M_to_t(uint64_t M){
return time_spec_t::from_ticks(convert_M_to_N(M) -
d_original_ref_offset, d_samp_rate*d_resamp_rate) + d_ref_time;
}
time_spec_t convert_M_to_ideal_t(uint64_t M){
return time_spec_t::from_ticks(M, d_samp_rate);
}
time_spec_t get_ref_time(){
return d_ref_time;
}
uint64_t convert_t_to_M(time_spec_t time)
{
uint64_t samples_since_ref_time_tag = (time-d_ref_time).to_ticks(d_samp_rate);
uint64_t offset = samples_since_ref_time_tag + d_ref_offset;
return offset;
}
uint64_t convert_t_to_N(time_spec_t time)
{
return convert_M_to_N(convert_t_to_M(time));
}
};
} // namespace gsm
} // namespace gr

View File

@ -25,6 +25,7 @@
#endif
#include <gnuradio/io_signature.h>
#include <grgsm/endian.h>
#include <grgsm/gsmtap.h>
#include <grgsm/gsm_constants.h>
#include "gen_test_ab_impl.h"

View File

@ -26,6 +26,7 @@
#endif
#include <gnuradio/io_signature.h>
#include <grgsm/endian.h>
#include <grgsm/gsmtap.h>
#include "txtime_setter_impl.h"
@ -116,52 +117,56 @@ namespace gr {
void txtime_setter_impl::process_txtime_of_burst(pmt::pmt_t msg_in)
{
if (d_fn_ref != UNKNOWN_FN)
{
pmt::pmt_t blob = pmt::cdr(msg_in);
pmt::pmt_t blob = pmt::cdr(msg_in);
// Extract GSMTAP header from message
gsmtap_hdr *header = (gsmtap_hdr *) pmt::blob_data(blob);
uint32_t frame_nr = be32toh(header->frame_number);
uint32_t ts_num = header->timeslot;
// Extract GSMTAP header from message
gsmtap_hdr *header = (gsmtap_hdr *) pmt::blob_data(blob);
uint32_t frame_nr = be32toh(header->frame_number);
uint32_t ts_num = header->timeslot;
time_format txtime = fn_time_delta_cpp(d_fn_ref, d_time_ref,
frame_nr, d_time_hint, ts_num, d_ts_ref);
if (d_fn_ref == UNKNOWN_FN) {
std::cout << "Missing reference TDMA frame number, dropping "
<< "burst (fn=" << frame_nr << ", tn=" << ts_num << ")"
<< std::endl;
return;
}
time_spec_t txtime_spec = time_spec_t(txtime.first, txtime.second);
txtime_spec -= d_delay_correction;
txtime_spec -= d_timing_advance;
time_format txtime = fn_time_delta_cpp(d_fn_ref, d_time_ref,
frame_nr, d_time_hint, ts_num, d_ts_ref);
time_spec_t current_time_estimate = time_spec_t(d_time_hint.first, d_time_hint.second);
time_spec_t txtime_spec = time_spec_t(txtime.first, txtime.second);
txtime_spec -= d_delay_correction;
txtime_spec -= d_timing_advance;
if (txtime_spec <= current_time_estimate) { // Drop too late bursts
std::cout << "lB" << std::flush;
} else if (txtime_spec > current_time_estimate + MAX_EARLY_TIME_DIFF) { // Drop too early bursts
std::cout << "eB" << std::flush; //TODO: too early condition might happen when changing BTSes.
//Wrong fn_time is applied to new or old bursts in such situation.
//This solution is not perfect as MS might be blocked upto
//MAX_EARLY_TIME_DIFF seconds.
//Better solution would be to indentify fn_time and burst coming
//from given BTS (i.e. based on ARFCN) and dropping bursts for which
//the bts_id doesn't match with bts_id of fn_time.
} else { //process bursts that are in the right time-frame
pmt::pmt_t tags_dict = pmt::dict_add(
pmt::make_dict(),
pmt::intern("tx_time"),
pmt::make_tuple(
pmt::from_uint64(txtime_spec.get_full_secs()),
pmt::from_double(txtime_spec.get_frac_secs()))
);
time_spec_t current_time_estimate = time_spec_t(d_time_hint.first, d_time_hint.second);
tags_dict = pmt::dict_add(tags_dict,
pmt::intern("fn"), pmt::from_uint64(frame_nr));
tags_dict = pmt::dict_add(tags_dict,
pmt::intern("ts"), pmt::from_uint64(ts_num));
if (txtime_spec <= current_time_estimate) { // Drop too late bursts
std::cout << "lB" << std::flush;
} else if (txtime_spec > current_time_estimate + MAX_EARLY_TIME_DIFF) { // Drop too early bursts
std::cout << "eB" << std::flush; //TODO: too early condition might happen when changing BTSes.
//Wrong fn_time is applied to new or old bursts in such situation.
//This solution is not perfect as MS might be blocked upto
//MAX_EARLY_TIME_DIFF seconds.
//Better solution would be to indentify fn_time and burst coming
//from given BTS (i.e. based on ARFCN) and dropping bursts for which
//the bts_id doesn't match with bts_id of fn_time.
} else { //process bursts that are in the right time-frame
pmt::pmt_t tags_dict = pmt::dict_add(
pmt::make_dict(),
pmt::intern("tx_time"),
pmt::make_tuple(
pmt::from_uint64(txtime_spec.get_full_secs()),
pmt::from_double(txtime_spec.get_frac_secs()))
);
// Send a message to the output
pmt::pmt_t msg_out = pmt::cons(tags_dict, pmt::cdr(msg_in));
message_port_pub(pmt::mp("bursts_out"), msg_out);
}
tags_dict = pmt::dict_add(tags_dict,
pmt::intern("fn"), pmt::from_uint64(frame_nr));
tags_dict = pmt::dict_add(tags_dict,
pmt::intern("ts"), pmt::from_uint64(ts_num));
// Send a message to the output
pmt::pmt_t msg_out = pmt::cons(tags_dict, pmt::cdr(msg_in));
message_port_pub(pmt::mp("bursts_out"), msg_out);
}
}

25
lib/trx/CMakeLists.txt Normal file
View File

@ -0,0 +1,25 @@
# Copyright 2011,2012 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
add_sources(
trx_burst_if_impl.cc
freq_hopper_tag_impl.cc
freq_hopper_msg_impl.cc
)

View File

@ -0,0 +1,273 @@
/* -*- c++ -*- */
/* @file
* @author Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include <grgsm/gsmtap.h>
#include "freq_hopper_msg_impl.h"
#include "../misc_utils/freq_hopping_utils.h"
extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
namespace gr {
namespace gsm {
using namespace pmt;
freq_hopper_msg::sptr
freq_hopper_msg::make(
double samp_rate,
double start_fc_rx,
double start_fc_tx,
bool rx_hopping,
bool tx_hopping,
double freq_change_period)
{
return gnuradio::get_initial_sptr
(new freq_hopper_msg_impl(samp_rate, start_fc_rx, start_fc_tx,
rx_hopping, tx_hopping, freq_change_period));
}
/*
* The private constructor
*/
freq_hopper_msg_impl::freq_hopper_msg_impl(
double samp_rate,
double start_fc_rx,
double start_fc_tx,
bool rx_hopping,
bool tx_hopping,
double freq_change_period) :
sync_block("freq_hopper_msg",
gr::io_signature::make(1, 1, sizeof(gr_complex)),
gr::io_signature::make(0, 0, 0)),
d_samp_rate(samp_rate),
d_fc_rx(start_fc_rx),
d_fc_tx(start_fc_tx),
d_rx_hopping(rx_hopping),
d_tx_hopping(tx_hopping),
d_freq_change_period(freq_change_period),
d_rx_time_received(false),
d_last_rx_time(0.0),
d_current_time(0.0),
d_current_start_offset(0.0),
d_i(0),
d_start_time(1.0),
d_next_change_time(time_spec_t(2.0)),
d_change_advance(0.15),
d_rx_time_key(string_to_symbol("rx_time"))
// d_hopping_cmd(PMT_NIL),
// d_hopping_enable(false),
// d_base_freq(890e6)
{
// Register I/O ports
// message_port_register_in(mp("hopping_cmd"));
message_port_register_out(mp("control"));
// d_f.push_back(-0.3e6);
// d_f.push_back(-0.2e6);
// d_f.push_back(-0.1e6);
// d_f.push_back(0);
// d_f.push_back(0.1e6);
// d_f.push_back(0.2e6);
// d_f.push_back(0.3e6);
d_ma.push_back(51);
d_ma.push_back(2);
d_ma.push_back(37);
d_ma.push_back(45);
d_hsn = 0;
d_maio = 0;
d_current_fn = 0;
// // Bind message handlers
// set_msg_handler(mp("hopping_cmd"),
// boost::bind(&freq_hopper_msg_impl::add_hopping_cmd,
// this, _1));
// add_hopping_cmd(hopping_cmd);
}
/*
* Our virtual destructor.
*/
freq_hopper_msg_impl::~freq_hopper_msg_impl()
{
}
int freq_hopper_msg_impl::work(
int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
std::vector<tag_t> rx_time_tags;
get_tags_in_window(rx_time_tags, 0, 0, noutput_items, d_rx_time_key);
if(rx_time_tags.size() > 0){
tag_t rx_time_tag = rx_time_tags[0];
uint64_t rx_time_full_part = to_uint64(tuple_ref(rx_time_tag.value,0));
double rx_time_frac_part = to_double(tuple_ref(rx_time_tag.value,1));
d_last_rx_time = time_spec_t(rx_time_full_part, rx_time_frac_part);
d_current_start_offset = rx_time_tag.offset;
d_rx_time_received = true;
}
if(d_rx_time_received){
uint64_t buffer_end_offset = nitems_read(0) + noutput_items;
d_current_time = time_spec_t::from_ticks(buffer_end_offset - d_current_start_offset, d_samp_rate) + d_last_rx_time;
}
// Freq. control messages generation
if ((d_current_time.get_real_secs() > d_start_time) &&
((d_next_change_time-d_current_time).get_real_secs() < d_change_advance)){
pmt_t freq_change_time_pmt = cons(
from_uint64(d_next_change_time.get_full_secs()),
from_double(d_next_change_time.get_frac_secs())
);
d_next_change_time = d_next_change_time + d_freq_change_period;
double dsp_freq = get_dsp_freq(d_current_fn);
if(d_tx_hopping){
pmt_t tx_tune_msg = dict_add(make_dict(), mp("lo_freq"), from_double(d_fc_tx));
// tx_tune_msg = dict_add(tx_tune_msg, mp("dsp_freq"), from_double(d_f[d_i % d_f.size()]));
tx_tune_msg = dict_add(tx_tune_msg, mp("dsp_freq"), from_double(dsp_freq));
tx_tune_msg = dict_add(tx_tune_msg, mp("time"), freq_change_time_pmt);
tx_tune_msg = dict_add(tx_tune_msg, mp("direction"), mp("TX"));
message_port_pub(mp("control"), tx_tune_msg);
// std::cout << "tx: " << tx_tune_msg << std::endl;
}
if(d_rx_hopping){
pmt_t rx_tune_msg = dict_add(make_dict(), mp("lo_freq"), from_double(d_fc_rx));
// rx_tune_msg = dict_add(rx_tune_msg, mp("dsp_freq"), from_double(-d_f[d_i % d_f.size()]));
rx_tune_msg = dict_add(rx_tune_msg, mp("dsp_freq"), from_double(-dsp_freq));
rx_tune_msg = dict_add(rx_tune_msg, mp("time"), freq_change_time_pmt);
rx_tune_msg = dict_add(rx_tune_msg, mp("direction"), mp("RX"));
message_port_pub(mp("control"), rx_tune_msg);
// std::cout << "rx: " << rx_tune_msg << std::endl;
}
d_i++;
d_current_fn++;
}
return noutput_items;
}
double freq_hopper_msg_impl::get_dsp_freq(uint64_t fn){ //TODO: jak otrzymac fn
int mai = calculate_ma_sfh(d_maio, d_hsn, d_ma.size(), fn);
uint16_t arfcn = d_ma[mai];
double dsp_freq = static_cast<double>(gsm_arfcn2freq10(arfcn, 0)) * 1.0e5 - d_fc_rx;
}
/*
void freq_hopper_msg_impl::add_hopping_cmd(pmt_t cmd) //TODO: fn and discard not supported at the moment
{
if(dict_ref(cmd,intern("cmd"), PMT_NIL) == intern("start")) {
if(dict_ref(cmd,intern("fn"), PMT_NIL) != PMT_NIL){
//TODO add the command to the map<int,pmt_t>
} else {
pmt_t hopping_params = dict_ref(cmd, intern("hopping_params"), PMT_NIL);
d_hopping_enable = set_hopping_params(hopping_params);
}
} else if(dict_ref(cmd,intern("cmd"), PMT_NIL) == intern("stop")) {
if(dict_ref(cmd,intern("fn"),PMT_NIL) != PMT_NIL){
//TODO add the command to the map<int,pmt_t>
} else {
d_hopping_enable = false;
}
}
}
void freq_hopper_msg_impl::set_freq_metadata(pmt_t burst)
{
if(d_hopping_enable) {
pmt_t pdu_header = car(burst);
pmt_t tx_time = dict_ref(pdu_header, intern("tx_time"),PMT_NIL);
if(tx_time != PMT_NIL){
pmt_t tx_command_time = cons(tuple_ref(tx_time,0),tuple_ref(tx_time,1));
pmt_t header_plus_burst = cdr(burst);
uint32_t frame_nr = 0;
pmt_t fn = dict_ref(pdu_header,intern("fn"),PMT_NIL);
if(fn == PMT_NIL){
gsmtap_hdr *header = (gsmtap_hdr *)blob_data(header_plus_burst);
uint32_t frame_nr = be32toh(header->frame_number);
} else {
frame_nr = to_uint64(fn);
}
int mai = calculate_ma_sfh(d_maio, d_hsn, d_ma.size(), frame_nr);
uint16_t arfcn = d_ma[mai];
// if(fn == PMT_NIL){
// header->arfcn = htobe16(arfcn);
// header->arfcn = header->arfcn | 0x8000; // set uplink flag
// }
//compute the frequences to be set in the burst header
double freq_uplink = static_cast<double>(gsm_arfcn2freq10(arfcn, 1)) * 1.0e5;
double freq_downlink = static_cast<double>(gsm_arfcn2freq10(arfcn, 0)) * 1.0e5;
pmt_t tx_command = dict_add(make_dict(),intern("lo_freq"),from_double(d_base_freq));
tx_command = dict_add(tx_command,intern("dsp_freq"),from_double(freq_uplink-d_base_freq));
tx_command = dict_add(tx_command,intern("time"),tx_command_time);
pmt_t rx_command = dict_add(make_dict(),intern("lo_freq"),from_double(d_base_freq));
rx_command = dict_add(rx_command,intern("dsp_freq"),from_double(freq_uplink-d_base_freq));
rx_command = dict_add(rx_command,intern("time"),tx_command_time);
rx_command = dict_add(rx_command,intern("direction"),intern("RX"));
pdu_header = dict_add(pdu_header, intern("tx_command"),tx_command);
// pdu_header = dict_add(pdu_header, intern("tx_command"),rx_command);
// std::cout << "arfcn " << arfcn << " mai " << mai << " d_ma.size() " << d_ma.size() << " d_hsn " << d_hsn << std::endl;
std::cout << "arfcn_uplink " << arfcn << std::endl;
// std::cout << "freq_downlink " << freq_downlink << std::endl;
// std::cout << "pdu_header " << pdu_header << std::endl;
// std::cout << "size_header_plus_burst " << length(header_plus_burst) << std::endl;
message_port_pub(mp("bursts_out"), cons(pdu_header,header_plus_burst));
}
} else {
message_port_pub(mp("bursts_out"), burst);
}
}
bool freq_hopper_msg_impl::set_hopping_params(pmt_t hopping_params){
bool retval = false;
if(hopping_params != PMT_NIL){
//set hopping parameters immediately
pmt_t hsn = dict_ref(hopping_params, intern("hsn"), PMT_NIL);
pmt_t maio = dict_ref(hopping_params, intern("maio"), PMT_NIL);;
pmt_t ma = dict_ref(hopping_params, intern("ma"), PMT_NIL);
if(is_vector(ma) && is_integer(hsn) && is_integer(maio)){ //TODO: checking the values
d_hsn = to_uint64(hsn);
d_maio = to_uint64(maio);
d_ma.resize(length(ma));
for(int i=0; i<length(ma); i++){
d_ma[i] = to_uint64(vector_ref(ma,i));
}
retval = true;
}
}
return retval;
}*/
} /* namespace gsm */
} /* namespace gr */

View File

@ -0,0 +1,84 @@
/* -*- c++ -*- */
/* @file
* @author Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef INCLUDED_GSM_FREQ_HOPPER_MSG_IMPL_H
#define INCLUDED_GSM_FREQ_HOPPER_MSG_IMPL_H
#include <grgsm/trx/freq_hopper_msg.h>
#include <grgsm/misc_utils/time_spec.h>
#include <grgsm/misc_utils/fn_time.h>
#include <pmt/pmt.h>
namespace gr {
namespace gsm {
class freq_hopper_msg_impl : public freq_hopper_msg
{
private:
double d_samp_rate;
double d_fc_rx;
double d_fc_tx;
bool d_rx_hopping;
bool d_tx_hopping;
double d_freq_change_period;
bool d_rx_time_received;
time_spec_t d_last_rx_time;
time_spec_t d_current_time;
uint64_t d_current_start_offset; //!!zmienic nazwe - ta zmienna okresla pozycje ostatniego rx_time
uint64_t d_i;
std::vector<double> d_f;
double d_start_time;
time_spec_t d_next_change_time;
double d_change_advance;
pmt::pmt_t d_rx_time_key;
// bool d_hopping_enable; //if true block do the hopping, if not block just passes the bursts
uint64_t d_hsn; //hopping sequence number
uint64_t d_maio; //mobile allocation index offset
std::vector<uint64_t> d_ma; //mobile allocation
uint64_t d_current_fn;//!!
// pmt::pmt_t d_hopping_cmd; //TODO: change this a std::map
// void set_freq_metadata(pmt::pmt_t cmd);
// bool set_hopping_params(pmt::pmt_t hopping_params);
double get_dsp_freq(uint64_t fn);
public:
freq_hopper_msg_impl(
double samp_rate,
double start_fc_rx,
double start_fc_tx,
bool rx_hopping,
bool tx_hopping,
double freq_change_period);
~freq_hopper_msg_impl();
int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items);
// void add_hopping_cmd(pmt::pmt_t hopping_cmd);
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_FREQ_HOPPER_MSG_IMPL_H */

View File

@ -0,0 +1,178 @@
/* -*- c++ -*- */
/* @file
* @author Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include <grgsm/gsmtap.h>
#include "freq_hopper_tag_impl.h"
#include "../misc_utils/freq_hopping_utils.h"
extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
namespace gr {
namespace gsm {
using namespace pmt;
freq_hopper_tag::sptr
freq_hopper_tag::make(
pmt_t hopping_cmd)
{
return gnuradio::get_initial_sptr
(new freq_hopper_tag_impl(hopping_cmd));
}
/*
* The private constructor
*/
freq_hopper_tag_impl::freq_hopper_tag_impl(
pmt_t hopping_cmd
) : gr::block("freq_hopper_tag",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_hopping_cmd(PMT_NIL),
d_hopping_enable(false),
d_base_freq(890e6)
{
// Register I/O ports
message_port_register_in(mp("hopping_cmd"));
message_port_register_in(mp("bursts_in"));
message_port_register_out(mp("bursts_out"));
// Bind message handlers
set_msg_handler(mp("hopping_cmd"),
boost::bind(&freq_hopper_tag_impl::add_hopping_cmd,
this, _1));
set_msg_handler(mp("bursts_in"),
boost::bind(&freq_hopper_tag_impl::set_freq_metadata,
this, _1));
add_hopping_cmd(hopping_cmd);
}
/*
* Our virtual destructor.
*/
freq_hopper_tag_impl::~freq_hopper_tag_impl()
{
}
void freq_hopper_tag_impl::add_hopping_cmd(pmt_t cmd) //TODO: fn and discard not supported at the moment
{
if(dict_ref(cmd,intern("cmd"), PMT_NIL) == intern("start")) {
if(dict_ref(cmd,intern("fn"), PMT_NIL) != PMT_NIL){
//TODO add the command to the map<int,pmt_t>
} else {
pmt_t hopping_params = dict_ref(cmd, intern("hopping_params"), PMT_NIL);
d_hopping_enable = set_hopping_params(hopping_params);
}
} else if(dict_ref(cmd,intern("cmd"), PMT_NIL) == intern("stop")) {
if(dict_ref(cmd,intern("fn"),PMT_NIL) != PMT_NIL){
//TODO add the command to the map<int,pmt_t>
} else {
d_hopping_enable = false;
}
}
}
void freq_hopper_tag_impl::set_freq_metadata(pmt_t burst)
{
if(d_hopping_enable) {
pmt_t pdu_header = car(burst);
pmt_t tx_time = dict_ref(pdu_header, intern("tx_time"),PMT_NIL);
if(tx_time != PMT_NIL){
pmt_t tx_command_time = cons(tuple_ref(tx_time,0),tuple_ref(tx_time,1));
pmt_t header_plus_burst = cdr(burst);
uint32_t frame_nr = 0;
pmt_t fn = dict_ref(pdu_header,intern("fn"),PMT_NIL);
if(fn == PMT_NIL){
gsmtap_hdr *header = (gsmtap_hdr *)blob_data(header_plus_burst);
uint32_t frame_nr = be32toh(header->frame_number);
} else {
frame_nr = to_uint64(fn);
}
int mai = calculate_ma_sfh(d_maio, d_hsn, d_ma.size(), frame_nr);
uint16_t arfcn = d_ma[mai];
// if(fn == PMT_NIL){
// header->arfcn = htobe16(arfcn);
// header->arfcn = header->arfcn | 0x8000; // set uplink flag
// }
//compute the frequences to be set in the burst header
double freq_uplink = static_cast<double>(gsm_arfcn2freq10(arfcn, 1)) * 1.0e5;
double freq_downlink = static_cast<double>(gsm_arfcn2freq10(arfcn, 0)) * 1.0e5;
pmt_t tx_command = dict_add(make_dict(),intern("lo_freq"),from_double(d_base_freq));
tx_command = dict_add(tx_command,intern("dsp_freq"),from_double(freq_uplink-d_base_freq));
tx_command = dict_add(tx_command,intern("time"),tx_command_time);
pmt_t rx_command = dict_add(make_dict(),intern("lo_freq"),from_double(d_base_freq));
rx_command = dict_add(rx_command,intern("dsp_freq"),from_double(freq_uplink-d_base_freq));
rx_command = dict_add(rx_command,intern("time"),tx_command_time);
rx_command = dict_add(rx_command,intern("direction"),intern("RX"));
pdu_header = dict_add(pdu_header, intern("tx_command"),tx_command);
// pdu_header = dict_add(pdu_header, intern("tx_command"),rx_command);
// std::cout << "arfcn " << arfcn << " mai " << mai << " d_ma.size() " << d_ma.size() << " d_hsn " << d_hsn << std::endl;
std::cout << "arfcn_uplink " << arfcn << std::endl;
// std::cout << "freq_downlink " << freq_downlink << std::endl;
// std::cout << "pdu_header " << pdu_header << std::endl;
// std::cout << "size_header_plus_burst " << length(header_plus_burst) << std::endl;
message_port_pub(mp("bursts_out"), cons(pdu_header,header_plus_burst));
}
} else {
message_port_pub(mp("bursts_out"), burst);
}
}
bool freq_hopper_tag_impl::set_hopping_params(pmt_t hopping_params){
bool retval = false;
if(hopping_params != PMT_NIL){
//set hopping parameters immediately
pmt_t hsn = dict_ref(hopping_params, intern("hsn"), PMT_NIL);
pmt_t maio = dict_ref(hopping_params, intern("maio"), PMT_NIL);;
pmt_t ma = dict_ref(hopping_params, intern("ma"), PMT_NIL);
if(is_vector(ma) && is_integer(hsn) && is_integer(maio)){ //TODO: checking the values
d_hsn = to_uint64(hsn);
d_maio = to_uint64(maio);
d_ma.resize(length(ma));
for(int i=0; i<length(ma); i++){
d_ma[i] = to_uint64(vector_ref(ma,i));
}
retval = true;
}
}
return retval;
}
} /* namespace gsm */
} /* namespace gr */

View File

@ -0,0 +1,55 @@
/* -*- c++ -*- */
/* @file
* @author Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm 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.
*
* Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef INCLUDED_GSM_FREQ_HOPPER_TAG_IMPL_H
#define INCLUDED_GSM_FREQ_HOPPER_TAG_IMPL_H
#include <grgsm/trx/freq_hopper_tag.h>
#include <grgsm/misc_utils/time_spec.h>
#include <grgsm/misc_utils/fn_time.h>
namespace gr {
namespace gsm {
class freq_hopper_tag_impl : public freq_hopper_tag
{
private:
bool d_hopping_enable; //if true block do the hopping, if not block just passes the bursts
uint64_t d_hsn; //hopping sequence number
uint64_t d_maio; //mobile allocation index offset
double d_base_freq; //local oscillator frequency
std::vector<uint64_t> d_ma; //mobile allocation
pmt::pmt_t d_hopping_cmd; //TODO: change this uint64_to a std::map
void set_freq_metadata(pmt::pmt_t cmd);
bool set_hopping_params(pmt::pmt_t hopping_params);
public:
freq_hopper_tag_impl(pmt::pmt_t hopping_cmd);
~freq_hopper_tag_impl();
void add_hopping_cmd(pmt::pmt_t hopping_cmd);
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_FREQ_HOPPER_TAG_IMPL_H */

View File

@ -27,7 +27,8 @@
#include <gnuradio/io_signature.h>
#include <boost/lexical_cast.hpp>
#include "udp_socket.h"
#include "grgsm/endian.h"
#include "grgsm/misc_utils/udp_socket.h"
#include "trx_burst_if_impl.h"
#define BURST_SIZE 148
@ -48,19 +49,22 @@ namespace gr {
trx_burst_if::sptr
trx_burst_if::make(
const std::string &bind_addr,
const std::string &remote_addr,
const std::string &base_port)
{
int base_port_int = boost::lexical_cast<int> (base_port);
return gnuradio::get_initial_sptr
(new trx_burst_if_impl(remote_addr, base_port_int));
(new trx_burst_if_impl(bind_addr, remote_addr,
base_port_int));
}
/*
* The private constructor
*/
trx_burst_if_impl::trx_burst_if_impl(
const std::string &bind_addr,
const std::string &remote_addr,
int base_port
) : gr::block("trx_burst_if",
@ -79,8 +83,8 @@ namespace gr {
std::string data_dst_port = boost::lexical_cast<std::string> (base_port + 102);
// Init DATA interface
d_data_sock = new udp_socket(remote_addr,
data_src_port, data_dst_port, DATA_IF_MTU);
d_data_sock = new udp_socket(bind_addr, data_src_port,
remote_addr, data_dst_port, DATA_IF_MTU);
// Bind DATA interface handler
d_data_sock->udp_rx_handler = boost::bind(

View File

@ -26,7 +26,7 @@
#include <stddef.h>
#include <grgsm/gsmtap.h>
#include <grgsm/misc_utils/trx_burst_if.h>
#include <grgsm/trx/trx_burst_if.h>
namespace gr {
namespace gsm {
@ -40,7 +40,8 @@ namespace gr {
void burst_pack(pmt::pmt_t msg, uint8_t *buf);
public:
trx_burst_if_impl(const std::string &remote_addr, int base_port);
trx_burst_if_impl(const std::string &bind_addr,
const std::string &remote_addr, int base_port);
~trx_burst_if_impl();
void handle_dl_burst(pmt::pmt_t msg);

View File

@ -50,6 +50,7 @@ GR_ADD_TEST(qa_arfcn ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_arfcn.p
GR_ADD_TEST(qa_decryption ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_decryption.py)
GR_ADD_TEST(qa_burst_printer ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_burst_printer.py)
GR_ADD_TEST(qa_message_printer ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_message_printer.py)
GR_ADD_TEST(qa_burst_file_source ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_burst_file_source.py)
GR_ADD_TEST(qa_burst_timeslot_splitter ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_burst_timeslot_splitter.py)
GR_ADD_TEST(qa_burst_sdcch_subslot_splitter ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_burst_sdcch_subslot_splitter.py)
GR_ADD_TEST(qa_burst_timeslot_filter ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_burst_timeslot_filter.py)
@ -57,6 +58,9 @@ GR_ADD_TEST(qa_burst_sdcch_subslot_filter ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_S
GR_ADD_TEST(qa_burst_fnr_filter ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_burst_fnr_filter.py)
GR_ADD_TEST(qa_dummy_burst_filter ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_dummy_burst_filter.py)
GR_ADD_TEST(qa_tch_f_decoder ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_tch_f_decoder.py)
GR_ADD_TEST(qa_tch_h_decoder ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_tch_h_decoder.py)
GR_ADD_TEST(qa_tch_f_chans_demapper ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_tch_f_chans_demapper.py)
GR_ADD_TEST(qa_tch_h_chans_demapper ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_tch_h_chans_demapper.py)
#GR_ADD_TEST(qa_msg_to_tag ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_msg_to_tag.py)
#GR_ADD_TEST(qa_controlled_fractional_resampler_cc ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_controlled_fractional_resampler_cc.py)
#GR_ADD_TEST(qa_uplink_downlink_splitter ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_uplink_downlink_splitter.py)

View File

@ -59,6 +59,7 @@ from gsm_gmsk_mod import gsm_gmsk_mod
from fn_time import *
from txtime_bursts_tagger import *
import arfcn
import device
#

View File

@ -23,5 +23,6 @@ GR_PYTHON_INSTALL(
clock_offset_corrector_tagged.py
hier_block.py
fn_time.py
device.py
DESTINATION ${GR_PYTHON_DIR}/grgsm
)

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @file
# @author (C) 2019 by Vasil Velichkov <vvvelichkov@gmail.com>
# @section LICENSE
#
# Gr-gsm 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.
#
# Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
import osmosdr
import os
def get_devices(hint=""):
return osmosdr.device_find(osmosdr.device_t(hint))
def match(dev, filters):
for f in filters:
for k, v in f.items():
if (k not in dev or dev[k] != v):
break
else:
return True
return False
def exclude(devices, filters = ({'driver': 'audio'},)):
return [dev for dev in devices if not match(dev, filters)]
def get_all_args(hint="nofake"):
return map(lambda dev: dev.to_string(), exclude(get_devices(hint)))
def get_default_args(args):
# The presence of GRC_BLOCKS_PATH environment variable indicates that
# gnuradio-companion compiles a flowgraph and in this case no exception
# have to be thrown otherwise the generaged python script will be invalid.
# This allows compilation of flowgraphs without an SDR device.
if args or os.getenv("GRC_BLOCKS_PATH"):
return args
devices = get_all_args("nofake")
if not devices:
raise RuntimeError("Unable to find any supported SDR devices")
return devices[0]
def print_devices(hint=""):
devices = exclude(get_devices(hint))
if devices:
print("\n".join(map(lambda dev: dev.to_string(), devices)))
else:
print("Unable to find any supported SDR devices")

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @file
# @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
# @section LICENSE
#
# Gr-gsm 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.
#
# Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
from gnuradio import gr, gr_unittest, blocks
import grgsm_swig as grgsm
import tempfile
class qa_burst_file_sink (gr_unittest.TestCase):
def setUp (self):
self.tb = gr.top_block ()
def tearDown (self):
self.tb = None
def test_blob_only (self):
# prepare the input burst file
temp = tempfile.NamedTemporaryFile()
handle = open(temp.name, "wb")
handle.write(bytearray([
0x07, 0x06, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x02, 0x04, 0x03, 0x01, 0x00, 0x6f,
0xcd, 0x00, 0x00, 0x25, 0xc9, 0x82, 0x06, 0x1c, 0xf5, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01,
0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01
]))
handle.flush();
src = grgsm.burst_file_source(temp.name);
dst = grgsm.burst_sink();
self.tb.msg_connect(src, "out", dst, "in")
self.tb.run ()
self.assertEqual([2476418], list(dst.get_framenumbers()))
self.assertEqual([1], list(dst.get_timeslots()))
self.assertEqual([
"0000001000000101010111111110101000000000010101010111101010101001011011101111000101101111100000000001010101111010101010000000010101011101111010101001"],
list(dst.get_burst_data()))
def test_fn_time (self):
# prepare the input burst file
temp = tempfile.NamedTemporaryFile()
handle = open(temp.name, "wb")
handle.write(bytearray([
0x07,
# the additional fn_time field - 51 bytes
0x07, 0x07, 0x02, 0x00, 0x07, 0x66, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x07, 0x07, 0x0b,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x3b, 0x73, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x07, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3f, 0xe4, 0x99, 0x45,
0xbe, 0x81, 0xc0, 0xf4,
# the 173 original 173 bytes
0x06, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x02, 0x04, 0x03, 0x01, 0x00, 0x6f,
0xcd, 0x00, 0x00, 0x25, 0xc9, 0x82, 0x06, 0x1c, 0xf5, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01,
0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01
]))
handle.flush();
src = grgsm.burst_file_source(temp.name);
dst = grgsm.burst_sink();
self.tb.msg_connect(src, "out", dst, "in")
self.tb.run ()
self.assertEqual([2476418], list(dst.get_framenumbers()))
self.assertEqual([1], list(dst.get_timeslots()))
self.assertEqual([
"0000001000000101010111111110101000000000010101010111101010101001011011101111000101101111100000000001010101111010101010000000010101011101111010101001"],
list(dst.get_burst_data()))
if __name__ == '__main__':
gr_unittest.run(qa_burst_file_sink, "qa_burst_file_sink.xml")

154
python/qa_tch_f_chans_demapper.py Executable file
View File

@ -0,0 +1,154 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @file
# @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
# @section LICENSE
#
# Gr-gsm 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.
#
# Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
import numpy as np
from gnuradio import gr, gr_unittest, blocks
import grgsm_swig as grgsm
import pmt
class qa_tch_f_chans_demapper (gr_unittest.TestCase):
def setUp (self):
self.tb = gr.top_block ()
self.bursts = [
"1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]
def tearDown (self):
self.tb = None
def test_fr_demapper (self):
"""
TCH/F Full Rate demapper
"""
frames = [
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29]
timeslots = [
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3
]
b = self.bursts
bursts = [
b[ 0], b[ 1], b[ 2], b[ 3], b[ 4], b[ 5], b[ 6], b[ 7],
b[ 8], b[ 9], b[10], b[11], b[12], #12 - sacch
b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20],
b[21], b[22], b[23], b[24], b[25], #25 - idle
b[26], b[27], b[28], b[29], b[30]
]
src = grgsm.burst_source(frames, timeslots, bursts)
demapper = grgsm.tch_f_chans_demapper(3)
tch = grgsm.burst_sink()
acch = grgsm.burst_sink()
self.tb.msg_connect(src, "out", demapper, "bursts")
self.tb.msg_connect(demapper, "tch_bursts", tch, "in")
self.tb.msg_connect(demapper, "acch_bursts", acch, "in")
self.tb.run ()
self.assertEqual([
b[ 0], b[ 1], b[ 2], b[ 3], b[ 4], b[ 5], b[ 6], b[ 7],
b[ 4], b[ 5], b[ 6], b[ 7], b[ 8], b[ 9], b[10], b[11],
b[ 8], b[ 9], b[10], b[11],
b[13], b[14], b[15], b[16],
b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20],
b[17], b[18], b[19], b[20], b[21], b[22], b[23], b[24],
b[21], b[22], b[23], b[24],
b[26], b[27], b[28], b[29],
], list(tch.get_burst_data()))
self.assertEqual([], list(acch.get_burst_data()))
def sacch_fr_test (self, ts, frames, bursts):
timeslots = [ts, ts, ts, ts, ts, ts, ts, ts]
src = grgsm.burst_source(frames, timeslots, bursts)
demapper = grgsm.tch_f_chans_demapper(ts)
tch = grgsm.burst_sink()
acch = grgsm.burst_sink()
self.tb.msg_connect(src, "out", demapper, "bursts")
self.tb.msg_connect(demapper, "tch_bursts", tch, "in")
self.tb.msg_connect(demapper, "acch_bursts", acch, "in")
self.tb.run ()
self.assertEqual([], list(tch.get_burst_data()))
return list(acch.get_burst_data())
def test_sacch_tf (self):
"""
SACCH/TF tests
"""
b = self.bursts
bursts=[b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]
even = [b[0], b[2], b[4], b[6]]
odd = [b[1], b[3], b[5], b[7]]
self.assertEqual(even, self.sacch_fr_test(ts=0, frames=[12, 25, 38, 51, 64, 77, 90, 103], bursts=bursts))
self.assertEqual(odd, self.sacch_fr_test(ts=1, frames=[12, 25, 38, 51, 64, 77, 90, 103], bursts=bursts))
self.assertEqual(even, self.sacch_fr_test(ts=2, frames=[38, 51, 64, 77, 90, 103, 116, 129], bursts=bursts))
self.assertEqual(odd, self.sacch_fr_test(ts=3, frames=[38, 51, 64, 77, 90, 103, 116, 129], bursts=bursts))
self.assertEqual(even, self.sacch_fr_test(ts=4, frames=[64, 77, 90, 103, 116, 129, 142, 155], bursts=bursts))
self.assertEqual(odd, self.sacch_fr_test(ts=5, frames=[64, 77, 90, 103, 116, 129, 142, 155], bursts=bursts))
self.assertEqual(even, self.sacch_fr_test(ts=6, frames=[90, 103, 116, 129, 142, 155, 168, 181], bursts=bursts))
self.assertEqual(odd, self.sacch_fr_test(ts=7, frames=[90, 103, 116, 129, 142, 155, 168, 181], bursts=bursts))
if __name__ == '__main__':
gr_unittest.run(qa_tch_f_chans_demapper, "qa_tch_f_chans_demapper.xml")

209
python/qa_tch_h_chans_demapper.py Executable file
View File

@ -0,0 +1,209 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @file
# @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
# @section LICENSE
#
# Gr-gsm 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.
#
# Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
import numpy as np
from gnuradio import gr, gr_unittest, blocks
import grgsm_swig as grgsm
import pmt
class qa_tch_h_chans_demapper (gr_unittest.TestCase):
def setUp (self):
self.tb = gr.top_block ()
self.bursts = [
"1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]
def tearDown (self):
self.tb = None
def test_hr_demapper_sub0 (self):
"""
TCH/F Half Rate demapper sub-channel 0
"""
frames = [
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29]
timeslots = [
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3
]
b = self.bursts
bursts = [
b[ 0], b[ 1], b[ 2], b[ 3], b[ 4], b[ 5], b[ 6], b[ 7],
b[ 8], b[ 9], b[10], b[11], b[12], #12 - sacch
b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20],
b[21], b[22], b[23], b[24], b[25], #25 - idle
b[26], b[27], b[28], b[29], b[30]
]
src = grgsm.burst_source(frames, timeslots, bursts)
demapper = grgsm.tch_h_chans_demapper(3, 0)
tch = grgsm.burst_sink()
acch = grgsm.burst_sink()
self.tb.msg_connect(src, "out", demapper, "bursts")
self.tb.msg_connect(demapper, "tch_bursts", tch, "in")
self.tb.msg_connect(demapper, "acch_bursts", acch, "in")
self.tb.run ()
self.assertEqual([
b[ 0], b[ 2], b[ 4], b[ 6],
b[ 4], b[ 6], b[ 8], b[10],
b[ 8], b[10],
b[13], b[15],
b[13], b[15], b[17], b[19],
b[17], b[19], b[21], b[23],
b[21], b[23],
b[26], b[28],
], list(tch.get_burst_data()))
self.assertEqual([], list(acch.get_burst_data()))
def test_hr_demapper_sub1 (self):
"""
TCH/F Half Rate demapper sub-channel 1
"""
frames = [
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29]
timeslots = [
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3
]
b = self.bursts
bursts = [
b[ 0], b[ 1], b[ 2], b[ 3], b[ 4], b[ 5], b[ 6], b[ 7],
b[ 8], b[ 9], b[10], b[11], b[12], #12 - sacch
b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20],
b[21], b[22], b[23], b[24], b[25], #25 - idle
b[26], b[27], b[28], b[29], b[30]
]
src = grgsm.burst_source(frames, timeslots, bursts)
demapper = grgsm.tch_h_chans_demapper(3, 1)
tch = grgsm.burst_sink()
acch = grgsm.burst_sink()
self.tb.msg_connect(src, "out", demapper, "bursts")
self.tb.msg_connect(demapper, "tch_bursts", tch, "in")
self.tb.msg_connect(demapper, "acch_bursts", acch, "in")
self.tb.run ()
self.assertEqual([
b[ 1], b[ 3], b[ 5], b[ 7],
b[ 5], b[ 7], b[ 9], b[11],
b[ 9], b[11],
b[14], b[16],
b[14], b[16], b[18], b[20],
b[18], b[20], b[22], b[24],
b[22], b[24],
b[27], b[29],
], list(tch.get_burst_data()))
self.assertEqual([], list(acch.get_burst_data()))
def sacch_hr_test (self, ts, sub, frames, bursts):
timeslots = [ts, ts, ts, ts, ts, ts, ts, ts]
src = grgsm.burst_source(frames, timeslots, bursts)
demapper = grgsm.tch_h_chans_demapper(ts, sub)
tch = grgsm.burst_sink()
acch = grgsm.burst_sink()
self.tb.msg_connect(src, "out", demapper, "bursts")
self.tb.msg_connect(demapper, "tch_bursts", tch, "in")
self.tb.msg_connect(demapper, "acch_bursts", acch, "in")
self.tb.run ()
self.assertEqual([], list(tch.get_burst_data()))
return list(acch.get_burst_data())
def test_sacch_th (self):
"""
SACCH/TH tests
"""
b = self.bursts
bursts=[b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]
even = [b[0], b[2], b[4], b[6]]
odd = [b[1], b[3], b[5], b[7]]
self.assertEqual(even, self.sacch_hr_test(ts=0, sub=0, frames=[12, 25, 38, 51, 64, 77, 90, 103], bursts=bursts))
self.assertEqual(odd, self.sacch_hr_test(ts=0, sub=1, frames=[12, 25, 38, 51, 64, 77, 90, 103], bursts=bursts))
self.assertEqual(even, self.sacch_hr_test(ts=1, sub=0, frames=[12, 25, 38, 51, 64, 77, 90, 103], bursts=bursts))
self.assertEqual(odd, self.sacch_hr_test(ts=1, sub=1, frames=[12, 25, 38, 51, 64, 77, 90, 103], bursts=bursts))
self.assertEqual(even, self.sacch_hr_test(ts=2, sub=0, frames=[38, 51, 64, 77, 90, 103, 116, 129], bursts=bursts))
self.assertEqual(odd, self.sacch_hr_test(ts=2, sub=1, frames=[38, 51, 64, 77, 90, 103, 116, 129], bursts=bursts))
self.assertEqual(even, self.sacch_hr_test(ts=3, sub=0, frames=[38, 51, 64, 77, 90, 103, 116, 129], bursts=bursts))
self.assertEqual(odd, self.sacch_hr_test(ts=3, sub=1, frames=[38, 51, 64, 77, 90, 103, 116, 129], bursts=bursts))
self.assertEqual(even, self.sacch_hr_test(ts=4, sub=0, frames=[64, 77, 90, 103, 116, 129, 142, 155], bursts=bursts))
self.assertEqual(odd, self.sacch_hr_test(ts=4, sub=1, frames=[64, 77, 90, 103, 116, 129, 142, 155], bursts=bursts))
self.assertEqual(even, self.sacch_hr_test(ts=5, sub=0, frames=[64, 77, 90, 103, 116, 129, 142, 155], bursts=bursts))
self.assertEqual(odd, self.sacch_hr_test(ts=5, sub=1, frames=[64, 77, 90, 103, 116, 129, 142, 155], bursts=bursts))
self.assertEqual(even, self.sacch_hr_test(ts=6, sub=0, frames=[90, 103, 116, 129, 142, 155, 168, 181], bursts=bursts))
self.assertEqual(odd, self.sacch_hr_test(ts=6, sub=1, frames=[90, 103, 116, 129, 142, 155, 168, 181], bursts=bursts))
self.assertEqual(even, self.sacch_hr_test(ts=7, sub=0, frames=[90, 103, 116, 129, 142, 155, 168, 181], bursts=bursts))
self.assertEqual(odd, self.sacch_hr_test(ts=7, sub=1, frames=[90, 103, 116, 129, 142, 155, 168, 181], bursts=bursts))
if __name__ == '__main__':
gr_unittest.run(qa_tch_h_chans_demapper, "qa_tch_h_chans_demapper.xml")

190
python/qa_tch_h_decoder.py Executable file
View File

@ -0,0 +1,190 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @file
# @author (C) 2018 by Vasil Velichkov <vvvelichkov@gmail.com>
# @section LICENSE
#
# Gr-gsm 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.
#
# Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
import numpy as np
from gnuradio import gr, gr_unittest, blocks
import grgsm_swig as grgsm
import pmt
class qa_tch_h_decoder (gr_unittest.TestCase):
def setUp (self):
self.tb = gr.top_block ()
self.b = [
"0001010001011010010000111100010101011011110000001011100101110010011101011000001001110100100010011111111000001001011000001100100010001011011000010000",
"0000000110100000010101000111011101101100010000101101000011000010011101011000001001110100001111000111000010100010111111011000001010111101110001000000",
"0000001101101000100100101101000011110011010110111100110000100010011101011000001001110100001110011100011000010000000111110100111110001101101100100000",
"0000011111010101110111001101111001001111010010000011111111010010011101011000001001110100010110000010000000000011100101111111010101111101111011101000",
"0001001110111000101001110001010011001101101111000010111100010010011101011000001001110100111000111011000111111100001011011011111001011100110111001000",
"0000110110000101000011001001011110110110001110001000010001000010011101011000001001110100000010100001010001011000000110001110011110001000111100110000",
"0000001101100010101011110100000010110100111110010010001100010010011101011000001001110100100000110001101011011101011110000110100010111100101011011000",
"0001111110100000011111010011100100001101011101000011011000000010011101011000001001110100100111110010000011100011000011001110001111110000001010011000",
"0001010100101010111010111001000100010100000010101011010011011010011101011000001001110100010100011100101110101110000000000111101011111111100011000000",
"0000000011011011101011000101000001001110001011101000100001011010011101011000001001110100000100011110101010100011010101010100101011101010000100000000",
"0001010110101001110001111001110100011010000110100000111001110010011101011000001001110100100100010101000100001100100000111111000001010111100011000000",
"0001100000100001100001100101101001101110001000101011100001010010011101011000001001110100110010110101000100100011011101111100000011100010000100000000",
"0001010110100000010010111001000100011110101010100001010001110010011101011000001001110100110100010100000110001100000000100111000011011111100011000000",
"0000101001110011000001000101001001101110001001100000100001110010011101011000001001110100100110110100001000100011111101111110101011100000100100000000",
#FACCH framces 14 - 21
"0001011000000010000000001000010111001000101111010000000010110010011101011000001001110101100000100000010010100000101000011000100011100010100000110000",
"0000010101111010111010000001100001110101111100001010101001100010011101011000001001110101111101111101101110000010110011111111110101010000001000110000",
"0000000000111110101010100001000000001000101110100010100100011010011101011000001001110101001010101011101010100000010100000000101111101000000000010000",
"0000001010010101011111011011101010101000000001011111111110101010011101011000001001110101101000010000010101111011111010100001010001011101111010101000",
"0001010100101010111010111001000100010100000010101011010011011010011101011000001001110100010100011100101110101110000000000111101011111111100011000000",
"0000000011011011101011000101000001001110001011101000100001011010011101011000001001110100000100011110101010100011010101010100101011101010000100000000",
"0001010110101001110001111001110100011010000110100000111001110010011101011000001001110100100100010101000100001100100000111111000001010111100011000000",
"0001100000100001100001100101101001101110001000101011100001010010011101011000001001110100110010110101000100100011011101111100000011100010000100000000",
]
def tearDown (self):
self.tb = None
def tchh_multirate (self, frames, timeslots, bursts, multirate, subchan):
"""
Common TCH/H MultiRate test code
"""
src = grgsm.burst_source(frames, timeslots, bursts)
decoder = grgsm.tch_h_decoder(subchan, multirate, False);
dst = blocks.message_debug()
self.tb.msg_connect(src, "out", decoder, "bursts")
self.tb.msg_connect(decoder, "voice", dst, "store")
self.tb.run ()
result = []
for i in range(0, dst.num_messages()):
pdu = dst.get_message(i)
self.assertEqual(True, pmt.is_pair(pdu))
data = pmt.cdr(pdu)
self.assertEqual(True, pmt.is_blob(data))
result.append(pmt.to_python(data).tolist())
return result
def test_amr7_40 (self):
"""
TCH/H MultiRate AMR 7.40
"""
b = self.b
self.assertListEqual(self.tchh_multirate(
multirate = "28111a40",
subchan = 0,
frames = [259215, 259217, 259220, 259222, 259220, 259222, 259224, 259226],
timeslots = [ 6, 6, 6, 6, 6, 6, 6, 6],
bursts = [ b[8], b[9], b[10], b[11], b[10], b[11], b[12], b[13]]),
[
[0x23,0x21,0x41,0x4d,0x52,0x0a],
[0x20,0xff,0x3c,0x67,0xe0,0x00,0x1f,0x3d,0x01,0xf0,0xfc,0x3f,0x77,0x18,0x61,0x86,0x00,0x00,0x00,0x00]
])
def test_4_75 (self):
"""
TCH/H MultiRate AMR 4.75
"""
b = self.b
self.assertListEqual(self.tchh_multirate(
multirate = "28111a40",
subchan = 0,
frames = [259666, 259668, 259670, 259672, 259670, 259672, 259675, 259677],
timeslots = [ 6, 6, 6, 6, 6, 6, 6, 6],
bursts = [ b[2], b[3], b[4], b[5], b[4], b[5], b[6], b[7]]),
[
[0x23,0x21,0x41,0x4d,0x52,0x0a],
[0x00,0x67,0x19,0x24,0xd5,0x1b,0xd1,0x29,0x3f,0xa1,0x50,0x5f,0x3e]
])
def test_amr7_40_and_4_75 (self):
"""
TCH/H MultiRate AMR 7.40 and 4.75
Two 7.40 followed by two 4.75 frames
"""
b = self.b
self.assertListEqual(self.tchh_multirate(
multirate= "28111a40",
subchan = 0,
frames = [259657, 259659, 259662, 259664,
259662, 259664, 259666, 259668,
259666, 259668, 259670, 259672,
259670, 259672, 259675, 259677,
259675, 259677, 259679, 259681],
timeslots= [ 6, 6, 6, 6,
6, 6, 6, 6,
6, 6, 6, 6,
6, 6, 6, 6,
6, 6, 6, 6],
bursts = [ b[0], b[1], b[2], b[3],
b[0], b[1], b[2], b[3],
b[2], b[3], b[4], b[5],
b[2], b[3], b[4], b[5],
b[4], b[5], b[6], b[7]]),
[
[0x23,0x21,0x41,0x4d,0x52,0x0a],
[0x20,0xe0,0x27,0x2a,0x00,0x21,0x30,0x38,0x75,0xf8,0x14,0x3c,0xec,0xde,0x0b,0x47,0x6f,0x9c,0xc6,0x70],
[0x20,0xe0,0x27,0x2a,0x00,0x21,0x30,0x38,0x75,0xf8,0x14,0x3c,0xec,0xde,0x0b,0x47,0x6f,0x9c,0xc6,0x70],
[0x00,0x67,0x19,0x24,0xd5,0x1b,0xd1,0x29,0x3f,0xa1,0x50,0x5f,0x3e],
[0x00,0x67,0x19,0x24,0xd5,0x1b,0xd1,0x29,0x3f,0xa1,0x50,0x5f,0x3e],
])
def facch_test (self, frames, timeslots, bursts):
'''
Common FACCH/TH test code
'''
src = grgsm.burst_source(frames, timeslots, bursts)
decoder = grgsm.tch_h_decoder(0, "28111a40", False);
dst = blocks.message_debug()
facch = grgsm.message_sink()
self.tb.msg_connect(src, "out", decoder, "bursts")
self.tb.msg_connect(decoder, "voice", dst, "store")
self.tb.msg_connect(decoder, "msgs", facch, "in")
self.tb.run ()
self.assertEqual(dst.num_messages(), 0)
return list(facch.get_messages())
def test_facch_th (self):
"""
FACCH/TH test
"""
b = self.b
self.assertEqual(self.facch_test(
frames= [259207, 259209, 259211, 259213, 259211, 259213, 259215, 259217],
timeslots = [ 6, 6, 6, 6, 6, 6, 6, 6],
bursts = [ b[14], b[15], b[16], b[17], b[16], b[17], b[18], b[19]]),
['02 04 01 06 00 00 00 00 00 03 f4 8b 06 00 00 00 03 60 09 03 0f 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b'])
def test_facch_th_error (self):
"""
FACCH/TH Error handling test
"""
b = self.b
self.assertEqual(self.facch_test(
frames= [259207, 259209, 259211, 259213, 259211, 259213, 259215, 259217],
timeslots = [ 6, 6, 6, 6, 6, 6, 6, 6],
bursts = [ b[16], b[17], b[18], b[19], b[18], b[19], b[20], b[21]]),
[]) #Must return an empty array
if __name__ == '__main__':
gr_unittest.run(qa_tch_h_decoder, "qa_tch_h_decoder.xml")

View File

@ -23,9 +23,11 @@ GR_PYTHON_INSTALL(
udp_link.py
ctrl_if.py
ctrl_if_bb.py
fake_pm.py
radio_if.py
radio_if_uhd.py
radio_if_lms.py
radio_if_grc.py
change_sign_of_dict_elements.py
transceiver.py
dict_toggle_sign.py
DESTINATION ${GR_PYTHON_DIR}/grgsm/trx
)

View File

@ -20,10 +20,10 @@
This is a set of helper classes for the grgsm_trx application.
'''
from udp_link import udp_link
from ctrl_if import ctrl_if
from ctrl_if_bb import ctrl_if_bb
from fake_pm import fake_pm
from radio_if_grc import radio_if_grc
from radio_if import radio_if
from change_sign_of_dict_elements import change_sign_of_dict_elements
from udp_link import UDPLink
from ctrl_if import CTRLInterface
from ctrl_if_bb import CTRLInterfaceBB
from radio_if import RadioInterface
from transceiver import Transceiver
from dict_toggle_sign import dict_toggle_sign

View File

@ -22,18 +22,18 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from grgsm.trx import udp_link
from udp_link import UDPLink
class ctrl_if(udp_link):
def handle_rx(self, data):
class CTRLInterface(UDPLink):
def handle_rx(self, data, remote):
if self.verify_req(data):
request = self.prepare_req(data)
rc = self.parse_cmd(request)
if type(rc) is tuple:
self.send_response(request, rc[0], rc[1])
self.send_response(request, remote, rc[0], rc[1])
else:
self.send_response(request, rc)
self.send_response(request, remote, rc)
else:
print("[!] Wrong data on CTRL interface")
@ -65,7 +65,7 @@ class ctrl_if(udp_link):
return True
def send_response(self, request, response_code, params = None):
def send_response(self, request, remote, response_code, params = None):
# Include status code, for example ["TXTUNE", "0", "941600"]
request.insert(1, str(response_code))
@ -76,7 +76,7 @@ class ctrl_if(udp_link):
# Add the response signature, and join back to string
response = "RSP " + " ".join(request) + "\0"
# Now we have something like "RSP TXTUNE 0 941600"
self.send(response)
self.send(response, remote)
def parse_cmd(self, request):
raise NotImplementedError

View File

@ -4,7 +4,7 @@
# GR-GSM based transceiver
# CTRL interface for OsmocomBB
#
# (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
# (C) 2016-2019 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
@ -22,49 +22,32 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import grgsm
from ctrl_if import ctrl_if
from ctrl_if import CTRLInterface
class ctrl_if_bb(ctrl_if):
def __init__(self, remote_addr, remote_port, bind_port, tb, pm):
print("[i] Init CTRL interface")
ctrl_if.__init__(self, remote_addr, remote_port, bind_port)
class CTRLInterfaceBB(CTRLInterface):
def __init__(self, trx, *ctrl_if_args):
CTRLInterface.__init__(self, *ctrl_if_args)
print("[i] Init CTRL interface (%s)" % self.desc_link())
# Set link to the follow graph (top block)
self.tb = tb
# Power measurement
self.pm = pm
def shutdown(self):
print("[i] Shutdown CTRL interface")
ctrl_if.shutdown(self)
# Transceiver instance we belong to
self.trx = trx
def parse_cmd(self, request):
# Power control
if self.verify_cmd(request, "POWERON", 0):
print("[i] Recv POWERON CMD")
# Ensure transceiver isn't working
if self.tb.trx_started:
print("[!] Transceiver already started")
# Start transceiver
if not self.trx.start():
return -1
print("[i] Starting transceiver...")
self.tb.trx_started = True
self.tb.start()
return 0
elif self.verify_cmd(request, "POWEROFF", 0):
print("[i] Recv POWEROFF cmd")
# TODO: flush all buffers between blocks
if self.tb.trx_started:
print("[i] Stopping transceiver...")
self.tb.trx_started = False
self.tb.set_ta(0)
self.tb.stop()
self.tb.wait()
# Stop transceiver
self.trx.stop()
return 0
@ -74,7 +57,7 @@ class ctrl_if_bb(ctrl_if):
# TODO: check gain value
gain = int(request[1])
self.tb.set_rx_gain(gain)
self.trx.radio_if.set_rx_gain(gain)
return 0
@ -83,7 +66,7 @@ class ctrl_if_bb(ctrl_if):
# TODO: check gain value
gain = int(request[1])
self.tb.set_tx_gain(gain)
self.trx.radio_if.set_tx_gain(gain)
return 0
@ -93,7 +76,7 @@ class ctrl_if_bb(ctrl_if):
# TODO: check freq range
freq = int(request[1]) * 1000
self.tb.set_rx_freq(freq)
self.trx.radio_if.set_rx_freq(freq)
return 0
@ -102,7 +85,7 @@ class ctrl_if_bb(ctrl_if):
# TODO: check freq range
freq = int(request[1]) * 1000
self.tb.set_tx_freq(freq)
self.trx.radio_if.set_tx_freq(freq)
return 0
@ -116,19 +99,12 @@ class ctrl_if_bb(ctrl_if):
print("[!] TS index should be in range: 0..7")
return -1
# Ignore timeslot type for now
# Channel combination number (see GSM TS 05.02)
# TODO: check this value
config = int(request[2])
print("[i] Configure timeslot filter to: %s"
% ("drop all" if config == 0 else "TS %d" % tn))
if config == 0:
# Value 0 means 'drop all'
self.tb.ts_filter.set_policy(
grgsm.FILTER_POLICY_DROP_ALL)
else:
self.tb.ts_filter.set_policy(
grgsm.FILTER_POLICY_DEFAULT)
self.tb.ts_filter.set_tn(tn)
# TODO: check return value
self.trx.radio_if.set_slot(tn, config)
return 0
@ -138,12 +114,11 @@ class ctrl_if_bb(ctrl_if):
# TODO: check freq range
meas_freq = int(request[1]) * 1000
meas_dbm = self.trx.measure(meas_freq)
if meas_dbm is None:
return -1
# HACK: send fake low power values
# until actual power measurement is implemented
meas_dbm = str(self.pm.measure(meas_freq))
return (0, [meas_dbm])
return (0, [str(meas_dbm)])
# Timing Advance control
elif self.verify_cmd(request, "SETTA", 1):
@ -155,7 +130,7 @@ class ctrl_if_bb(ctrl_if):
print("[!] TA value must be in range: 0..63")
return -1
self.tb.set_ta(ta)
self.trx.radio_if.set_ta(ta)
return 0
# Misc

View File

@ -9,7 +9,7 @@ parameters. All of them are required to have default values!
from gnuradio import gr
from pmt import *
class change_sign_of_dict_elements(gr.basic_block):
class dict_toggle_sign(gr.basic_block):
def __init__(self): # only default arguments here
gr.basic_block.__init__(
self,

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