Compare commits
106 Commits
master
...
ptrkrysik/
Author | SHA1 | Date |
---|---|---|
Piotr Krysik | 21fc94db73 | |
Piotr Krysik | 59257a11a3 | |
Piotr Krysik | 9e861d7017 | |
Piotr Krysik | 467a7aa80f | |
Piotr Krysik | 0de0172afe | |
Piotr Krysik | 43dfbdf1a9 | |
Piotr Krysik | c5ed04572f | |
Piotr Krysik | 891b2deb5f | |
Piotr Krysik | dc4cb98344 | |
Piotr Krysik | cd69577a40 | |
Piotr Krysik | f11e31dba1 | |
Vasil Velichkov | f51a624ec0 | |
Vasil Velichkov | b615cefdee | |
Vasil Velichkov | f8151a8398 | |
Piotr Krysik | 252ed2feb2 | |
Vasil Velichkov | 807e06139b | |
Piotr Krysik | e11b1d3710 | |
Vasil Velichkov | 8c30ba6687 | |
Vasil Velichkov | 4b034e7141 | |
Vadim Yanitskiy | 6feac435ed | |
Vadim Yanitskiy | ad0c62a41c | |
Vadim Yanitskiy | 25203809c1 | |
Vadim Yanitskiy | 6ee957f904 | |
Vadim Yanitskiy | 8072d3acd8 | |
Vadim Yanitskiy | 0b70fcc19b | |
Vadim Yanitskiy | 5819e31450 | |
Vadim Yanitskiy | 2cbc3b5d97 | |
Vadim Yanitskiy | 04863d978f | |
Vadim Yanitskiy | 6e7f2ce50c | |
Vadim Yanitskiy | 7d8a1caa0f | |
Vadim Yanitskiy | d54e8fb500 | |
Vadim Yanitskiy | 3e6bfe799e | |
Vadim Yanitskiy | 44d8c1e16b | |
Vadim Yanitskiy | 0d0508aa86 | |
Vadim Yanitskiy | ac3fe63bad | |
Vadim Yanitskiy | 31c543f008 | |
Vasil Velichkov | 4d300e9c61 | |
Vasil Velichkov | 2479152d5c | |
Vasil Velichkov | 5968552b47 | |
Vasil Velichkov | 9d3606b17e | |
Vasil Velichkov | 63e568dc3d | |
Andrew Artyushok | 3e2f883ba0 | |
Piotr Krysik | 7fb1738cd8 | |
David Holm | 9aadf3db64 | |
Vadim Yanitskiy | fad602b2b0 | |
Vadim Yanitskiy | 06f76fb3a6 | |
Vadim Yanitskiy | ef93382ed1 | |
Piotr Krysik | db65883a68 | |
Vasil Velichkov | ad01342ec7 | |
Piotr Krysik | 554aa905e4 | |
Piotr Krysik | 89dc99016e | |
Piotr Krysik | acd7d71db4 | |
Piotr Krysik | 6a0fb1a415 | |
Vasil Velichkov | 00a9c400a8 | |
Piotr Krysik | d6e8bb744e | |
Piotr Krysik | 11583adb9d | |
Vasil Velichkov | 492f48c7d3 | |
Piotr Krysik | 262bda10e6 | |
Piotr Krysik | 58659a4dee | |
Vadim Yanitskiy | 562125270b | |
Vasil Velichkov | 9e46c4807d | |
Vasil Velichkov | 1e04c1fd9c | |
Piotr Krysik | ba6fd0e776 | |
Piotr Krysik | fcb64a8adf | |
Vadim Yanitskiy | 3f9abbdf92 | |
Piotr Krysik | 22cd7c973f | |
Piotr Krysik | 6d2f53826f | |
Piotr Krysik | 80ec5ba599 | |
Vadim Yanitskiy | 641133e5d8 | |
Vadim Yanitskiy | 216558454d | |
Vadim Yanitskiy | f94a51b64d | |
Vadim Yanitskiy | 053f9e53f0 | |
Vadim Yanitskiy | 8705f0e546 | |
Vadim Yanitskiy | ed9d7282d4 | |
Vadim Yanitskiy | 4cedbb530e | |
Vadim Yanitskiy | 3824bbaeb1 | |
Vadim Yanitskiy | 45457e959d | |
Vadim Yanitskiy | 07a2abb635 | |
Vadim Yanitskiy | a609d2f9c9 | |
Piotr Krysik | f0f7183cb7 | |
Piotr Krysik | 8d30757dc0 | |
Harald Welte | de798b38ca | |
Harald Welte | cd3b36f1d5 | |
Piotr Krysik | 6a64ce82f7 | |
Vasil Velichkov | f9f7cbd56b | |
Vasil Velichkov | 9d211b3da3 | |
Piotr Krysik | bbc8af53e8 | |
Piotr Krysik | ded359bf36 | |
Vasil Velichkov | 0658bd424d | |
Vasil Velichkov | b4a0acf9f5 | |
Vasil Velichkov | e9aaeb14cc | |
Vasil Velichkov | c91b128c1d | |
Vasil Velichkov | f608e7c766 | |
Piotr Krysik | 3fbc96c5ea | |
Piotr Krysik | 02454b32b9 | |
Piotr Krysik | 4a3a52c897 | |
Piotr Krysik | 6463dc7641 | |
Piotr Krysik | 4e4a92ea05 | |
Piotr Krysik | 1a32fc42a0 | |
Piotr Krysik | b7cce89e55 | |
Piotr Krysik | fd3d883d4e | |
Piotr Krysik | 0aa24edf21 | |
Piotr Krysik | 88a4d483c9 | |
Piotr Krysik | 29dc4b3667 | |
Piotr Krysik | 1373c32d7e | |
Piotr Krysik | 450ed8fbbe |
|
@ -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
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=gr-gsm
|
|
@ -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()
|
||||
|
|
12
README.md
12
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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.
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
201
apps/grgsm_trx
201
apps/grgsm_trx
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 )
|
|
@ -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)
|
|
@ -0,0 +1,3 @@
|
|||
export PYTHONPATH="$1"
|
||||
export GRC_BLOCKS_PATH="$2"
|
||||
eval "$3"
|
|
@ -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()
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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() > -1 and $sub_channel() < 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>
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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() > -1 and $tch_h_channel() < 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>
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 >= 0</check>
|
||||
|
||||
|
||||
<sink>
|
||||
<name>in</name>
|
||||
<type>complex</type>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -37,3 +37,4 @@ add_subdirectory(misc_utils)
|
|||
add_subdirectory(qa_utils)
|
||||
add_subdirectory(flow_control)
|
||||
add_subdirectory(transmitter)
|
||||
add_subdirectory(trx)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -41,7 +41,8 @@ namespace gr {
|
|||
TCH_AFS5_15,
|
||||
TCH_AFS4_75,
|
||||
TCH_FS,
|
||||
TCH_EFR
|
||||
TCH_EFR,
|
||||
TCH_HS,
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 */
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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);
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -2,5 +2,6 @@ add_sources(
|
|||
a5.c
|
||||
auth_core.c
|
||||
gsm48_ie.c
|
||||
gsm_utils.c
|
||||
kasumi.c
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
}*/
|
|
@ -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");
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
add_sources(
|
||||
tch_f_chans_demapper_impl.cc
|
||||
tch_h_chans_demapper_impl.cc
|
||||
universal_ctrl_chans_demapper_impl.cc
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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))));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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(
|
|
@ -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);
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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")
|
|
@ -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")
|
|
@ -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")
|
|
@ -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")
|
|
@ -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")
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue