Initial commit - gsm-receiver with removed quick hacks

This commit is contained in:
piotr 2014-02-04 17:57:25 +01:00
commit 437f5467a1
60 changed files with 18010 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
build
*.o
*.log
*~
*qa_*

155
CMakeLists.txt Normal file
View File

@ -0,0 +1,155 @@
# 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.
########################################################################
# Project setup
########################################################################
cmake_minimum_required(VERSION 2.6)
project(gr-gsm CXX C)
enable_testing()
#select the release build type by default to get optimization flags
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
message(STATUS "Build type not specified: defaulting to release.")
endif(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "")
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
########################################################################
# Compiler specific setup
########################################################################
if(CMAKE_COMPILER_IS_GNUCXX AND NOT WIN32)
#http://gcc.gnu.org/wiki/Visibility
add_definitions(-fvisibility=hidden)
endif()
########################################################################
# Find boost
########################################################################
if(UNIX AND EXISTS "/usr/lib64")
list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix
endif(UNIX AND EXISTS "/usr/lib64")
set(Boost_ADDITIONAL_VERSIONS
"1.35.0" "1.35" "1.36.0" "1.36" "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39"
"1.40.0" "1.40" "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44"
"1.45.0" "1.45" "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49"
"1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53" "1.54.0" "1.54"
"1.55.0" "1.55" "1.56.0" "1.56" "1.57.0" "1.57" "1.58.0" "1.58" "1.59.0" "1.59"
"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)
if(NOT Boost_FOUND)
message(FATAL_ERROR "Boost required to compile gsm")
endif()
########################################################################
# Install directories
########################################################################
include(GrPlatform) #define LIB_SUFFIX
set(GR_RUNTIME_DIR bin)
set(GR_LIBRARY_DIR lib${LIB_SUFFIX})
set(GR_INCLUDE_DIR include/gsm)
set(GR_DATA_DIR share)
set(GR_PKG_DATA_DIR ${GR_DATA_DIR}/${CMAKE_PROJECT_NAME})
set(GR_DOC_DIR ${GR_DATA_DIR}/doc)
set(GR_PKG_DOC_DIR ${GR_DOC_DIR}/${CMAKE_PROJECT_NAME})
set(GR_CONF_DIR etc)
set(GR_PKG_CONF_DIR ${GR_CONF_DIR}/${CMAKE_PROJECT_NAME}/conf.d)
set(GR_LIBEXEC_DIR libexec)
set(GR_PKG_LIBEXEC_DIR ${GR_LIBEXEC_DIR}/${CMAKE_PROJECT_NAME})
set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks)
########################################################################
# Find gnuradio build dependencies
########################################################################
find_package(GnuradioRuntime)
find_package(CppUnit)
# To run a more advanced search for GNU Radio and it's components and
# versions, use the following. Add any components required to the list
# of GR_REQUIRED_COMPONENTS (in all caps) and change "version" to the
# minimum API compatible version required.
#
# set(GR_REQUIRED_COMPONENTS RUNTIME BLOCKS FILTER ...)
# find_package(Gnuradio "version")
if(NOT GNURADIO_RUNTIME_FOUND)
message(FATAL_ERROR "GnuRadio Runtime required to compile gsm")
endif()
if(NOT CPPUNIT_FOUND)
message(FATAL_ERROR "CppUnit required to compile gsm")
endif()
########################################################################
# Setup the include and linker paths
########################################################################
include_directories(
${CMAKE_SOURCE_DIR}/lib
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/lib
${CMAKE_BINARY_DIR}/include
${Boost_INCLUDE_DIRS}
${CPPUNIT_INCLUDE_DIRS}
${GNURADIO_RUNTIME_INCLUDE_DIRS}
)
link_directories(
${Boost_LIBRARY_DIRS}
${CPPUNIT_LIBRARY_DIRS}
${GNURADIO_RUNTIME_LIBRARY_DIRS}
)
# Set component parameters
set(GR_GSM_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "" FORCE)
set(GR_GSM_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/swig CACHE INTERNAL "" FORCE)
########################################################################
# Create uninstall target
########################################################################
configure_file(
${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake
@ONLY)
add_custom_target(uninstall
${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake
)
########################################################################
# Add subdirectories
########################################################################
add_subdirectory(include/gsm)
add_subdirectory(lib)
add_subdirectory(swig)
add_subdirectory(python)
add_subdirectory(grc)
add_subdirectory(apps)
add_subdirectory(docs)
########################################################################
# Install cmake search helper for this library
########################################################################
install(FILES cmake/Modules/gsmConfig.cmake
DESTINATION lib/cmake/gsm
)

25
apps/CMakeLists.txt Normal file
View File

@ -0,0 +1,25 @@
# Copyright 2011 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.
include(GrPython)
GR_PYTHON_INSTALL(
PROGRAMS
DESTINATION bin
)

View File

@ -0,0 +1,138 @@
# CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords> <multi_value_keywords> args...)
#
# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions for
# parsing the arguments given to that macro or function.
# It processes the arguments and defines a set of variables which hold the
# values of the respective options.
#
# The <options> argument contains all options for the respective macro,
# i.e. keywords which can be used when calling the macro without any value
# following, like e.g. the OPTIONAL keyword of the install() command.
#
# The <one_value_keywords> argument contains all keywords for this macro
# which are followed by one value, like e.g. DESTINATION keyword of the
# install() command.
#
# The <multi_value_keywords> argument contains all keywords for this macro
# which can be followed by more than one value, like e.g. the TARGETS or
# FILES keywords of the install() command.
#
# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the
# keywords listed in <options>, <one_value_keywords> and
# <multi_value_keywords> a variable composed of the given <prefix>
# followed by "_" and the name of the respective keyword.
# These variables will then hold the respective value from the argument list.
# For the <options> keywords this will be TRUE or FALSE.
#
# All remaining arguments are collected in a variable
# <prefix>_UNPARSED_ARGUMENTS, this can be checked afterwards to see whether
# your macro was called with unrecognized parameters.
#
# As an example here a my_install() macro, which takes similar arguments as the
# real install() command:
#
# function(MY_INSTALL)
# set(options OPTIONAL FAST)
# set(oneValueArgs DESTINATION RENAME)
# set(multiValueArgs TARGETS CONFIGURATIONS)
# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
# ...
#
# Assume my_install() has been called like this:
# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub)
#
# After the cmake_parse_arguments() call the macro will have set the following
# variables:
# MY_INSTALL_OPTIONAL = TRUE
# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install()
# MY_INSTALL_DESTINATION = "bin"
# MY_INSTALL_RENAME = "" (was not used)
# MY_INSTALL_TARGETS = "foo;bar"
# MY_INSTALL_CONFIGURATIONS = "" (was not used)
# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL"
#
# You can the continue and process these variables.
#
# Keywords terminate lists of values, e.g. if directly after a one_value_keyword
# another recognized keyword follows, this is interpreted as the beginning of
# the new option.
# E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in
# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would
# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor.
#=============================================================================
# Copyright 2010 Alexander Neundorf <neundorf@kde.org>
#
# 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.)
if(__CMAKE_PARSE_ARGUMENTS_INCLUDED)
return()
endif()
set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE)
function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames)
# first set all result variables to empty/FALSE
foreach(arg_name ${_singleArgNames} ${_multiArgNames})
set(${prefix}_${arg_name})
endforeach(arg_name)
foreach(option ${_optionNames})
set(${prefix}_${option} FALSE)
endforeach(option)
set(${prefix}_UNPARSED_ARGUMENTS)
set(insideValues FALSE)
set(currentArgName)
# now iterate over all arguments and fill the result variables
foreach(currentArg ${ARGN})
list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword
list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword
list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword
if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1)
if(insideValues)
if("${insideValues}" STREQUAL "SINGLE")
set(${prefix}_${currentArgName} ${currentArg})
set(insideValues FALSE)
elseif("${insideValues}" STREQUAL "MULTI")
list(APPEND ${prefix}_${currentArgName} ${currentArg})
endif()
else(insideValues)
list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg})
endif(insideValues)
else()
if(NOT ${optionIndex} EQUAL -1)
set(${prefix}_${currentArg} TRUE)
set(insideValues FALSE)
elseif(NOT ${singleArgIndex} EQUAL -1)
set(currentArgName ${currentArg})
set(${prefix}_${currentArgName})
set(insideValues "SINGLE")
elseif(NOT ${multiArgIndex} EQUAL -1)
set(currentArgName ${currentArg})
set(${prefix}_${currentArgName})
set(insideValues "MULTI")
endif()
endif()
endforeach(currentArg)
# propagate the result variables to the caller:
foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames})
set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE)
endforeach(arg_name)
set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE)
endfunction(CMAKE_PARSE_ARGUMENTS _options _singleArgs _multiArgs)

View File

@ -0,0 +1,36 @@
# http://www.cmake.org/pipermail/cmake/2006-October/011446.html
# Modified to use pkg config and use standard var names
#
# Find the CppUnit includes and library
#
# This module defines
# CPPUNIT_INCLUDE_DIR, where to find tiff.h, etc.
# CPPUNIT_LIBRARIES, the libraries to link against to use CppUnit.
# CPPUNIT_FOUND, If false, do not try to use CppUnit.
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_CPPUNIT "cppunit")
FIND_PATH(CPPUNIT_INCLUDE_DIRS
NAMES cppunit/TestCase.h
HINTS ${PC_CPPUNIT_INCLUDE_DIR}
PATHS
/usr/local/include
/usr/include
)
FIND_LIBRARY(CPPUNIT_LIBRARIES
NAMES cppunit
HINTS ${PC_CPPUNIT_LIBDIR}
PATHS
${CPPUNIT_INCLUDE_DIRS}/../lib
/usr/local/lib
/usr/lib
)
LIST(APPEND CPPUNIT_LIBRARIES ${CMAKE_DL_LIBS})
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CPPUNIT DEFAULT_MSG CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS)
MARK_AS_ADVANCED(CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS)

View File

@ -0,0 +1,36 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_GNURADIO_RUNTIME gnuradio-runtime)
if(PC_GNURADIO_RUNTIME_FOUND)
# look for include files
FIND_PATH(
GNURADIO_RUNTIME_INCLUDE_DIRS
NAMES gnuradio/top_block.h
HINTS $ENV{GNURADIO_RUNTIME_DIR}/include
${PC_GNURADIO_RUNTIME_INCLUDE_DIRS}
${CMAKE_INSTALL_PREFIX}/include
PATHS /usr/local/include
/usr/include
)
# look for libs
FIND_LIBRARY(
GNURADIO_RUNTIME_LIBRARIES
NAMES gnuradio-runtime
HINTS $ENV{GNURADIO_RUNTIME_DIR}/lib
${PC_GNURADIO_RUNTIME_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib/
${CMAKE_INSTALL_PREFIX}/lib64/
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
set(GNURADIO_RUNTIME_FOUND ${PC_GNURADIO_RUNTIME_FOUND})
endif(PC_GNURADIO_RUNTIME_FOUND)
INCLUDE(FindPackageHandleStandardArgs)
# do not check GNURADIO_RUNTIME_INCLUDE_DIRS, is not set when default include path us used.
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES)
MARK_AS_ADVANCED(GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)

View File

@ -0,0 +1,210 @@
# Copyright 2010-2011 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.
if(DEFINED __INCLUDED_GR_MISC_UTILS_CMAKE)
return()
endif()
set(__INCLUDED_GR_MISC_UTILS_CMAKE TRUE)
########################################################################
# Set global variable macro.
# Used for subdirectories to export settings.
# Example: include and library paths.
########################################################################
function(GR_SET_GLOBAL var)
set(${var} ${ARGN} CACHE INTERNAL "" FORCE)
endfunction(GR_SET_GLOBAL)
########################################################################
# Set the pre-processor definition if the condition is true.
# - def the pre-processor definition to set and condition name
########################################################################
function(GR_ADD_COND_DEF def)
if(${def})
add_definitions(-D${def})
endif(${def})
endfunction(GR_ADD_COND_DEF)
########################################################################
# Check for a header and conditionally set a compile define.
# - hdr the relative path to the header file
# - def the pre-processor definition to set
########################################################################
function(GR_CHECK_HDR_N_DEF hdr def)
include(CheckIncludeFileCXX)
CHECK_INCLUDE_FILE_CXX(${hdr} ${def})
GR_ADD_COND_DEF(${def})
endfunction(GR_CHECK_HDR_N_DEF)
########################################################################
# Include subdirectory macro.
# Sets the CMake directory variables,
# includes the subdirectory CMakeLists.txt,
# resets the CMake directory variables.
#
# This macro includes subdirectories rather than adding them
# so that the subdirectory can affect variables in the level above.
# This provides a work-around for the lack of convenience libraries.
# This way a subdirectory can append to the list of library sources.
########################################################################
macro(GR_INCLUDE_SUBDIRECTORY subdir)
#insert the current directories on the front of the list
list(INSERT _cmake_source_dirs 0 ${CMAKE_CURRENT_SOURCE_DIR})
list(INSERT _cmake_binary_dirs 0 ${CMAKE_CURRENT_BINARY_DIR})
#set the current directories to the names of the subdirs
set(CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${subdir})
set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${subdir})
#include the subdirectory CMakeLists to run it
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt)
#reset the value of the current directories
list(GET _cmake_source_dirs 0 CMAKE_CURRENT_SOURCE_DIR)
list(GET _cmake_binary_dirs 0 CMAKE_CURRENT_BINARY_DIR)
#pop the subdir names of the front of the list
list(REMOVE_AT _cmake_source_dirs 0)
list(REMOVE_AT _cmake_binary_dirs 0)
endmacro(GR_INCLUDE_SUBDIRECTORY)
########################################################################
# Check if a compiler flag works and conditionally set a compile define.
# - flag the compiler flag to check for
# - have the variable to set with result
########################################################################
macro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG(${flag} ${have})
if(${have})
add_definitions(${flag})
endif(${have})
endmacro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE)
########################################################################
# Generates the .la libtool file
# This appears to generate libtool files that cannot be used by auto*.
# Usage GR_LIBTOOL(TARGET [target] DESTINATION [dest])
# Notice: there is not COMPONENT option, these will not get distributed.
########################################################################
function(GR_LIBTOOL)
if(NOT DEFINED GENERATE_LIBTOOL)
set(GENERATE_LIBTOOL OFF) #disabled by default
endif()
if(GENERATE_LIBTOOL)
include(CMakeParseArgumentsCopy)
CMAKE_PARSE_ARGUMENTS(GR_LIBTOOL "" "TARGET;DESTINATION" "" ${ARGN})
find_program(LIBTOOL libtool)
if(LIBTOOL)
include(CMakeMacroLibtoolFile)
CREATE_LIBTOOL_FILE(${GR_LIBTOOL_TARGET} /${GR_LIBTOOL_DESTINATION})
endif(LIBTOOL)
endif(GENERATE_LIBTOOL)
endfunction(GR_LIBTOOL)
########################################################################
# Do standard things to the library target
# - set target properties
# - make install rules
# Also handle gnuradio custom naming conventions w/ extras mode.
########################################################################
function(GR_LIBRARY_FOO target)
#parse the arguments for component names
include(CMakeParseArgumentsCopy)
CMAKE_PARSE_ARGUMENTS(GR_LIBRARY "" "RUNTIME_COMPONENT;DEVEL_COMPONENT" "" ${ARGN})
#set additional target properties
set_target_properties(${target} PROPERTIES SOVERSION ${LIBVER})
#install the generated files like so...
install(TARGETS ${target}
LIBRARY DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .so/.dylib file
ARCHIVE DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_DEVEL_COMPONENT} # .lib file
RUNTIME DESTINATION ${GR_RUNTIME_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .dll file
)
#extras mode enabled automatically on linux
if(NOT DEFINED LIBRARY_EXTRAS)
set(LIBRARY_EXTRAS ${LINUX})
endif()
#special extras mode to enable alternative naming conventions
if(LIBRARY_EXTRAS)
#create .la file before changing props
GR_LIBTOOL(TARGET ${target} DESTINATION ${GR_LIBRARY_DIR})
#give the library a special name with ultra-zero soversion
set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_NAME ${target}-${LIBVER} SOVERSION "0.0.0")
set(target_name lib${target}-${LIBVER}.so.0.0.0)
#custom command to generate symlinks
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so
COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0
COMMAND ${CMAKE_COMMAND} -E touch ${target_name} #so the symlinks point to something valid so cmake 2.6 will install
)
#and install the extra symlinks
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so
${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0
DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT}
)
endif(LIBRARY_EXTRAS)
endfunction(GR_LIBRARY_FOO)
########################################################################
# Create a dummy custom command that depends on other targets.
# Usage:
# GR_GEN_TARGET_DEPS(unique_name target_deps <target1> <target2> ...)
# ADD_CUSTOM_COMMAND(<the usual args> ${target_deps})
#
# Custom command cant depend on targets, but can depend on executables,
# and executables can depend on targets. So this is the process:
########################################################################
function(GR_GEN_TARGET_DEPS name var)
file(
WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in
"int main(void){return 0;}\n"
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in
${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp
)
add_executable(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp)
if(ARGN)
add_dependencies(${name} ${ARGN})
endif(ARGN)
if(CMAKE_CROSSCOMPILING)
set(${var} "DEPENDS;${name}" PARENT_SCOPE) #cant call command when cross
else()
set(${var} "DEPENDS;${name};COMMAND;${name}" PARENT_SCOPE)
endif()
endfunction(GR_GEN_TARGET_DEPS)

View File

@ -0,0 +1,46 @@
# Copyright 2011 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.
if(DEFINED __INCLUDED_GR_PLATFORM_CMAKE)
return()
endif()
set(__INCLUDED_GR_PLATFORM_CMAKE TRUE)
########################################################################
# Setup additional defines for OS types
########################################################################
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LINUX TRUE)
endif()
if(LINUX AND EXISTS "/etc/debian_version")
set(DEBIAN TRUE)
endif()
if(LINUX AND EXISTS "/etc/redhat-release")
set(REDHAT TRUE)
endif()
########################################################################
# when the library suffix should be 64 (applies to redhat linux family)
########################################################################
if(NOT DEFINED LIB_SUFFIX AND REDHAT AND CMAKE_SYSTEM_PROCESSOR MATCHES "64$")
set(LIB_SUFFIX 64)
endif()
set(LIB_SUFFIX ${LIB_SUFFIX} CACHE STRING "lib directory suffix")

View File

@ -0,0 +1,227 @@
# Copyright 2010-2011 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.
if(DEFINED __INCLUDED_GR_PYTHON_CMAKE)
return()
endif()
set(__INCLUDED_GR_PYTHON_CMAKE TRUE)
########################################################################
# Setup the python interpreter:
# This allows the user to specify a specific interpreter,
# or finds the interpreter via the built-in cmake module.
########################################################################
#this allows the user to override PYTHON_EXECUTABLE
if(PYTHON_EXECUTABLE)
set(PYTHONINTERP_FOUND TRUE)
#otherwise if not set, try to automatically find it
else(PYTHON_EXECUTABLE)
#use the built-in find script
find_package(PythonInterp 2)
#and if that fails use the find program routine
if(NOT PYTHONINTERP_FOUND)
find_program(PYTHON_EXECUTABLE NAMES python python2 python2.7 python2.6 python2.5)
if(PYTHON_EXECUTABLE)
set(PYTHONINTERP_FOUND TRUE)
endif(PYTHON_EXECUTABLE)
endif(NOT PYTHONINTERP_FOUND)
endif(PYTHON_EXECUTABLE)
#make the path to the executable appear in the cmake gui
set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter")
#make sure we can use -B with python (introduced in 2.6)
if(PYTHON_EXECUTABLE)
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -B -c ""
OUTPUT_QUIET ERROR_QUIET
RESULT_VARIABLE PYTHON_HAS_DASH_B_RESULT
)
if(PYTHON_HAS_DASH_B_RESULT EQUAL 0)
set(PYTHON_DASH_B "-B")
endif()
endif(PYTHON_EXECUTABLE)
########################################################################
# Check for the existence of a python module:
# - desc a string description of the check
# - mod the name of the module to import
# - cmd an additional command to run
# - have the result variable to set
########################################################################
macro(GR_PYTHON_CHECK_MODULE desc mod cmd have)
message(STATUS "")
message(STATUS "Python checking for ${desc}")
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c "
#########################################
try: import ${mod}
except: exit(-1)
try: assert ${cmd}
except: exit(-1)
#########################################"
RESULT_VARIABLE ${have}
)
if(${have} EQUAL 0)
message(STATUS "Python checking for ${desc} - found")
set(${have} TRUE)
else(${have} EQUAL 0)
message(STATUS "Python checking for ${desc} - not found")
set(${have} FALSE)
endif(${have} EQUAL 0)
endmacro(GR_PYTHON_CHECK_MODULE)
########################################################################
# Sets the python installation directory GR_PYTHON_DIR
########################################################################
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "
from distutils import sysconfig
print sysconfig.get_python_lib(plat_specific=True, prefix='')
" OUTPUT_VARIABLE GR_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE
)
file(TO_CMAKE_PATH ${GR_PYTHON_DIR} GR_PYTHON_DIR)
########################################################################
# Create an always-built target with a unique name
# Usage: GR_UNIQUE_TARGET(<description> <dependencies list>)
########################################################################
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
unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5]
print(re.sub('\\W', '_', '${desc} ${reldir} ' + unique))"
OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE)
add_custom_target(${_target} ALL DEPENDS ${ARGN})
endfunction(GR_UNIQUE_TARGET)
########################################################################
# Install python sources (also builds and installs byte-compiled python)
########################################################################
function(GR_PYTHON_INSTALL)
include(CMakeParseArgumentsCopy)
CMAKE_PARSE_ARGUMENTS(GR_PYTHON_INSTALL "" "DESTINATION;COMPONENT" "FILES;PROGRAMS" ${ARGN})
####################################################################
if(GR_PYTHON_INSTALL_FILES)
####################################################################
install(${ARGN}) #installs regular python files
#create a list of all generated files
unset(pysrcfiles)
unset(pycfiles)
unset(pyofiles)
foreach(pyfile ${GR_PYTHON_INSTALL_FILES})
get_filename_component(pyfile ${pyfile} ABSOLUTE)
list(APPEND pysrcfiles ${pyfile})
#determine if this file is in the source or binary directory
file(RELATIVE_PATH source_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${pyfile})
string(LENGTH "${source_rel_path}" source_rel_path_len)
file(RELATIVE_PATH binary_rel_path ${CMAKE_CURRENT_BINARY_DIR} ${pyfile})
string(LENGTH "${binary_rel_path}" binary_rel_path_len)
#and set the generated path appropriately
if(${source_rel_path_len} GREATER ${binary_rel_path_len})
set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${binary_rel_path})
else()
set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${source_rel_path})
endif()
list(APPEND pycfiles ${pygenfile}c)
list(APPEND pyofiles ${pygenfile}o)
#ensure generation path exists
get_filename_component(pygen_path ${pygenfile} PATH)
file(MAKE_DIRECTORY ${pygen_path})
endforeach(pyfile)
#the command to generate the pyc files
add_custom_command(
DEPENDS ${pysrcfiles} OUTPUT ${pycfiles}
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pycfiles}
)
#the command to generate the pyo files
add_custom_command(
DEPENDS ${pysrcfiles} OUTPUT ${pyofiles}
COMMAND ${PYTHON_EXECUTABLE} -O ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pyofiles}
)
#create install rule and add generated files to target list
set(python_install_gen_targets ${pycfiles} ${pyofiles})
install(FILES ${python_install_gen_targets}
DESTINATION ${GR_PYTHON_INSTALL_DESTINATION}
COMPONENT ${GR_PYTHON_INSTALL_COMPONENT}
)
####################################################################
elseif(GR_PYTHON_INSTALL_PROGRAMS)
####################################################################
file(TO_NATIVE_PATH ${PYTHON_EXECUTABLE} pyexe_native)
foreach(pyfile ${GR_PYTHON_INSTALL_PROGRAMS})
get_filename_component(pyfile_name ${pyfile} NAME)
get_filename_component(pyfile ${pyfile} ABSOLUTE)
string(REPLACE "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" pyexefile "${pyfile}.exe")
list(APPEND python_install_gen_targets ${pyexefile})
get_filename_component(pyexefile_path ${pyexefile} PATH)
file(MAKE_DIRECTORY ${pyexefile_path})
add_custom_command(
OUTPUT ${pyexefile} DEPENDS ${pyfile}
COMMAND ${PYTHON_EXECUTABLE} -c
\"open('${pyexefile}', 'w').write('\#!${pyexe_native}\\n'+open('${pyfile}').read())\"
COMMENT "Shebangin ${pyfile_name}"
)
#on windows, python files need an extension to execute
get_filename_component(pyfile_ext ${pyfile} EXT)
if(WIN32 AND NOT pyfile_ext)
set(pyfile_name "${pyfile_name}.py")
endif()
install(PROGRAMS ${pyexefile} RENAME ${pyfile_name}
DESTINATION ${GR_PYTHON_INSTALL_DESTINATION}
COMPONENT ${GR_PYTHON_INSTALL_COMPONENT}
)
endforeach(pyfile)
endif()
GR_UNIQUE_TARGET("pygen" ${python_install_gen_targets})
endfunction(GR_PYTHON_INSTALL)
########################################################################
# Write the python helper script that generates byte code files
########################################################################
file(WRITE ${CMAKE_BINARY_DIR}/python_compile_helper.py "
import sys, py_compile
files = sys.argv[1:]
srcs, gens = files[:len(files)/2], files[len(files)/2:]
for src, gen in zip(srcs, gens):
py_compile.compile(file=src, cfile=gen, doraise=True)
")

229
cmake/Modules/GrSwig.cmake Normal file
View File

@ -0,0 +1,229 @@
# Copyright 2010-2011 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.
if(DEFINED __INCLUDED_GR_SWIG_CMAKE)
return()
endif()
set(__INCLUDED_GR_SWIG_CMAKE TRUE)
include(GrPython)
########################################################################
# Builds a swig documentation file to be generated into python docstrings
# Usage: GR_SWIG_MAKE_DOCS(output_file input_path input_path....)
#
# Set the following variable to specify extra dependent targets:
# - GR_SWIG_DOCS_SOURCE_DEPS
# - GR_SWIG_DOCS_TARGET_DEPS
########################################################################
function(GR_SWIG_MAKE_DOCS output_file)
find_package(Doxygen)
if(DOXYGEN_FOUND)
#setup the input files variable list, quote formated
set(input_files)
unset(INPUT_PATHS)
foreach(input_path ${ARGN})
if (IS_DIRECTORY ${input_path}) #when input path is a directory
file(GLOB input_path_h_files ${input_path}/*.h)
else() #otherwise its just a file, no glob
set(input_path_h_files ${input_path})
endif()
list(APPEND input_files ${input_path_h_files})
set(INPUT_PATHS "${INPUT_PATHS} \"${input_path}\"")
endforeach(input_path)
#determine the output directory
get_filename_component(name ${output_file} NAME_WE)
get_filename_component(OUTPUT_DIRECTORY ${output_file} PATH)
set(OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}/${name}_swig_docs)
make_directory(${OUTPUT_DIRECTORY})
#generate the Doxyfile used by doxygen
configure_file(
${CMAKE_SOURCE_DIR}/docs/doxygen/Doxyfile.swig_doc.in
${OUTPUT_DIRECTORY}/Doxyfile
@ONLY)
#Create a dummy custom command that depends on other targets
include(GrMiscUtils)
GR_GEN_TARGET_DEPS(_${name}_tag tag_deps ${GR_SWIG_DOCS_TARGET_DEPS})
#call doxygen on the Doxyfile + input headers
add_custom_command(
OUTPUT ${OUTPUT_DIRECTORY}/xml/index.xml
DEPENDS ${input_files} ${GR_SWIG_DOCS_SOURCE_DEPS} ${tag_deps}
COMMAND ${DOXYGEN_EXECUTABLE} ${OUTPUT_DIRECTORY}/Doxyfile
COMMENT "Generating doxygen xml for ${name} docs"
)
#call the swig_doc script on the xml files
add_custom_command(
OUTPUT ${output_file}
DEPENDS ${input_files} ${OUTPUT_DIRECTORY}/xml/index.xml
COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B}
${CMAKE_SOURCE_DIR}/docs/doxygen/swig_doc.py
${OUTPUT_DIRECTORY}/xml
${output_file}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/doxygen
)
else(DOXYGEN_FOUND)
file(WRITE ${output_file} "\n") #no doxygen -> empty file
endif(DOXYGEN_FOUND)
endfunction(GR_SWIG_MAKE_DOCS)
########################################################################
# Build a swig target for the common gnuradio use case. Usage:
# GR_SWIG_MAKE(target ifile ifile ifile...)
#
# Set the following variables before calling:
# - GR_SWIG_FLAGS
# - GR_SWIG_INCLUDE_DIRS
# - GR_SWIG_LIBRARIES
# - GR_SWIG_SOURCE_DEPS
# - GR_SWIG_TARGET_DEPS
# - GR_SWIG_DOC_FILE
# - GR_SWIG_DOC_DIRS
########################################################################
macro(GR_SWIG_MAKE name)
set(ifiles ${ARGN})
#do swig doc generation if specified
if (GR_SWIG_DOC_FILE)
set(GR_SWIG_DOCS_SOURCE_DEPS ${GR_SWIG_SOURCE_DEPS})
set(GR_SWIG_DOCS_TAREGT_DEPS ${GR_SWIG_TARGET_DEPS})
GR_SWIG_MAKE_DOCS(${GR_SWIG_DOC_FILE} ${GR_SWIG_DOC_DIRS})
list(APPEND GR_SWIG_SOURCE_DEPS ${GR_SWIG_DOC_FILE})
endif()
#append additional include directories
find_package(PythonLibs 2)
list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_PATH}) #deprecated name (now dirs)
list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS})
list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR})
#determine include dependencies for swig file
execute_process(
COMMAND ${PYTHON_EXECUTABLE}
${CMAKE_BINARY_DIR}/get_swig_deps.py
"${ifiles}" "${GR_SWIG_INCLUDE_DIRS}"
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE SWIG_MODULE_${name}_EXTRA_DEPS
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
#Create a dummy custom command that depends on other targets
include(GrMiscUtils)
GR_GEN_TARGET_DEPS(_${name}_swig_tag tag_deps ${GR_SWIG_TARGET_DEPS})
set(tag_file ${CMAKE_CURRENT_BINARY_DIR}/${name}.tag)
add_custom_command(
OUTPUT ${tag_file}
DEPENDS ${GR_SWIG_SOURCE_DEPS} ${tag_deps}
COMMAND ${CMAKE_COMMAND} -E touch ${tag_file}
)
#append the specified include directories
include_directories(${GR_SWIG_INCLUDE_DIRS})
list(APPEND SWIG_MODULE_${name}_EXTRA_DEPS ${tag_file})
#setup the swig flags with flags and include directories
set(CMAKE_SWIG_FLAGS -fvirtual -modern -keyword -w511 -module ${name} ${GR_SWIG_FLAGS})
foreach(dir ${GR_SWIG_INCLUDE_DIRS})
list(APPEND CMAKE_SWIG_FLAGS "-I${dir}")
endforeach(dir)
#set the C++ property on the swig .i file so it builds
set_source_files_properties(${ifiles} PROPERTIES CPLUSPLUS ON)
#setup the actual swig library target to be built
include(UseSWIG)
SWIG_ADD_MODULE(${name} python ${ifiles})
SWIG_LINK_LIBRARIES(${name} ${PYTHON_LIBRARIES} ${GR_SWIG_LIBRARIES})
endmacro(GR_SWIG_MAKE)
########################################################################
# Install swig targets generated by GR_SWIG_MAKE. Usage:
# GR_SWIG_INSTALL(
# TARGETS target target target...
# [DESTINATION destination]
# [COMPONENT component]
# )
########################################################################
macro(GR_SWIG_INSTALL)
include(CMakeParseArgumentsCopy)
CMAKE_PARSE_ARGUMENTS(GR_SWIG_INSTALL "" "DESTINATION;COMPONENT" "TARGETS" ${ARGN})
foreach(name ${GR_SWIG_INSTALL_TARGETS})
install(TARGETS ${SWIG_MODULE_${name}_REAL_NAME}
DESTINATION ${GR_SWIG_INSTALL_DESTINATION}
COMPONENT ${GR_SWIG_INSTALL_COMPONENT}
)
include(GrPython)
GR_PYTHON_INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}.py
DESTINATION ${GR_SWIG_INSTALL_DESTINATION}
COMPONENT ${GR_SWIG_INSTALL_COMPONENT}
)
GR_LIBTOOL(
TARGET ${SWIG_MODULE_${name}_REAL_NAME}
DESTINATION ${GR_SWIG_INSTALL_DESTINATION}
)
endforeach(name)
endmacro(GR_SWIG_INSTALL)
########################################################################
# Generate a python file that can determine swig dependencies.
# Used by the make macro above to determine extra dependencies.
# When you build C++, CMake figures out the header dependencies.
# This code essentially performs that logic for swig includes.
########################################################################
file(WRITE ${CMAKE_BINARY_DIR}/get_swig_deps.py "
import os, sys, re
include_matcher = re.compile('[#|%]include\\s*[<|\"](.*)[>|\"]')
include_dirs = sys.argv[2].split(';')
def get_swig_incs(file_path):
file_contents = open(file_path, 'r').read()
return include_matcher.findall(file_contents, re.MULTILINE)
def get_swig_deps(file_path, level):
deps = [file_path]
if level == 0: return deps
for inc_file in get_swig_incs(file_path):
for inc_dir in include_dirs:
inc_path = os.path.join(inc_dir, inc_file)
if not os.path.exists(inc_path): continue
deps.extend(get_swig_deps(inc_path, level-1))
return deps
if __name__ == '__main__':
ifiles = sys.argv[1].split(';')
deps = sum([get_swig_deps(ifile, 3) for ifile in ifiles], [])
#sys.stderr.write(';'.join(set(deps)) + '\\n\\n')
print(';'.join(set(deps)))
")

133
cmake/Modules/GrTest.cmake Normal file
View File

@ -0,0 +1,133 @@
# Copyright 2010-2011 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.
if(DEFINED __INCLUDED_GR_TEST_CMAKE)
return()
endif()
set(__INCLUDED_GR_TEST_CMAKE TRUE)
########################################################################
# Add a unit test and setup the environment for a unit test.
# Takes the same arguments as the ADD_TEST function.
#
# Before calling set the following variables:
# GR_TEST_TARGET_DEPS - built targets for the library path
# GR_TEST_LIBRARY_DIRS - directories for the library path
# GR_TEST_PYTHON_DIRS - directories for the python path
########################################################################
function(GR_ADD_TEST test_name)
if(WIN32)
#Ensure that the build exe also appears in the PATH.
list(APPEND GR_TEST_TARGET_DEPS ${ARGN})
#In the land of windows, all libraries must be in the PATH.
#Since the dependent libraries are not yet installed,
#we must manually set them in the PATH to run tests.
#The following appends the path of a target dependency.
foreach(target ${GR_TEST_TARGET_DEPS})
get_target_property(location ${target} LOCATION)
if(location)
get_filename_component(path ${location} PATH)
string(REGEX REPLACE "\\$\\(.*\\)" ${CMAKE_BUILD_TYPE} path ${path})
list(APPEND GR_TEST_LIBRARY_DIRS ${path})
endif(location)
endforeach(target)
#SWIG generates the python library files into a subdirectory.
#Therefore, we must append this subdirectory into PYTHONPATH.
#Only do this for the python directories matching the following:
foreach(pydir ${GR_TEST_PYTHON_DIRS})
get_filename_component(name ${pydir} NAME)
if(name MATCHES "^(swig|lib|src)$")
list(APPEND GR_TEST_PYTHON_DIRS ${pydir}/${CMAKE_BUILD_TYPE})
endif()
endforeach(pydir)
endif(WIN32)
file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} srcdir)
file(TO_NATIVE_PATH "${GR_TEST_LIBRARY_DIRS}" libpath) #ok to use on dir list?
file(TO_NATIVE_PATH "${GR_TEST_PYTHON_DIRS}" pypath) #ok to use on dir list?
set(environs "GR_DONT_LOAD_PREFS=1" "srcdir=${srcdir}")
#http://www.cmake.org/pipermail/cmake/2009-May/029464.html
#Replaced this add test + set environs code with the shell script generation.
#Its nicer to be able to manually run the shell script to diagnose problems.
#ADD_TEST(${ARGV})
#SET_TESTS_PROPERTIES(${test_name} PROPERTIES ENVIRONMENT "${environs}")
if(UNIX)
set(binpath "${CMAKE_CURRENT_BINARY_DIR}:$PATH")
#set both LD and DYLD paths to cover multiple UNIX OS library paths
list(APPEND libpath "$LD_LIBRARY_PATH" "$DYLD_LIBRARY_PATH")
list(APPEND pypath "$PYTHONPATH")
#replace list separator with the path separator
string(REPLACE ";" ":" libpath "${libpath}")
string(REPLACE ";" ":" pypath "${pypath}")
list(APPEND environs "PATH=${binpath}" "LD_LIBRARY_PATH=${libpath}" "DYLD_LIBRARY_PATH=${libpath}" "PYTHONPATH=${pypath}")
#generate a bat file that sets the environment and runs the test
find_program(SHELL sh)
set(sh_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.sh)
file(WRITE ${sh_file} "#!${SHELL}\n")
#each line sets an environment variable
foreach(environ ${environs})
file(APPEND ${sh_file} "export ${environ}\n")
endforeach(environ)
#load the command to run with its arguments
foreach(arg ${ARGN})
file(APPEND ${sh_file} "${arg} ")
endforeach(arg)
file(APPEND ${sh_file} "\n")
#make the shell file executable
execute_process(COMMAND chmod +x ${sh_file})
add_test(${test_name} ${SHELL} ${sh_file})
endif(UNIX)
if(WIN32)
list(APPEND libpath ${DLL_PATHS} "%PATH%")
list(APPEND pypath "%PYTHONPATH%")
#replace list separator with the path separator (escaped)
string(REPLACE ";" "\\;" libpath "${libpath}")
string(REPLACE ";" "\\;" pypath "${pypath}")
list(APPEND environs "PATH=${libpath}" "PYTHONPATH=${pypath}")
#generate a bat file that sets the environment and runs the test
set(bat_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.bat)
file(WRITE ${bat_file} "@echo off\n")
#each line sets an environment variable
foreach(environ ${environs})
file(APPEND ${bat_file} "SET ${environ}\n")
endforeach(environ)
#load the command to run with its arguments
foreach(arg ${ARGN})
file(APPEND ${bat_file} "${arg} ")
endforeach(arg)
file(APPEND ${bat_file} "\n")
add_test(${test_name} ${bat_file})
endif(WIN32)
endfunction(GR_ADD_TEST)

View File

@ -0,0 +1,30 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_GSM gsm)
FIND_PATH(
GSM_INCLUDE_DIRS
NAMES gsm/api.h
HINTS $ENV{GSM_DIR}/include
${PC_GSM_INCLUDEDIR}
PATHS ${CMAKE_INSTALL_PREFIX}/include
/usr/local/include
/usr/include
)
FIND_LIBRARY(
GSM_LIBRARIES
NAMES gnuradio-gsm
HINTS $ENV{GSM_DIR}/lib
${PC_GSM_LIBDIR}
PATHS ${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
/usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GSM DEFAULT_MSG GSM_LIBRARIES GSM_INCLUDE_DIRS)
MARK_AS_ADVANCED(GSM_LIBRARIES GSM_INCLUDE_DIRS)

View File

@ -0,0 +1,32 @@
# http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F
IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
STRING(REGEX REPLACE "\n" ";" files "${files}")
FOREACH(file ${files})
MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
IF(EXISTS "$ENV{DESTDIR}${file}")
EXEC_PROGRAM(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
IF(NOT "${rm_retval}" STREQUAL 0)
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
ENDIF(NOT "${rm_retval}" STREQUAL 0)
ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}")
EXEC_PROGRAM(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
IF(NOT "${rm_retval}" STREQUAL 0)
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
ENDIF(NOT "${rm_retval}" STREQUAL 0)
ELSE(EXISTS "$ENV{DESTDIR}${file}")
MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
ENDIF(EXISTS "$ENV{DESTDIR}${file}")
ENDFOREACH(file)

35
docs/CMakeLists.txt Normal file
View File

@ -0,0 +1,35 @@
# Copyright 2011 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.
########################################################################
# Setup dependencies
########################################################################
find_package(Doxygen)
########################################################################
# Begin conditional configuration
########################################################################
if(ENABLE_DOXYGEN)
########################################################################
# Add subdirectories
########################################################################
add_subdirectory(doxygen)
endif(ENABLE_DOXYGEN)

11
docs/README.gsm Normal file
View File

@ -0,0 +1,11 @@
This is the gsm-write-a-block package meant as a guide to building
out-of-tree packages. To use the gsm blocks, the Python namespaces
is in 'gsm', which is imported as:
import gsm
See the Doxygen documentation for details about the blocks available
in this package. A quick listing of the details can be found in Python
after importing by using:
help(gsm)

View File

@ -0,0 +1,52 @@
# Copyright 2011 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.
########################################################################
# Create the doxygen configuration file
########################################################################
file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} top_srcdir)
file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} top_builddir)
file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} abs_top_srcdir)
file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} abs_top_builddir)
set(HAVE_DOT ${DOXYGEN_DOT_FOUND})
set(enable_html_docs YES)
set(enable_latex_docs NO)
set(enable_xml_docs YES)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
@ONLY)
set(BUILT_DIRS ${CMAKE_CURRENT_BINARY_DIR}/xml ${CMAKE_CURRENT_BINARY_DIR}/html)
########################################################################
# Make and install doxygen docs
########################################################################
add_custom_command(
OUTPUT ${BUILT_DIRS}
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating documentation with doxygen"
)
add_custom_target(doxygen_target ALL DEPENDS ${BUILT_DIRS})
install(DIRECTORY ${BUILT_DIRS} DESTINATION ${GR_PKG_DOC_DIR})

1504
docs/doxygen/Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
#
# Copyright 2010 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.
#
"""
Python interface to contents of doxygen xml documentation.
Example use:
See the contents of the example folder for the C++ and
doxygen-generated xml used in this example.
>>> # Parse the doxygen docs.
>>> import os
>>> this_dir = os.path.dirname(globals()['__file__'])
>>> xml_path = this_dir + "/example/xml/"
>>> di = DoxyIndex(xml_path)
Get a list of all top-level objects.
>>> print([mem.name() for mem in di.members()])
[u'Aadvark', u'aadvarky_enough', u'main']
Get all functions.
>>> print([mem.name() for mem in di.in_category(DoxyFunction)])
[u'aadvarky_enough', u'main']
Check if an object is present.
>>> di.has_member(u'Aadvark')
True
>>> di.has_member(u'Fish')
False
Get an item by name and check its properties.
>>> aad = di.get_member(u'Aadvark')
>>> print(aad.brief_description)
Models the mammal Aadvark.
>>> print(aad.detailed_description)
Sadly the model is incomplete and cannot capture all aspects of an aadvark yet.
<BLANKLINE>
This line is uninformative and is only to test line breaks in the comments.
>>> [mem.name() for mem in aad.members()]
[u'aadvarkness', u'print', u'Aadvark', u'get_aadvarkness']
>>> aad.get_member(u'print').brief_description
u'Outputs the vital aadvark statistics.'
"""
from doxyindex import DoxyIndex, DoxyFunction, DoxyParam, DoxyClass, DoxyFile, DoxyNamespace, DoxyGroup, DoxyFriend, DoxyOther
def _test():
import os
this_dir = os.path.dirname(globals()['__file__'])
xml_path = this_dir + "/example/xml/"
di = DoxyIndex(xml_path)
# Get the Aadvark class
aad = di.get_member('Aadvark')
aad.brief_description
import doctest
return doctest.testmod()
if __name__ == "__main__":
_test()

View File

@ -0,0 +1,219 @@
#
# Copyright 2010 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.
#
"""
A base class is created.
Classes based upon this are used to make more user-friendly interfaces
to the doxygen xml docs than the generated classes provide.
"""
import os
import pdb
from xml.parsers.expat import ExpatError
from generated import compound
class Base(object):
class Duplicate(StandardError):
pass
class NoSuchMember(StandardError):
pass
class ParsingError(StandardError):
pass
def __init__(self, parse_data, top=None):
self._parsed = False
self._error = False
self._parse_data = parse_data
self._members = []
self._dict_members = {}
self._in_category = {}
self._data = {}
if top is not None:
self._xml_path = top._xml_path
# Set up holder of references
else:
top = self
self._refs = {}
self._xml_path = parse_data
self.top = top
@classmethod
def from_refid(cls, refid, top=None):
""" Instantiate class from a refid rather than parsing object. """
# First check to see if its already been instantiated.
if top is not None and refid in top._refs:
return top._refs[refid]
# Otherwise create a new instance and set refid.
inst = cls(None, top=top)
inst.refid = refid
inst.add_ref(inst)
return inst
@classmethod
def from_parse_data(cls, parse_data, top=None):
refid = getattr(parse_data, 'refid', None)
if refid is not None and top is not None and refid in top._refs:
return top._refs[refid]
inst = cls(parse_data, top=top)
if refid is not None:
inst.refid = refid
inst.add_ref(inst)
return inst
def add_ref(self, obj):
if hasattr(obj, 'refid'):
self.top._refs[obj.refid] = obj
mem_classes = []
def get_cls(self, mem):
for cls in self.mem_classes:
if cls.can_parse(mem):
return cls
raise StandardError(("Did not find a class for object '%s'." \
% (mem.get_name())))
def convert_mem(self, mem):
try:
cls = self.get_cls(mem)
converted = cls.from_parse_data(mem, self.top)
if converted is None:
raise StandardError('No class matched this object.')
self.add_ref(converted)
return converted
except StandardError, e:
print e
@classmethod
def includes(cls, inst):
return isinstance(inst, cls)
@classmethod
def can_parse(cls, obj):
return False
def _parse(self):
self._parsed = True
def _get_dict_members(self, cat=None):
"""
For given category a dictionary is returned mapping member names to
members of that category. For names that are duplicated the name is
mapped to None.
"""
self.confirm_no_error()
if cat not in self._dict_members:
new_dict = {}
for mem in self.in_category(cat):
if mem.name() not in new_dict:
new_dict[mem.name()] = mem
else:
new_dict[mem.name()] = self.Duplicate
self._dict_members[cat] = new_dict
return self._dict_members[cat]
def in_category(self, cat):
self.confirm_no_error()
if cat is None:
return self._members
if cat not in self._in_category:
self._in_category[cat] = [mem for mem in self._members
if cat.includes(mem)]
return self._in_category[cat]
def get_member(self, name, cat=None):
self.confirm_no_error()
# Check if it's in a namespace or class.
bits = name.split('::')
first = bits[0]
rest = '::'.join(bits[1:])
member = self._get_dict_members(cat).get(first, self.NoSuchMember)
# Raise any errors that are returned.
if member in set([self.NoSuchMember, self.Duplicate]):
raise member()
if rest:
return member.get_member(rest, cat=cat)
return member
def has_member(self, name, cat=None):
try:
mem = self.get_member(name, cat=cat)
return True
except self.NoSuchMember:
return False
def data(self):
self.confirm_no_error()
return self._data
def members(self):
self.confirm_no_error()
return self._members
def process_memberdefs(self):
mdtss = []
for sec in self._retrieved_data.compounddef.sectiondef:
mdtss += sec.memberdef
# At the moment we lose all information associated with sections.
# Sometimes a memberdef is in several sectiondef.
# We make sure we don't get duplicates here.
uniques = set([])
for mem in mdtss:
converted = self.convert_mem(mem)
pair = (mem.name, mem.__class__)
if pair not in uniques:
uniques.add(pair)
self._members.append(converted)
def retrieve_data(self):
filename = os.path.join(self._xml_path, self.refid + '.xml')
try:
self._retrieved_data = compound.parse(filename)
except ExpatError:
print('Error in xml in file %s' % filename)
self._error = True
self._retrieved_data = None
def check_parsed(self):
if not self._parsed:
self._parse()
def confirm_no_error(self):
self.check_parsed()
if self._error:
raise self.ParsingError()
def error(self):
self.check_parsed()
return self._error
def name(self):
# first see if we can do it without processing.
if self._parse_data is not None:
return self._parse_data.name
self.check_parsed()
return self._retrieved_data.compounddef.name

View File

@ -0,0 +1,237 @@
#
# Copyright 2010 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.
#
"""
Classes providing more user-friendly interfaces to the doxygen xml
docs than the generated classes provide.
"""
import os
from generated import index
from base import Base
from text import description
class DoxyIndex(Base):
"""
Parses a doxygen xml directory.
"""
__module__ = "gnuradio.utils.doxyxml"
def _parse(self):
if self._parsed:
return
super(DoxyIndex, self)._parse()
self._root = index.parse(os.path.join(self._xml_path, 'index.xml'))
for mem in self._root.compound:
converted = self.convert_mem(mem)
# For files we want the contents to be accessible directly
# from the parent rather than having to go through the file
# object.
if self.get_cls(mem) == DoxyFile:
if mem.name.endswith('.h'):
self._members += converted.members()
self._members.append(converted)
else:
self._members.append(converted)
def generate_swig_doc_i(self):
"""
%feature("docstring") gr_make_align_on_samplenumbers_ss::align_state "
Wraps the C++: gr_align_on_samplenumbers_ss::align_state";
"""
pass
class DoxyCompMem(Base):
kind = None
def __init__(self, *args, **kwargs):
super(DoxyCompMem, self).__init__(*args, **kwargs)
@classmethod
def can_parse(cls, obj):
return obj.kind == cls.kind
def set_descriptions(self, parse_data):
bd = description(getattr(parse_data, 'briefdescription', None))
dd = description(getattr(parse_data, 'detaileddescription', None))
self._data['brief_description'] = bd
self._data['detailed_description'] = dd
class DoxyCompound(DoxyCompMem):
pass
class DoxyMember(DoxyCompMem):
pass
class DoxyFunction(DoxyMember):
__module__ = "gnuradio.utils.doxyxml"
kind = 'function'
def _parse(self):
if self._parsed:
return
super(DoxyFunction, self)._parse()
self.set_descriptions(self._parse_data)
self._data['params'] = []
prms = self._parse_data.param
for prm in prms:
self._data['params'].append(DoxyParam(prm))
brief_description = property(lambda self: self.data()['brief_description'])
detailed_description = property(lambda self: self.data()['detailed_description'])
params = property(lambda self: self.data()['params'])
Base.mem_classes.append(DoxyFunction)
class DoxyParam(DoxyMember):
__module__ = "gnuradio.utils.doxyxml"
def _parse(self):
if self._parsed:
return
super(DoxyParam, self)._parse()
self.set_descriptions(self._parse_data)
self._data['declname'] = self._parse_data.declname
brief_description = property(lambda self: self.data()['brief_description'])
detailed_description = property(lambda self: self.data()['detailed_description'])
declname = property(lambda self: self.data()['declname'])
class DoxyClass(DoxyCompound):
__module__ = "gnuradio.utils.doxyxml"
kind = 'class'
def _parse(self):
if self._parsed:
return
super(DoxyClass, self)._parse()
self.retrieve_data()
if self._error:
return
self.set_descriptions(self._retrieved_data.compounddef)
# Sectiondef.kind tells about whether private or public.
# We just ignore this for now.
self.process_memberdefs()
brief_description = property(lambda self: self.data()['brief_description'])
detailed_description = property(lambda self: self.data()['detailed_description'])
Base.mem_classes.append(DoxyClass)
class DoxyFile(DoxyCompound):
__module__ = "gnuradio.utils.doxyxml"
kind = 'file'
def _parse(self):
if self._parsed:
return
super(DoxyFile, self)._parse()
self.retrieve_data()
self.set_descriptions(self._retrieved_data.compounddef)
if self._error:
return
self.process_memberdefs()
brief_description = property(lambda self: self.data()['brief_description'])
detailed_description = property(lambda self: self.data()['detailed_description'])
Base.mem_classes.append(DoxyFile)
class DoxyNamespace(DoxyCompound):
__module__ = "gnuradio.utils.doxyxml"
kind = 'namespace'
Base.mem_classes.append(DoxyNamespace)
class DoxyGroup(DoxyCompound):
__module__ = "gnuradio.utils.doxyxml"
kind = 'group'
def _parse(self):
if self._parsed:
return
super(DoxyGroup, self)._parse()
self.retrieve_data()
if self._error:
return
cdef = self._retrieved_data.compounddef
self._data['title'] = description(cdef.title)
# Process inner groups
grps = cdef.innergroup
for grp in grps:
converted = DoxyGroup.from_refid(grp.refid, top=self.top)
self._members.append(converted)
# Process inner classes
klasses = cdef.innerclass
for kls in klasses:
converted = DoxyClass.from_refid(kls.refid, top=self.top)
self._members.append(converted)
# Process normal members
self.process_memberdefs()
title = property(lambda self: self.data()['title'])
Base.mem_classes.append(DoxyGroup)
class DoxyFriend(DoxyMember):
__module__ = "gnuradio.utils.doxyxml"
kind = 'friend'
Base.mem_classes.append(DoxyFriend)
class DoxyOther(Base):
__module__ = "gnuradio.utils.doxyxml"
kinds = set(['variable', 'struct', 'union', 'define', 'typedef', 'enum', 'dir', 'page'])
@classmethod
def can_parse(cls, obj):
return obj.kind in cls.kinds
Base.mem_classes.append(DoxyOther)

View File

@ -0,0 +1,7 @@
"""
Contains generated files produced by generateDS.py.
These do the real work of parsing the doxygen xml files but the
resultant classes are not very friendly to navigate so the rest of the
doxyxml module processes them further.
"""

View File

@ -0,0 +1,503 @@
#!/usr/bin/env python
"""
Generated Mon Feb 9 19:08:05 2009 by generateDS.py.
"""
from string import lower as str_lower
from xml.dom import minidom
from xml.dom import Node
import sys
import compoundsuper as supermod
from compoundsuper import MixedContainer
class DoxygenTypeSub(supermod.DoxygenType):
def __init__(self, version=None, compounddef=None):
supermod.DoxygenType.__init__(self, version, compounddef)
def find(self, details):
return self.compounddef.find(details)
supermod.DoxygenType.subclass = DoxygenTypeSub
# end class DoxygenTypeSub
class compounddefTypeSub(supermod.compounddefType):
def __init__(self, kind=None, prot=None, id=None, compoundname='', title='', basecompoundref=None, derivedcompoundref=None, includes=None, includedby=None, incdepgraph=None, invincdepgraph=None, innerdir=None, innerfile=None, innerclass=None, innernamespace=None, innerpage=None, innergroup=None, templateparamlist=None, sectiondef=None, briefdescription=None, detaileddescription=None, inheritancegraph=None, collaborationgraph=None, programlisting=None, location=None, listofallmembers=None):
supermod.compounddefType.__init__(self, kind, prot, id, compoundname, title, basecompoundref, derivedcompoundref, includes, includedby, incdepgraph, invincdepgraph, innerdir, innerfile, innerclass, innernamespace, innerpage, innergroup, templateparamlist, sectiondef, briefdescription, detaileddescription, inheritancegraph, collaborationgraph, programlisting, location, listofallmembers)
def find(self, details):
if self.id == details.refid:
return self
for sectiondef in self.sectiondef:
result = sectiondef.find(details)
if result:
return result
supermod.compounddefType.subclass = compounddefTypeSub
# end class compounddefTypeSub
class listofallmembersTypeSub(supermod.listofallmembersType):
def __init__(self, member=None):
supermod.listofallmembersType.__init__(self, member)
supermod.listofallmembersType.subclass = listofallmembersTypeSub
# end class listofallmembersTypeSub
class memberRefTypeSub(supermod.memberRefType):
def __init__(self, virt=None, prot=None, refid=None, ambiguityscope=None, scope='', name=''):
supermod.memberRefType.__init__(self, virt, prot, refid, ambiguityscope, scope, name)
supermod.memberRefType.subclass = memberRefTypeSub
# end class memberRefTypeSub
class compoundRefTypeSub(supermod.compoundRefType):
def __init__(self, virt=None, prot=None, refid=None, valueOf_='', mixedclass_=None, content_=None):
supermod.compoundRefType.__init__(self, mixedclass_, content_)
supermod.compoundRefType.subclass = compoundRefTypeSub
# end class compoundRefTypeSub
class reimplementTypeSub(supermod.reimplementType):
def __init__(self, refid=None, valueOf_='', mixedclass_=None, content_=None):
supermod.reimplementType.__init__(self, mixedclass_, content_)
supermod.reimplementType.subclass = reimplementTypeSub
# end class reimplementTypeSub
class incTypeSub(supermod.incType):
def __init__(self, local=None, refid=None, valueOf_='', mixedclass_=None, content_=None):
supermod.incType.__init__(self, mixedclass_, content_)
supermod.incType.subclass = incTypeSub
# end class incTypeSub
class refTypeSub(supermod.refType):
def __init__(self, prot=None, refid=None, valueOf_='', mixedclass_=None, content_=None):
supermod.refType.__init__(self, mixedclass_, content_)
supermod.refType.subclass = refTypeSub
# end class refTypeSub
class refTextTypeSub(supermod.refTextType):
def __init__(self, refid=None, kindref=None, external=None, valueOf_='', mixedclass_=None, content_=None):
supermod.refTextType.__init__(self, mixedclass_, content_)
supermod.refTextType.subclass = refTextTypeSub
# end class refTextTypeSub
class sectiondefTypeSub(supermod.sectiondefType):
def __init__(self, kind=None, header='', description=None, memberdef=None):
supermod.sectiondefType.__init__(self, kind, header, description, memberdef)
def find(self, details):
for memberdef in self.memberdef:
if memberdef.id == details.refid:
return memberdef
return None
supermod.sectiondefType.subclass = sectiondefTypeSub
# end class sectiondefTypeSub
class memberdefTypeSub(supermod.memberdefType):
def __init__(self, initonly=None, kind=None, volatile=None, const=None, raise_=None, virt=None, readable=None, prot=None, explicit=None, new=None, final=None, writable=None, add=None, static=None, remove=None, sealed=None, mutable=None, gettable=None, inline=None, settable=None, id=None, templateparamlist=None, type_=None, definition='', argsstring='', name='', read='', write='', bitfield='', reimplements=None, reimplementedby=None, param=None, enumvalue=None, initializer=None, exceptions=None, briefdescription=None, detaileddescription=None, inbodydescription=None, location=None, references=None, referencedby=None):
supermod.memberdefType.__init__(self, initonly, kind, volatile, const, raise_, virt, readable, prot, explicit, new, final, writable, add, static, remove, sealed, mutable, gettable, inline, settable, id, templateparamlist, type_, definition, argsstring, name, read, write, bitfield, reimplements, reimplementedby, param, enumvalue, initializer, exceptions, briefdescription, detaileddescription, inbodydescription, location, references, referencedby)
supermod.memberdefType.subclass = memberdefTypeSub
# end class memberdefTypeSub
class descriptionTypeSub(supermod.descriptionType):
def __init__(self, title='', para=None, sect1=None, internal=None, mixedclass_=None, content_=None):
supermod.descriptionType.__init__(self, mixedclass_, content_)
supermod.descriptionType.subclass = descriptionTypeSub
# end class descriptionTypeSub
class enumvalueTypeSub(supermod.enumvalueType):
def __init__(self, prot=None, id=None, name='', initializer=None, briefdescription=None, detaileddescription=None, mixedclass_=None, content_=None):
supermod.enumvalueType.__init__(self, mixedclass_, content_)
supermod.enumvalueType.subclass = enumvalueTypeSub
# end class enumvalueTypeSub
class templateparamlistTypeSub(supermod.templateparamlistType):
def __init__(self, param=None):
supermod.templateparamlistType.__init__(self, param)
supermod.templateparamlistType.subclass = templateparamlistTypeSub
# end class templateparamlistTypeSub
class paramTypeSub(supermod.paramType):
def __init__(self, type_=None, declname='', defname='', array='', defval=None, briefdescription=None):
supermod.paramType.__init__(self, type_, declname, defname, array, defval, briefdescription)
supermod.paramType.subclass = paramTypeSub
# end class paramTypeSub
class linkedTextTypeSub(supermod.linkedTextType):
def __init__(self, ref=None, mixedclass_=None, content_=None):
supermod.linkedTextType.__init__(self, mixedclass_, content_)
supermod.linkedTextType.subclass = linkedTextTypeSub
# end class linkedTextTypeSub
class graphTypeSub(supermod.graphType):
def __init__(self, node=None):
supermod.graphType.__init__(self, node)
supermod.graphType.subclass = graphTypeSub
# end class graphTypeSub
class nodeTypeSub(supermod.nodeType):
def __init__(self, id=None, label='', link=None, childnode=None):
supermod.nodeType.__init__(self, id, label, link, childnode)
supermod.nodeType.subclass = nodeTypeSub
# end class nodeTypeSub
class childnodeTypeSub(supermod.childnodeType):
def __init__(self, relation=None, refid=None, edgelabel=None):
supermod.childnodeType.__init__(self, relation, refid, edgelabel)
supermod.childnodeType.subclass = childnodeTypeSub
# end class childnodeTypeSub
class linkTypeSub(supermod.linkType):
def __init__(self, refid=None, external=None, valueOf_=''):
supermod.linkType.__init__(self, refid, external)
supermod.linkType.subclass = linkTypeSub
# end class linkTypeSub
class listingTypeSub(supermod.listingType):
def __init__(self, codeline=None):
supermod.listingType.__init__(self, codeline)
supermod.listingType.subclass = listingTypeSub
# end class listingTypeSub
class codelineTypeSub(supermod.codelineType):
def __init__(self, external=None, lineno=None, refkind=None, refid=None, highlight=None):
supermod.codelineType.__init__(self, external, lineno, refkind, refid, highlight)
supermod.codelineType.subclass = codelineTypeSub
# end class codelineTypeSub
class highlightTypeSub(supermod.highlightType):
def __init__(self, class_=None, sp=None, ref=None, mixedclass_=None, content_=None):
supermod.highlightType.__init__(self, mixedclass_, content_)
supermod.highlightType.subclass = highlightTypeSub
# end class highlightTypeSub
class referenceTypeSub(supermod.referenceType):
def __init__(self, endline=None, startline=None, refid=None, compoundref=None, valueOf_='', mixedclass_=None, content_=None):
supermod.referenceType.__init__(self, mixedclass_, content_)
supermod.referenceType.subclass = referenceTypeSub
# end class referenceTypeSub
class locationTypeSub(supermod.locationType):
def __init__(self, bodystart=None, line=None, bodyend=None, bodyfile=None, file=None, valueOf_=''):
supermod.locationType.__init__(self, bodystart, line, bodyend, bodyfile, file)
supermod.locationType.subclass = locationTypeSub
# end class locationTypeSub
class docSect1TypeSub(supermod.docSect1Type):
def __init__(self, id=None, title='', para=None, sect2=None, internal=None, mixedclass_=None, content_=None):
supermod.docSect1Type.__init__(self, mixedclass_, content_)
supermod.docSect1Type.subclass = docSect1TypeSub
# end class docSect1TypeSub
class docSect2TypeSub(supermod.docSect2Type):
def __init__(self, id=None, title='', para=None, sect3=None, internal=None, mixedclass_=None, content_=None):
supermod.docSect2Type.__init__(self, mixedclass_, content_)
supermod.docSect2Type.subclass = docSect2TypeSub
# end class docSect2TypeSub
class docSect3TypeSub(supermod.docSect3Type):
def __init__(self, id=None, title='', para=None, sect4=None, internal=None, mixedclass_=None, content_=None):
supermod.docSect3Type.__init__(self, mixedclass_, content_)
supermod.docSect3Type.subclass = docSect3TypeSub
# end class docSect3TypeSub
class docSect4TypeSub(supermod.docSect4Type):
def __init__(self, id=None, title='', para=None, internal=None, mixedclass_=None, content_=None):
supermod.docSect4Type.__init__(self, mixedclass_, content_)
supermod.docSect4Type.subclass = docSect4TypeSub
# end class docSect4TypeSub
class docInternalTypeSub(supermod.docInternalType):
def __init__(self, para=None, sect1=None, mixedclass_=None, content_=None):
supermod.docInternalType.__init__(self, mixedclass_, content_)
supermod.docInternalType.subclass = docInternalTypeSub
# end class docInternalTypeSub
class docInternalS1TypeSub(supermod.docInternalS1Type):
def __init__(self, para=None, sect2=None, mixedclass_=None, content_=None):
supermod.docInternalS1Type.__init__(self, mixedclass_, content_)
supermod.docInternalS1Type.subclass = docInternalS1TypeSub
# end class docInternalS1TypeSub
class docInternalS2TypeSub(supermod.docInternalS2Type):
def __init__(self, para=None, sect3=None, mixedclass_=None, content_=None):
supermod.docInternalS2Type.__init__(self, mixedclass_, content_)
supermod.docInternalS2Type.subclass = docInternalS2TypeSub
# end class docInternalS2TypeSub
class docInternalS3TypeSub(supermod.docInternalS3Type):
def __init__(self, para=None, sect3=None, mixedclass_=None, content_=None):
supermod.docInternalS3Type.__init__(self, mixedclass_, content_)
supermod.docInternalS3Type.subclass = docInternalS3TypeSub
# end class docInternalS3TypeSub
class docInternalS4TypeSub(supermod.docInternalS4Type):
def __init__(self, para=None, mixedclass_=None, content_=None):
supermod.docInternalS4Type.__init__(self, mixedclass_, content_)
supermod.docInternalS4Type.subclass = docInternalS4TypeSub
# end class docInternalS4TypeSub
class docURLLinkSub(supermod.docURLLink):
def __init__(self, url=None, valueOf_='', mixedclass_=None, content_=None):
supermod.docURLLink.__init__(self, mixedclass_, content_)
supermod.docURLLink.subclass = docURLLinkSub
# end class docURLLinkSub
class docAnchorTypeSub(supermod.docAnchorType):
def __init__(self, id=None, valueOf_='', mixedclass_=None, content_=None):
supermod.docAnchorType.__init__(self, mixedclass_, content_)
supermod.docAnchorType.subclass = docAnchorTypeSub
# end class docAnchorTypeSub
class docFormulaTypeSub(supermod.docFormulaType):
def __init__(self, id=None, valueOf_='', mixedclass_=None, content_=None):
supermod.docFormulaType.__init__(self, mixedclass_, content_)
supermod.docFormulaType.subclass = docFormulaTypeSub
# end class docFormulaTypeSub
class docIndexEntryTypeSub(supermod.docIndexEntryType):
def __init__(self, primaryie='', secondaryie=''):
supermod.docIndexEntryType.__init__(self, primaryie, secondaryie)
supermod.docIndexEntryType.subclass = docIndexEntryTypeSub
# end class docIndexEntryTypeSub
class docListTypeSub(supermod.docListType):
def __init__(self, listitem=None):
supermod.docListType.__init__(self, listitem)
supermod.docListType.subclass = docListTypeSub
# end class docListTypeSub
class docListItemTypeSub(supermod.docListItemType):
def __init__(self, para=None):
supermod.docListItemType.__init__(self, para)
supermod.docListItemType.subclass = docListItemTypeSub
# end class docListItemTypeSub
class docSimpleSectTypeSub(supermod.docSimpleSectType):
def __init__(self, kind=None, title=None, para=None):
supermod.docSimpleSectType.__init__(self, kind, title, para)
supermod.docSimpleSectType.subclass = docSimpleSectTypeSub
# end class docSimpleSectTypeSub
class docVarListEntryTypeSub(supermod.docVarListEntryType):
def __init__(self, term=None):
supermod.docVarListEntryType.__init__(self, term)
supermod.docVarListEntryType.subclass = docVarListEntryTypeSub
# end class docVarListEntryTypeSub
class docRefTextTypeSub(supermod.docRefTextType):
def __init__(self, refid=None, kindref=None, external=None, valueOf_='', mixedclass_=None, content_=None):
supermod.docRefTextType.__init__(self, mixedclass_, content_)
supermod.docRefTextType.subclass = docRefTextTypeSub
# end class docRefTextTypeSub
class docTableTypeSub(supermod.docTableType):
def __init__(self, rows=None, cols=None, row=None, caption=None):
supermod.docTableType.__init__(self, rows, cols, row, caption)
supermod.docTableType.subclass = docTableTypeSub
# end class docTableTypeSub
class docRowTypeSub(supermod.docRowType):
def __init__(self, entry=None):
supermod.docRowType.__init__(self, entry)
supermod.docRowType.subclass = docRowTypeSub
# end class docRowTypeSub
class docEntryTypeSub(supermod.docEntryType):
def __init__(self, thead=None, para=None):
supermod.docEntryType.__init__(self, thead, para)
supermod.docEntryType.subclass = docEntryTypeSub
# end class docEntryTypeSub
class docHeadingTypeSub(supermod.docHeadingType):
def __init__(self, level=None, valueOf_='', mixedclass_=None, content_=None):
supermod.docHeadingType.__init__(self, mixedclass_, content_)
supermod.docHeadingType.subclass = docHeadingTypeSub
# end class docHeadingTypeSub
class docImageTypeSub(supermod.docImageType):
def __init__(self, width=None, type_=None, name=None, height=None, valueOf_='', mixedclass_=None, content_=None):
supermod.docImageType.__init__(self, mixedclass_, content_)
supermod.docImageType.subclass = docImageTypeSub
# end class docImageTypeSub
class docDotFileTypeSub(supermod.docDotFileType):
def __init__(self, name=None, valueOf_='', mixedclass_=None, content_=None):
supermod.docDotFileType.__init__(self, mixedclass_, content_)
supermod.docDotFileType.subclass = docDotFileTypeSub
# end class docDotFileTypeSub
class docTocItemTypeSub(supermod.docTocItemType):
def __init__(self, id=None, valueOf_='', mixedclass_=None, content_=None):
supermod.docTocItemType.__init__(self, mixedclass_, content_)
supermod.docTocItemType.subclass = docTocItemTypeSub
# end class docTocItemTypeSub
class docTocListTypeSub(supermod.docTocListType):
def __init__(self, tocitem=None):
supermod.docTocListType.__init__(self, tocitem)
supermod.docTocListType.subclass = docTocListTypeSub
# end class docTocListTypeSub
class docLanguageTypeSub(supermod.docLanguageType):
def __init__(self, langid=None, para=None):
supermod.docLanguageType.__init__(self, langid, para)
supermod.docLanguageType.subclass = docLanguageTypeSub
# end class docLanguageTypeSub
class docParamListTypeSub(supermod.docParamListType):
def __init__(self, kind=None, parameteritem=None):
supermod.docParamListType.__init__(self, kind, parameteritem)
supermod.docParamListType.subclass = docParamListTypeSub
# end class docParamListTypeSub
class docParamListItemSub(supermod.docParamListItem):
def __init__(self, parameternamelist=None, parameterdescription=None):
supermod.docParamListItem.__init__(self, parameternamelist, parameterdescription)
supermod.docParamListItem.subclass = docParamListItemSub
# end class docParamListItemSub
class docParamNameListSub(supermod.docParamNameList):
def __init__(self, parametername=None):
supermod.docParamNameList.__init__(self, parametername)
supermod.docParamNameList.subclass = docParamNameListSub
# end class docParamNameListSub
class docParamNameSub(supermod.docParamName):
def __init__(self, direction=None, ref=None, mixedclass_=None, content_=None):
supermod.docParamName.__init__(self, mixedclass_, content_)
supermod.docParamName.subclass = docParamNameSub
# end class docParamNameSub
class docXRefSectTypeSub(supermod.docXRefSectType):
def __init__(self, id=None, xreftitle=None, xrefdescription=None):
supermod.docXRefSectType.__init__(self, id, xreftitle, xrefdescription)
supermod.docXRefSectType.subclass = docXRefSectTypeSub
# end class docXRefSectTypeSub
class docCopyTypeSub(supermod.docCopyType):
def __init__(self, link=None, para=None, sect1=None, internal=None):
supermod.docCopyType.__init__(self, link, para, sect1, internal)
supermod.docCopyType.subclass = docCopyTypeSub
# end class docCopyTypeSub
class docCharTypeSub(supermod.docCharType):
def __init__(self, char=None, valueOf_=''):
supermod.docCharType.__init__(self, char)
supermod.docCharType.subclass = docCharTypeSub
# end class docCharTypeSub
class docParaTypeSub(supermod.docParaType):
def __init__(self, char=None, valueOf_=''):
supermod.docParaType.__init__(self, char)
self.parameterlist = []
self.simplesects = []
self.content = []
def buildChildren(self, child_, nodeName_):
supermod.docParaType.buildChildren(self, child_, nodeName_)
if child_.nodeType == Node.TEXT_NODE:
obj_ = self.mixedclass_(MixedContainer.CategoryText,
MixedContainer.TypeNone, '', child_.nodeValue)
self.content.append(obj_)
elif child_.nodeType == Node.ELEMENT_NODE and \
nodeName_ == "ref":
obj_ = supermod.docRefTextType.factory()
obj_.build(child_)
self.content.append(obj_)
elif child_.nodeType == Node.ELEMENT_NODE and \
nodeName_ == 'parameterlist':
obj_ = supermod.docParamListType.factory()
obj_.build(child_)
self.parameterlist.append(obj_)
elif child_.nodeType == Node.ELEMENT_NODE and \
nodeName_ == 'simplesect':
obj_ = supermod.docSimpleSectType.factory()
obj_.build(child_)
self.simplesects.append(obj_)
supermod.docParaType.subclass = docParaTypeSub
# end class docParaTypeSub
def parse(inFilename):
doc = minidom.parse(inFilename)
rootNode = doc.documentElement
rootObj = supermod.DoxygenType.factory()
rootObj.build(rootNode)
return rootObj

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
"""
Generated Mon Feb 9 19:08:05 2009 by generateDS.py.
"""
from xml.dom import minidom
import os
import sys
import compound
import indexsuper as supermod
class DoxygenTypeSub(supermod.DoxygenType):
def __init__(self, version=None, compound=None):
supermod.DoxygenType.__init__(self, version, compound)
def find_compounds_and_members(self, details):
"""
Returns a list of all compounds and their members which match details
"""
results = []
for compound in self.compound:
members = compound.find_members(details)
if members:
results.append([compound, members])
else:
if details.match(compound):
results.append([compound, []])
return results
supermod.DoxygenType.subclass = DoxygenTypeSub
# end class DoxygenTypeSub
class CompoundTypeSub(supermod.CompoundType):
def __init__(self, kind=None, refid=None, name='', member=None):
supermod.CompoundType.__init__(self, kind, refid, name, member)
def find_members(self, details):
"""
Returns a list of all members which match details
"""
results = []
for member in self.member:
if details.match(member):
results.append(member)
return results
supermod.CompoundType.subclass = CompoundTypeSub
# end class CompoundTypeSub
class MemberTypeSub(supermod.MemberType):
def __init__(self, kind=None, refid=None, name=''):
supermod.MemberType.__init__(self, kind, refid, name)
supermod.MemberType.subclass = MemberTypeSub
# end class MemberTypeSub
def parse(inFilename):
doc = minidom.parse(inFilename)
rootNode = doc.documentElement
rootObj = supermod.DoxygenType.factory()
rootObj.build(rootNode)
return rootObj

View File

@ -0,0 +1,523 @@
#!/usr/bin/env python
#
# Generated Thu Jun 11 18:43:54 2009 by generateDS.py.
#
import sys
import getopt
from string import lower as str_lower
from xml.dom import minidom
from xml.dom import Node
#
# User methods
#
# Calls to the methods in these classes are generated by generateDS.py.
# You can replace these methods by re-implementing the following class
# in a module named generatedssuper.py.
try:
from generatedssuper import GeneratedsSuper
except ImportError, exp:
class GeneratedsSuper:
def format_string(self, input_data, input_name=''):
return input_data
def format_integer(self, input_data, input_name=''):
return '%d' % input_data
def format_float(self, input_data, input_name=''):
return '%f' % input_data
def format_double(self, input_data, input_name=''):
return '%e' % input_data
def format_boolean(self, input_data, input_name=''):
return '%s' % input_data
#
# If you have installed IPython you can uncomment and use the following.
# IPython is available from http://ipython.scipy.org/.
#
## from IPython.Shell import IPShellEmbed
## args = ''
## ipshell = IPShellEmbed(args,
## banner = 'Dropping into IPython',
## exit_msg = 'Leaving Interpreter, back to program.')
# Then use the following line where and when you want to drop into the
# IPython shell:
# ipshell('<some message> -- Entering ipshell.\nHit Ctrl-D to exit')
#
# Globals
#
ExternalEncoding = 'ascii'
#
# Support/utility functions.
#
def showIndent(outfile, level):
for idx in range(level):
outfile.write(' ')
def quote_xml(inStr):
s1 = (isinstance(inStr, basestring) and inStr or
'%s' % inStr)
s1 = s1.replace('&', '&amp;')
s1 = s1.replace('<', '&lt;')
s1 = s1.replace('>', '&gt;')
return s1
def quote_attrib(inStr):
s1 = (isinstance(inStr, basestring) and inStr or
'%s' % inStr)
s1 = s1.replace('&', '&amp;')
s1 = s1.replace('<', '&lt;')
s1 = s1.replace('>', '&gt;')
if '"' in s1:
if "'" in s1:
s1 = '"%s"' % s1.replace('"', "&quot;")
else:
s1 = "'%s'" % s1
else:
s1 = '"%s"' % s1
return s1
def quote_python(inStr):
s1 = inStr
if s1.find("'") == -1:
if s1.find('\n') == -1:
return "'%s'" % s1
else:
return "'''%s'''" % s1
else:
if s1.find('"') != -1:
s1 = s1.replace('"', '\\"')
if s1.find('\n') == -1:
return '"%s"' % s1
else:
return '"""%s"""' % s1
class MixedContainer:
# Constants for category:
CategoryNone = 0
CategoryText = 1
CategorySimple = 2
CategoryComplex = 3
# Constants for content_type:
TypeNone = 0
TypeText = 1
TypeString = 2
TypeInteger = 3
TypeFloat = 4
TypeDecimal = 5
TypeDouble = 6
TypeBoolean = 7
def __init__(self, category, content_type, name, value):
self.category = category
self.content_type = content_type
self.name = name
self.value = value
def getCategory(self):
return self.category
def getContenttype(self, content_type):
return self.content_type
def getValue(self):
return self.value
def getName(self):
return self.name
def export(self, outfile, level, name, namespace):
if self.category == MixedContainer.CategoryText:
outfile.write(self.value)
elif self.category == MixedContainer.CategorySimple:
self.exportSimple(outfile, level, name)
else: # category == MixedContainer.CategoryComplex
self.value.export(outfile, level, namespace,name)
def exportSimple(self, outfile, level, name):
if self.content_type == MixedContainer.TypeString:
outfile.write('<%s>%s</%s>' % (self.name, self.value, self.name))
elif self.content_type == MixedContainer.TypeInteger or \
self.content_type == MixedContainer.TypeBoolean:
outfile.write('<%s>%d</%s>' % (self.name, self.value, self.name))
elif self.content_type == MixedContainer.TypeFloat or \
self.content_type == MixedContainer.TypeDecimal:
outfile.write('<%s>%f</%s>' % (self.name, self.value, self.name))
elif self.content_type == MixedContainer.TypeDouble:
outfile.write('<%s>%g</%s>' % (self.name, self.value, self.name))
def exportLiteral(self, outfile, level, name):
if self.category == MixedContainer.CategoryText:
showIndent(outfile, level)
outfile.write('MixedContainer(%d, %d, "%s", "%s"),\n' % \
(self.category, self.content_type, self.name, self.value))
elif self.category == MixedContainer.CategorySimple:
showIndent(outfile, level)
outfile.write('MixedContainer(%d, %d, "%s", "%s"),\n' % \
(self.category, self.content_type, self.name, self.value))
else: # category == MixedContainer.CategoryComplex
showIndent(outfile, level)
outfile.write('MixedContainer(%d, %d, "%s",\n' % \
(self.category, self.content_type, self.name,))
self.value.exportLiteral(outfile, level + 1)
showIndent(outfile, level)
outfile.write(')\n')
class _MemberSpec(object):
def __init__(self, name='', data_type='', container=0):
self.name = name
self.data_type = data_type
self.container = container
def set_name(self, name): self.name = name
def get_name(self): return self.name
def set_data_type(self, data_type): self.data_type = data_type
def get_data_type(self): return self.data_type
def set_container(self, container): self.container = container
def get_container(self): return self.container
#
# Data representation classes.
#
class DoxygenType(GeneratedsSuper):
subclass = None
superclass = None
def __init__(self, version=None, compound=None):
self.version = version
if compound is None:
self.compound = []
else:
self.compound = compound
def factory(*args_, **kwargs_):
if DoxygenType.subclass:
return DoxygenType.subclass(*args_, **kwargs_)
else:
return DoxygenType(*args_, **kwargs_)
factory = staticmethod(factory)
def get_compound(self): return self.compound
def set_compound(self, compound): self.compound = compound
def add_compound(self, value): self.compound.append(value)
def insert_compound(self, index, value): self.compound[index] = value
def get_version(self): return self.version
def set_version(self, version): self.version = version
def export(self, outfile, level, namespace_='', name_='DoxygenType', namespacedef_=''):
showIndent(outfile, level)
outfile.write('<%s%s %s' % (namespace_, name_, namespacedef_, ))
self.exportAttributes(outfile, level, namespace_, name_='DoxygenType')
if self.hasContent_():
outfile.write('>\n')
self.exportChildren(outfile, level + 1, namespace_, name_)
showIndent(outfile, level)
outfile.write('</%s%s>\n' % (namespace_, name_))
else:
outfile.write(' />\n')
def exportAttributes(self, outfile, level, namespace_='', name_='DoxygenType'):
outfile.write(' version=%s' % (self.format_string(quote_attrib(self.version).encode(ExternalEncoding), input_name='version'), ))
def exportChildren(self, outfile, level, namespace_='', name_='DoxygenType'):
for compound_ in self.compound:
compound_.export(outfile, level, namespace_, name_='compound')
def hasContent_(self):
if (
self.compound is not None
):
return True
else:
return False
def exportLiteral(self, outfile, level, name_='DoxygenType'):
level += 1
self.exportLiteralAttributes(outfile, level, name_)
if self.hasContent_():
self.exportLiteralChildren(outfile, level, name_)
def exportLiteralAttributes(self, outfile, level, name_):
if self.version is not None:
showIndent(outfile, level)
outfile.write('version = %s,\n' % (self.version,))
def exportLiteralChildren(self, outfile, level, name_):
showIndent(outfile, level)
outfile.write('compound=[\n')
level += 1
for compound in self.compound:
showIndent(outfile, level)
outfile.write('model_.compound(\n')
compound.exportLiteral(outfile, level, name_='compound')
showIndent(outfile, level)
outfile.write('),\n')
level -= 1
showIndent(outfile, level)
outfile.write('],\n')
def build(self, node_):
attrs = node_.attributes
self.buildAttributes(attrs)
for child_ in node_.childNodes:
nodeName_ = child_.nodeName.split(':')[-1]
self.buildChildren(child_, nodeName_)
def buildAttributes(self, attrs):
if attrs.get('version'):
self.version = attrs.get('version').value
def buildChildren(self, child_, nodeName_):
if child_.nodeType == Node.ELEMENT_NODE and \
nodeName_ == 'compound':
obj_ = CompoundType.factory()
obj_.build(child_)
self.compound.append(obj_)
# end class DoxygenType
class CompoundType(GeneratedsSuper):
subclass = None
superclass = None
def __init__(self, kind=None, refid=None, name=None, member=None):
self.kind = kind
self.refid = refid
self.name = name
if member is None:
self.member = []
else:
self.member = member
def factory(*args_, **kwargs_):
if CompoundType.subclass:
return CompoundType.subclass(*args_, **kwargs_)
else:
return CompoundType(*args_, **kwargs_)
factory = staticmethod(factory)
def get_name(self): return self.name
def set_name(self, name): self.name = name
def get_member(self): return self.member
def set_member(self, member): self.member = member
def add_member(self, value): self.member.append(value)
def insert_member(self, index, value): self.member[index] = value
def get_kind(self): return self.kind
def set_kind(self, kind): self.kind = kind
def get_refid(self): return self.refid
def set_refid(self, refid): self.refid = refid
def export(self, outfile, level, namespace_='', name_='CompoundType', namespacedef_=''):
showIndent(outfile, level)
outfile.write('<%s%s %s' % (namespace_, name_, namespacedef_, ))
self.exportAttributes(outfile, level, namespace_, name_='CompoundType')
if self.hasContent_():
outfile.write('>\n')
self.exportChildren(outfile, level + 1, namespace_, name_)
showIndent(outfile, level)
outfile.write('</%s%s>\n' % (namespace_, name_))
else:
outfile.write(' />\n')
def exportAttributes(self, outfile, level, namespace_='', name_='CompoundType'):
outfile.write(' kind=%s' % (quote_attrib(self.kind), ))
outfile.write(' refid=%s' % (self.format_string(quote_attrib(self.refid).encode(ExternalEncoding), input_name='refid'), ))
def exportChildren(self, outfile, level, namespace_='', name_='CompoundType'):
if self.name is not None:
showIndent(outfile, level)
outfile.write('<%sname>%s</%sname>\n' % (namespace_, self.format_string(quote_xml(self.name).encode(ExternalEncoding), input_name='name'), namespace_))
for member_ in self.member:
member_.export(outfile, level, namespace_, name_='member')
def hasContent_(self):
if (
self.name is not None or
self.member is not None
):
return True
else:
return False
def exportLiteral(self, outfile, level, name_='CompoundType'):
level += 1
self.exportLiteralAttributes(outfile, level, name_)
if self.hasContent_():
self.exportLiteralChildren(outfile, level, name_)
def exportLiteralAttributes(self, outfile, level, name_):
if self.kind is not None:
showIndent(outfile, level)
outfile.write('kind = "%s",\n' % (self.kind,))
if self.refid is not None:
showIndent(outfile, level)
outfile.write('refid = %s,\n' % (self.refid,))
def exportLiteralChildren(self, outfile, level, name_):
showIndent(outfile, level)
outfile.write('name=%s,\n' % quote_python(self.name).encode(ExternalEncoding))
showIndent(outfile, level)
outfile.write('member=[\n')
level += 1
for member in self.member:
showIndent(outfile, level)
outfile.write('model_.member(\n')
member.exportLiteral(outfile, level, name_='member')
showIndent(outfile, level)
outfile.write('),\n')
level -= 1
showIndent(outfile, level)
outfile.write('],\n')
def build(self, node_):
attrs = node_.attributes
self.buildAttributes(attrs)
for child_ in node_.childNodes:
nodeName_ = child_.nodeName.split(':')[-1]
self.buildChildren(child_, nodeName_)
def buildAttributes(self, attrs):
if attrs.get('kind'):
self.kind = attrs.get('kind').value
if attrs.get('refid'):
self.refid = attrs.get('refid').value
def buildChildren(self, child_, nodeName_):
if child_.nodeType == Node.ELEMENT_NODE and \
nodeName_ == 'name':
name_ = ''
for text__content_ in child_.childNodes:
name_ += text__content_.nodeValue
self.name = name_
elif child_.nodeType == Node.ELEMENT_NODE and \
nodeName_ == 'member':
obj_ = MemberType.factory()
obj_.build(child_)
self.member.append(obj_)
# end class CompoundType
class MemberType(GeneratedsSuper):
subclass = None
superclass = None
def __init__(self, kind=None, refid=None, name=None):
self.kind = kind
self.refid = refid
self.name = name
def factory(*args_, **kwargs_):
if MemberType.subclass:
return MemberType.subclass(*args_, **kwargs_)
else:
return MemberType(*args_, **kwargs_)
factory = staticmethod(factory)
def get_name(self): return self.name
def set_name(self, name): self.name = name
def get_kind(self): return self.kind
def set_kind(self, kind): self.kind = kind
def get_refid(self): return self.refid
def set_refid(self, refid): self.refid = refid
def export(self, outfile, level, namespace_='', name_='MemberType', namespacedef_=''):
showIndent(outfile, level)
outfile.write('<%s%s %s' % (namespace_, name_, namespacedef_, ))
self.exportAttributes(outfile, level, namespace_, name_='MemberType')
if self.hasContent_():
outfile.write('>\n')
self.exportChildren(outfile, level + 1, namespace_, name_)
showIndent(outfile, level)
outfile.write('</%s%s>\n' % (namespace_, name_))
else:
outfile.write(' />\n')
def exportAttributes(self, outfile, level, namespace_='', name_='MemberType'):
outfile.write(' kind=%s' % (quote_attrib(self.kind), ))
outfile.write(' refid=%s' % (self.format_string(quote_attrib(self.refid).encode(ExternalEncoding), input_name='refid'), ))
def exportChildren(self, outfile, level, namespace_='', name_='MemberType'):
if self.name is not None:
showIndent(outfile, level)
outfile.write('<%sname>%s</%sname>\n' % (namespace_, self.format_string(quote_xml(self.name).encode(ExternalEncoding), input_name='name'), namespace_))
def hasContent_(self):
if (
self.name is not None
):
return True
else:
return False
def exportLiteral(self, outfile, level, name_='MemberType'):
level += 1
self.exportLiteralAttributes(outfile, level, name_)
if self.hasContent_():
self.exportLiteralChildren(outfile, level, name_)
def exportLiteralAttributes(self, outfile, level, name_):
if self.kind is not None:
showIndent(outfile, level)
outfile.write('kind = "%s",\n' % (self.kind,))
if self.refid is not None:
showIndent(outfile, level)
outfile.write('refid = %s,\n' % (self.refid,))
def exportLiteralChildren(self, outfile, level, name_):
showIndent(outfile, level)
outfile.write('name=%s,\n' % quote_python(self.name).encode(ExternalEncoding))
def build(self, node_):
attrs = node_.attributes
self.buildAttributes(attrs)
for child_ in node_.childNodes:
nodeName_ = child_.nodeName.split(':')[-1]
self.buildChildren(child_, nodeName_)
def buildAttributes(self, attrs):
if attrs.get('kind'):
self.kind = attrs.get('kind').value
if attrs.get('refid'):
self.refid = attrs.get('refid').value
def buildChildren(self, child_, nodeName_):
if child_.nodeType == Node.ELEMENT_NODE and \
nodeName_ == 'name':
name_ = ''
for text__content_ in child_.childNodes:
name_ += text__content_.nodeValue
self.name = name_
# end class MemberType
USAGE_TEXT = """
Usage: python <Parser>.py [ -s ] <in_xml_file>
Options:
-s Use the SAX parser, not the minidom parser.
"""
def usage():
print USAGE_TEXT
sys.exit(1)
def parse(inFileName):
doc = minidom.parse(inFileName)
rootNode = doc.documentElement
rootObj = DoxygenType.factory()
rootObj.build(rootNode)
# Enable Python to collect the space used by the DOM.
doc = None
sys.stdout.write('<?xml version="1.0" ?>\n')
rootObj.export(sys.stdout, 0, name_="doxygenindex",
namespacedef_='')
return rootObj
def parseString(inString):
doc = minidom.parseString(inString)
rootNode = doc.documentElement
rootObj = DoxygenType.factory()
rootObj.build(rootNode)
# Enable Python to collect the space used by the DOM.
doc = None
sys.stdout.write('<?xml version="1.0" ?>\n')
rootObj.export(sys.stdout, 0, name_="doxygenindex",
namespacedef_='')
return rootObj
def parseLiteral(inFileName):
doc = minidom.parse(inFileName)
rootNode = doc.documentElement
rootObj = DoxygenType.factory()
rootObj.build(rootNode)
# Enable Python to collect the space used by the DOM.
doc = None
sys.stdout.write('from index import *\n\n')
sys.stdout.write('rootObj = doxygenindex(\n')
rootObj.exportLiteral(sys.stdout, 0, name_="doxygenindex")
sys.stdout.write(')\n')
return rootObj
def main():
args = sys.argv[1:]
if len(args) == 1:
parse(args[0])
else:
usage()
if __name__ == '__main__':
main()
#import pdb
#pdb.run('main()')

View File

@ -0,0 +1,56 @@
#
# Copyright 2010 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.
#
"""
Utilities for extracting text from generated classes.
"""
def is_string(txt):
if isinstance(txt, str):
return True
try:
if isinstance(txt, unicode):
return True
except NameError:
pass
return False
def description(obj):
if obj is None:
return None
return description_bit(obj).strip()
def description_bit(obj):
if hasattr(obj, 'content'):
contents = [description_bit(item) for item in obj.content]
result = ''.join(contents)
elif hasattr(obj, 'content_'):
contents = [description_bit(item) for item in obj.content_]
result = ''.join(contents)
elif hasattr(obj, 'value'):
result = description_bit(obj.value)
elif is_string(obj):
return obj
else:
raise StandardError('Expecting a string or something with content, content_ or value attribute')
# If this bit is a paragraph then add one some line breaks.
if hasattr(obj, 'name') and obj.name == 'para':
result += "\n\n"
return result

View File

@ -0,0 +1,7 @@
/*!
* \defgroup block GNU Radio GSM C++ Signal Processing Blocks
* \brief All C++ blocks that can be used from the GSM GNU Radio
* module are listed here or in the subcategories below.
*
*/

View File

@ -0,0 +1,10 @@
/*! \mainpage
Welcome to the GNU Radio GSM Block
This is the intro page for the Doxygen manual generated for the GSM
block (docs/doxygen/other/main_page.dox). Edit it to add more detailed
documentation about the new GNU Radio modules contained in this
project.
*/

255
docs/doxygen/swig_doc.py Normal file
View File

@ -0,0 +1,255 @@
#
# Copyright 2010,2011 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.
#
"""
Creates the swig_doc.i SWIG interface file.
Execute using: python swig_doc.py xml_path outputfilename
The file instructs SWIG to transfer the doxygen comments into the
python docstrings.
"""
import sys
try:
from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base
except ImportError:
from gnuradio.doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base
def py_name(name):
bits = name.split('_')
return '_'.join(bits[1:])
def make_name(name):
bits = name.split('_')
return bits[0] + '_make_' + '_'.join(bits[1:])
class Block(object):
"""
Checks if doxyxml produced objects correspond to a gnuradio block.
"""
@classmethod
def includes(cls, item):
if not isinstance(item, DoxyClass):
return False
# Check for a parsing error.
if item.error():
return False
return item.has_member(make_name(item.name()), DoxyFriend)
def utoascii(text):
"""
Convert unicode text into ascii and escape quotes.
"""
if text is None:
return ''
out = text.encode('ascii', 'replace')
out = out.replace('"', '\\"')
return out
def combine_descriptions(obj):
"""
Combines the brief and detailed descriptions of an object together.
"""
description = []
bd = obj.brief_description.strip()
dd = obj.detailed_description.strip()
if bd:
description.append(bd)
if dd:
description.append(dd)
return utoascii('\n\n'.join(description)).strip()
entry_templ = '%feature("docstring") {name} "{docstring}"'
def make_entry(obj, name=None, templ="{description}", description=None):
"""
Create a docstring entry for a swig interface file.
obj - a doxyxml object from which documentation will be extracted.
name - the name of the C object (defaults to obj.name())
templ - an optional template for the docstring containing only one
variable named 'description'.
description - if this optional variable is set then it's value is
used as the description instead of extracting it from obj.
"""
if name is None:
name=obj.name()
if "operator " in name:
return ''
if description is None:
description = combine_descriptions(obj)
docstring = templ.format(description=description)
if not docstring:
return ''
return entry_templ.format(
name=name,
docstring=docstring,
)
def make_func_entry(func, name=None, description=None, params=None):
"""
Create a function docstring entry for a swig interface file.
func - a doxyxml object from which documentation will be extracted.
name - the name of the C object (defaults to func.name())
description - if this optional variable is set then it's value is
used as the description instead of extracting it from func.
params - a parameter list that overrides using func.params.
"""
if params is None:
params = func.params
params = [prm.declname for prm in params]
if params:
sig = "Params: (%s)" % ", ".join(params)
else:
sig = "Params: (NONE)"
templ = "{description}\n\n" + sig
return make_entry(func, name=name, templ=utoascii(templ),
description=description)
def make_class_entry(klass, description=None):
"""
Create a class docstring for a swig interface file.
"""
output = []
output.append(make_entry(klass, description=description))
for func in klass.in_category(DoxyFunction):
name = klass.name() + '::' + func.name()
output.append(make_func_entry(func, name=name))
return "\n\n".join(output)
def make_block_entry(di, block):
"""
Create class and function docstrings of a gnuradio block for a
swig interface file.
"""
descriptions = []
# Get the documentation associated with the class.
class_desc = combine_descriptions(block)
if class_desc:
descriptions.append(class_desc)
# Get the documentation associated with the make function
make_func = di.get_member(make_name(block.name()), DoxyFunction)
make_func_desc = combine_descriptions(make_func)
if make_func_desc:
descriptions.append(make_func_desc)
# Get the documentation associated with the file
try:
block_file = di.get_member(block.name() + ".h", DoxyFile)
file_desc = combine_descriptions(block_file)
if file_desc:
descriptions.append(file_desc)
except base.Base.NoSuchMember:
# Don't worry if we can't find a matching file.
pass
# And join them all together to make a super duper description.
super_description = "\n\n".join(descriptions)
# Associate the combined description with the class and
# the make function.
output = []
output.append(make_class_entry(block, description=super_description))
creator = block.get_member(block.name(), DoxyFunction)
output.append(make_func_entry(make_func, description=super_description,
params=creator.params))
return "\n\n".join(output)
def make_swig_interface_file(di, swigdocfilename, custom_output=None):
output = ["""
/*
* This file was automatically generated using swig_doc.py.
*
* Any changes to it will be lost next time it is regenerated.
*/
"""]
if custom_output is not None:
output.append(custom_output)
# Create docstrings for the blocks.
blocks = di.in_category(Block)
make_funcs = set([])
for block in blocks:
try:
make_func = di.get_member(make_name(block.name()), DoxyFunction)
make_funcs.add(make_func.name())
output.append(make_block_entry(di, block))
except block.ParsingError:
print('Parsing error for block %s' % block.name())
# Create docstrings for functions
# Don't include the make functions since they have already been dealt with.
funcs = [f for f in di.in_category(DoxyFunction) if f.name() not in make_funcs]
for f in funcs:
try:
output.append(make_func_entry(f))
except f.ParsingError:
print('Parsing error for function %s' % f.name())
# Create docstrings for classes
block_names = [block.name() for block in blocks]
klasses = [k for k in di.in_category(DoxyClass) if k.name() not in block_names]
for k in klasses:
try:
output.append(make_class_entry(k))
except k.ParsingError:
print('Parsing error for class %s' % k.name())
# Docstrings are not created for anything that is not a function or a class.
# If this excludes anything important please add it here.
output = "\n\n".join(output)
swig_doc = file(swigdocfilename, 'w')
swig_doc.write(output)
swig_doc.close()
if __name__ == "__main__":
# Parse command line options and set up doxyxml.
err_msg = "Execute using: python swig_doc.py xml_path outputfilename"
if len(sys.argv) != 3:
raise StandardError(err_msg)
xml_path = sys.argv[1]
swigdocfilename = sys.argv[2]
di = DoxyIndex(xml_path)
# gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined!
# This is presumably a bug in SWIG.
#msg_q = di.get_member(u'gr_msg_queue', DoxyClass)
#insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction)
#delete_head = msg_q.get_member(u'delete_head', DoxyFunction)
output = []
#output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail'))
#output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head'))
custom_output = "\n\n".join(output)
# Generate the docstrings interface file.
make_swig_interface_file(di, swigdocfilename, custom_output=custom_output)

4
examples/README Normal file
View File

@ -0,0 +1,4 @@
It is considered good practice to add examples in here to demonstrate the
functionality of your OOT module. Python scripts, GRC flow graphs or other
code can go here.

23
grc/CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
# Copyright 2011 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_receiver.xml
gsm_receiver_hier.xml DESTINATION share/gnuradio/grc/blocks
)

38
grc/gsm_receiver.xml Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0"?>
<block>
<name>receiver</name>
<key>gsm_receiver</key>
<category>gsm</category>
<import>import gsm</import>
<make>gsm.receiver($tuner, $osr)</make>
<!-- Make one 'param' node for every Parameter you want settable from the GUI.
Sub-nodes:
* name
* key (makes the value accessible as $keyname, e.g. in the make node)
* type -->
<param>
<name>...</name>
<key>...</key>
<type>...</type>
</param>
<!-- Make one 'sink' node per input. Sub-nodes:
* name (an identifier for the GUI)
* type
* vlen
* optional (set to 1 for optional inputs) -->
<sink>
<name>in</name>
<type><!-- e.g. int, float, complex, byte, short, xxx_vector, ...--></type>
</sink>
<!-- Make one 'source' node per output. Sub-nodes:
* name (an identifier for the GUI)
* type
* vlen
* optional (set to 1 for optional inputs) -->
<source>
<name>out</name>
<type><!-- e.g. int, float, complex, byte, short, xxx_vector, ...--></type>
</source>
</block>

32
grc/gsm_receiver_hier.xml Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0"?>
<block>
<name>GSM Receiver</name>
<key>gsm_receiver_hier</key>
<category>GSM</category>
<import>import gsm</import>
<make>gsm.receiver_hier($input_rate, $osr)</make>
<param>
<name>Input rate</name>
<key>input_rate</key>
<value>samp_rate</value>
<type>real</type>
</param>
<param>
<name>Oversampling ration</name>
<key>osr</key>
<value>4</value>
<type>int</type>
</param>
<sink>
<name>in</name>
<type>complex</type>
</sink>
<source>
<name>out</name>
<type>float</type>
<vlen>142</vlen>
</source>
</block>

View File

@ -0,0 +1,26 @@
# 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
api.h
receiver.h DESTINATION include/gsm
)

33
include/gsm/api.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright 2011 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.
*/
#ifndef INCLUDED_GSM_API_H
#define INCLUDED_GSM_API_H
#include <gnuradio/attributes.h>
#ifdef gnuradio_gsm_EXPORTS
# define GSM_API __GR_ATTR_EXPORT
#else
# define GSM_API __GR_ATTR_IMPORT
#endif
#endif /* INCLUDED_GSM_API_H */

57
include/gsm/receiver.h Normal file
View File

@ -0,0 +1,57 @@
/* -*- c++ -*- */
/*
* Copyright 2014 <+YOU OR YOUR COMPANY+>.
*
* This 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.
*
* This software 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 software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_GSM_RECEIVER_H
#define INCLUDED_GSM_RECEIVER_H
#include <gsm/api.h>
#include <gnuradio/block.h>
#include <gnuradio/feval.h>
namespace gr {
namespace gsm {
/*!
* \brief <+description of block+>
* \ingroup gsm
*
*/
class GSM_API receiver : virtual public block
{
public:
typedef boost::shared_ptr<receiver> sptr;
/*!
* \brief Return a shared_ptr to a new instance of gsm::receiver.
*
* To avoid accidental use of raw pointers, gsm::receiver's
* constructor is in a private implementation
* class. gsm::receiver::make is the public interface for
* creating new instances.
*/
static sptr make(feval_dd * tuner, int osr);
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_RECEIVER_H */

71
lib/CMakeLists.txt Normal file
View File

@ -0,0 +1,71 @@
# 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.
########################################################################
# Setup library
########################################################################
include(GrPlatform) #define LIB_SUFFIX
include_directories(${Boost_INCLUDE_DIR})
link_directories(${Boost_LIBRARY_DIRS})
list(APPEND gsm_sources
receiver_impl.cc
receiver_config.cc
viterbi_detector.cc
sch.c
)
add_library(gnuradio-gsm SHARED ${gsm_sources})
target_link_libraries(gnuradio-gsm ${Boost_LIBRARIES} ${GNURADIO_RUNTIME_LIBRARIES})
set_target_properties(gnuradio-gsm PROPERTIES DEFINE_SYMBOL "gnuradio_gsm_EXPORTS")
########################################################################
# Install built library files
########################################################################
install(TARGETS gnuradio-gsm
LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file
ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file
RUNTIME DESTINATION bin # .dll file
)
########################################################################
# Build and register unit test
########################################################################
include(GrTest)
include_directories(${CPPUNIT_INCLUDE_DIRS})
list(APPEND test_gsm_sources
${CMAKE_CURRENT_SOURCE_DIR}/test_gsm.cc
${CMAKE_CURRENT_SOURCE_DIR}/qa_gsm.cc
${CMAKE_CURRENT_SOURCE_DIR}/qa_receiver.cc
)
add_executable(test-gsm ${test_gsm_sources})
target_link_libraries(
test-gsm
${GNURADIO_RUNTIME_LIBRARIES}
${Boost_LIBRARIES}
${CPPUNIT_LIBRARIES}
gnuradio-gsm
)
GR_ADD_TEST(test_gsm test-gsm)

67
lib/assert.h Normal file
View File

@ -0,0 +1,67 @@
/*
* Copyright 2007 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ASSERT_H
#define ASSERT_H
#include "stdio.h"
#include <iostream>
//#define NDEBUG
/**@name Macros for standard messages. */
//@{
#define COUT(text) { std::cout << text << std::endl; }
#define CERR(text) { std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; }
#ifdef NDEBUG
#define DCOUT(text) {}
#define OBJDCOUT(text) {}
#else
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
#endif
//@}
/** This is thrown by assert() so that gdb can catch it. */
class assertion
{
public:
assertion() {
fprintf( stderr,"Try setting a breakpoint @ %s:%u.\n",__FILE__,__LINE__ );
return;
}
};
#ifdef NDEBUG
#define assert(EXPR) {};
#else
/** This replaces the regular assert() with a C++ exception. */
#include "stdio.h"
#define assert(E) { if (!(E)) { fprintf(stderr,"%s:%u failed assertion '%s'\n",__FILE__,__LINE__,#E); throw Assertion(); } }
#endif
#endif

154
lib/gsm_constants.h Normal file
View File

@ -0,0 +1,154 @@
#ifndef INCLUDED_GSM_CONSTANTS_H
#define INCLUDED_GSM_CONSTANTS_H
#define GSM_SYMBOL_RATE (1625000.0/6.0) //symbols per second
#define GSM_SYMBOL_PERIOD (1.0/GSM_SYMBOL_RATE) //seconds per symbol
//Burst timing
#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 DATA_BITS 57 //size of 1 data block in normal burst
#define STEALING_BIT 1
#define N_TRAIN_BITS 26
#define N_SYNC_BITS 64
#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
#define FCCH_BITS USEFUL_BITS
#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
#define SCH_DATA_LEN 39
#define TS_BITS (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS) //a full TS (156 bits)
#define TS_PER_FRAME 8
#define FRAME_BITS (TS_PER_FRAME * TS_BITS + 2) // 156.25 * 8
#define FCCH_POS TAIL_BITS
#define SYNC_POS 39
#define TRAIN_POS ( TAIL_BITS + (DATA_BITS+STEALING_BIT) + 5) //first 5 bits of a training sequence
//aren't used for channel impulse response estimation
#define TRAIN_BEGINNING 5
#define SAFETY_MARGIN 6 //
#define FCCH_HITS_NEEDED (USEFUL_BITS - 4)
#define FCCH_MAX_MISSES 1
#define FCCH_MAX_FREQ_OFFSET 100
#define CHAN_IMP_RESP_LENGTH 5
#define MAX_SCH_ERRORS 5 //maximum number of subsequent sch errors after which gsm receiver goes to find_next_fcch state
typedef enum {empty, fcch_burst, sch_burst, normal_burst, rach_burst, dummy, dummy_or_normal} burst_type;
typedef enum {unknown, multiframe_26, multiframe_51} multiframe_type;
static const unsigned char SYNC_BITS[] = {
1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
};
const unsigned FCCH_FRAMES[] = {0, 10, 20, 30, 40};
const unsigned SCH_FRAMES[] = {1, 11, 21, 31, 41};
const unsigned BCCH_FRAMES[] = {2, 3, 4, 5}; //!!the receiver shouldn't care about logical
//!!channels so this will be removed from this header
const unsigned TEST_CCH_FRAMES[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49};
const unsigned TRAFFIC_CHANNEL_F[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
const unsigned TEST51[] = {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, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50};
#define TSC0 0
#define TSC1 1
#define TSC2 2
#define TSC3 3
#define TSC4 4
#define TSC5 5
#define TSC6 6
#define TSC7 7
#define TS_DUMMY 8
#define TRAIN_SEQ_NUM 9
#define TIMESLOT0 0
#define TIMESLOT1 1
#define TIMESLOT2 2
#define TIMESLOT3 3
#define TIMESLOT4 4
#define TIMESLOT5 5
#define TIMESLOT6 6
#define TIMESLOT7 7
static const unsigned char train_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS] = {
{0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
{0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
{0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0},
{0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0},
{0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1},
{0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0},
{1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1},
{1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1} // DUMMY
};
//Dummy burst 0xFB 76 0A 4E 09 10 1F 1C 5C 5C 57 4A 33 39 E9 F1 2F A8
static const unsigned char dummy_burst[] = {
0, 0, 0,
1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0,
1, 0, 1, 0, 0, 1, 0, 0, 1, 1,
1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 0, 0, 0, 1, 0, 1,
1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
0, 0, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 0, 0, 1, 0,
1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
0, 0, 1, 1, 1, 0, 0, 1, 1, 1,
1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0
};
/*
* The frequency correction burst is used for frequency synchronization
* of the mobile. This is broadcast in TS0 together with the SCH and
* BCCH.
*
* Modulating the bits below causes a spike at 62.5kHz above (below for
* COMPACT) the center frequency. One can use this spike with a narrow
* band filter to accurately determine the center of the channel.
*/
static const unsigned char fc_fb[] = {
0, 0, 0, //I don't use this tables,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //I copied this here from burst_types.h because
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //the description is very informative - p.krysik
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0
};
static const unsigned char fc_compact_fb[] = {
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0
};
#endif /* INCLUDED_GSM_CONSTANTS_H */

84
lib/receiver_config.cc Normal file
View File

@ -0,0 +1,84 @@
/* -*- c++ -*- */
/*
* @file
* @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
* @section LICENSE
*
* 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.
*
* @section DESCRIPTION
* This file contains classes which define gsm_receiver configuration
* and the burst_counter which is used to store internal state of the receiver
* when it's synchronized
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <receiver_config.h>
burst_counter & burst_counter::operator++(int)
{
d_timeslot_nr++;
if (d_timeslot_nr == TS_PER_FRAME) {
d_timeslot_nr = 0;
if ((d_t2 == 25) && (d_t3 == 50)) {
d_t1 = (d_t1 + 1) % (1 << 11);
}
d_t2 = (d_t2 + 1) % 26;
d_t3 = (d_t3 + 1) % 51;
}
//update offset - this is integer for d_OSR which is multiple of four
d_offset_fractional += GUARD_FRACTIONAL * d_OSR;
d_offset_integer = floor(d_offset_fractional);
d_offset_fractional = d_offset_fractional - d_offset_integer;
return (*this);
}
void burst_counter::set(uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr)
{
d_t1 = t1;
d_t2 = t2;
d_t3 = t3;
d_timeslot_nr = timeslot_nr;
double first_sample_position = (get_frame_nr() * 8 + timeslot_nr) * TS_BITS;
d_offset_fractional = first_sample_position - floor(first_sample_position);
d_offset_integer = 0;
}
burst_type channel_configuration::get_burst_type(burst_counter burst_nr)
{
uint32_t timeslot_nr = burst_nr.get_timeslot_nr();
multiframe_type m_type = d_timeslots_descriptions[timeslot_nr].get_type();
uint32_t nr;
switch (m_type) {
case multiframe_26:
nr = burst_nr.get_t2();
break;
case multiframe_51:
nr = burst_nr.get_t3();
break;
default:
nr = 0;
break;
}
return d_timeslots_descriptions[timeslot_nr].get_burst_type(nr);
}

164
lib/receiver_config.h Normal file
View File

@ -0,0 +1,164 @@
/* -*- c++ -*- */
/*
* @file
* @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
* @section LICENSE
*
* 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.
*
* @section DESCRIPTION
* This file contains classes which define gsm_receiver configuration
* and the burst_counter which is used to store internal state of the receiver
* when it's synchronized
*/
#ifndef INCLUDED_GSM_RECEIVER_CONFIG_H
#define INCLUDED_GSM_RECEIVER_CONFIG_H
#include <vector>
#include <algorithm>
#include <math.h>
#include <stdint.h>
#include <gsm_constants.h>
class multiframe_configuration
{
private:
multiframe_type d_type;
std::vector<burst_type> d_burst_types;
public:
multiframe_configuration() {
d_type = unknown;
fill(d_burst_types.begin(), d_burst_types.end(), empty);
}
~multiframe_configuration() {}
void set_type(multiframe_type type) {
if (type == multiframe_26) {
d_burst_types.resize(26);
} else {
d_burst_types.resize(51);
}
d_type = type;
}
void set_burst_type(int nr, burst_type type) {
d_burst_types[nr] = type;
}
multiframe_type get_type() {
return d_type;
}
burst_type get_burst_type(int nr) {
return d_burst_types[nr];
}
};
class burst_counter
{
private:
const int d_OSR;
uint32_t d_t1, d_t2, d_t3, d_timeslot_nr;
double d_offset_fractional;
double d_offset_integer;
public:
burst_counter(int osr):
d_OSR(osr),
d_t1(0),
d_t2(0),
d_t3(0),
d_timeslot_nr(0),
d_offset_fractional(0.0),
d_offset_integer(0.0) {
}
burst_counter(int osr, uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr):
d_OSR(osr),
d_t1(t1),
d_t2(t2),
d_t3(t3),
d_timeslot_nr(timeslot_nr),
d_offset_fractional(0.0),
d_offset_integer(0.0) {
double first_sample_position = (get_frame_nr() * 8 + timeslot_nr) * TS_BITS;
d_offset_integer = floor(first_sample_position);
d_offset_fractional = first_sample_position - floor(first_sample_position);
}
burst_counter & operator++(int);
void set(uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr);
uint32_t get_t1() {
return d_t1;
}
uint32_t get_t2() {
return d_t2;
}
uint32_t get_t3() {
return d_t3;
}
uint32_t get_timeslot_nr() {
return d_timeslot_nr;
}
uint32_t get_frame_nr() {
return (51 * 26 * d_t1) + (51 * (((d_t3 + 26) - d_t2) % 26)) + d_t3;
}
uint32_t get_frame_nr_mod() {
return (d_t1 << 11) + (d_t3 << 5) + d_t2;
}
unsigned get_offset() {
return (unsigned)d_offset_integer;
}
};
class channel_configuration
{
private:
multiframe_configuration d_timeslots_descriptions[TS_PER_FRAME];
public:
channel_configuration() {
for (int i = 0; i < TS_PER_FRAME; i++) {
d_timeslots_descriptions[i].set_type(unknown);
}
}
void set_multiframe_type(int timeslot_nr, multiframe_type type) {
d_timeslots_descriptions[timeslot_nr].set_type(type);
}
void set_burst_types(int timeslot_nr, const unsigned mapping[], unsigned mapping_size, burst_type b_type) {
unsigned i;
for (i = 0; i < mapping_size; i++) {
d_timeslots_descriptions[timeslot_nr].set_burst_type(mapping[i], b_type);
}
}
void set_single_burst_type(int timeslot_nr, int burst_nr, burst_type b_type) {
d_timeslots_descriptions[timeslot_nr].set_burst_type(burst_nr, b_type);
}
burst_type get_burst_type(burst_counter burst_nr);
};
#endif /* INCLUDED_GSM_RECEIVER_CONFIG_H */

770
lib/receiver_impl.cc Normal file
View File

@ -0,0 +1,770 @@
/* -*- c++ -*- */
/*
* Copyright 2014 <+YOU OR YOUR COMPANY+>.
*
* This 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.
*
* This software 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 software; 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 "receiver_impl.h"
#include <gnuradio/io_signature.h>
#include <gnuradio/math.h>
#include <math.h>
#include <boost/circular_buffer.hpp>
#include <algorithm>
#include <numeric>
#include <viterbi_detector.h>
#include <string.h>
#include <sch.h>
#include <iostream>
#include <iomanip>
#include <assert.h>
#define SYNC_SEARCH_RANGE 30
namespace gr {
namespace gsm {
typedef std::list<float> list_float;
typedef std::vector<float> vector_float;
typedef boost::circular_buffer<float> circular_buffer_float;
receiver::sptr
receiver::make(feval_dd * tuner, int osr)
{
return gnuradio::get_initial_sptr
(new receiver_impl(tuner, osr));
}
/*
* The private constructor
*/
receiver_impl::receiver_impl(feval_dd * tuner, int osr)
: gr::block("receiver",
gr::io_signature::make(1, 1, sizeof(gr_complex)),
gr::io_signature::make(0, 1, 142 * sizeof(float))),
d_OSR(osr),
d_chan_imp_length(CHAN_IMP_RESP_LENGTH),
d_tuner(tuner),
d_counter(0),
d_fcch_start_pos(0),
d_freq_offset(0),
d_state(first_fcch_search),
d_burst_nr(osr),
d_failed_sch(0)
{
int i;
gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
for (i = 0; i < TRAIN_SEQ_NUM; i++) {
gr_complex startpoint;
if (i == 6 || i == 7) { //this is nasty hack
startpoint = gr_complex(-1.0, 0.0); //if I don't change it here all bits of normal bursts for BTSes with bcc=6 will have reversed values
} else {
startpoint = gr_complex(1.0, 0.0); //I've checked this hack for bcc==0,1,2,3,4,6
} //I don't know what about bcc==5 and 7 yet
//TODO:find source of this situation - this is purely mathematical problem I guess
gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
}
}
/*
* Our virtual destructor.
*/
receiver_impl::~receiver_impl()
{
}
void receiver_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required)
{
ninput_items_required[0] = noutput_items * floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR);
}
int
receiver_impl::general_work(int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const gr_complex *input = (const gr_complex *) input_items[0];
//float *out = (float *) output_items[0];
int produced_out = 0; //how many output elements were produced - this isn't used yet
//probably the gsm receiver will be changed into sink so this variable won't be necessary
switch (d_state) {
//bootstrapping
case first_fcch_search:
if (find_fcch_burst(input, ninput_items[0])) { //find frequency correction burst in the input buffer
set_frequency(d_freq_offset); //if fcch search is successful set frequency offset
//produced_out = 0;
d_state = next_fcch_search;
} else {
//produced_out = 0;
d_state = first_fcch_search;
}
break;
case next_fcch_search: { //this state is used because it takes some time (a bunch of buffered samples)
COUT("fcch");
float prev_freq_offset = d_freq_offset; //before previous set_frequqency cause change
if (find_fcch_burst(input, ninput_items[0])) {
if (abs(prev_freq_offset - d_freq_offset) > FCCH_MAX_FREQ_OFFSET) {
set_frequency(d_freq_offset); //call set_frequncy only frequency offset change is greater than some value
}
//produced_out = 0;
d_state = sch_search;
} else {
//produced_out = 0;
d_state = next_fcch_search;
}
break;
}
case sch_search: {
vector_complex channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR);
int t1, t2, t3;
int burst_start = 0;
unsigned char output_binary[BURST_SIZE];
if (reach_sch_burst(ninput_items[0])) { //wait for a SCH burst
burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response from it
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //detect bits using MLSE detection
if (decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc) == 0) { //decode SCH burst
COUT("sch burst_start: " << burst_start);
COUT("bcc: " << d_bcc << " ncc: " << d_ncc << " t1: " << t1 << " t2: " << t2 << " t3: " << t3);
d_burst_nr.set(t1, t2, t3, 0); //set counter of bursts value
//configure the receiver - tell him where to find which burst type
d_channel_conf.set_multiframe_type(TIMESLOT0, multiframe_51); //in the timeslot nr.0 bursts changes according to t3 counter
configure_receiver();//TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
d_channel_conf.set_burst_types(TIMESLOT0, FCCH_FRAMES, sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst); //tell where to find fcch bursts
d_channel_conf.set_burst_types(TIMESLOT0, SCH_FRAMES, sizeof(SCH_FRAMES) / sizeof(unsigned), sch_burst); //sch bursts
d_channel_conf.set_burst_types(TIMESLOT0, BCCH_FRAMES, sizeof(BCCH_FRAMES) / sizeof(unsigned), normal_burst);//!and maybe normal bursts of the BCCH logical channel
d_burst_nr++;
consume_each(burst_start + BURST_SIZE * d_OSR); //consume samples up to next guard period
d_state = synchronized;
} else {
d_state = next_fcch_search; //if there is error in the sch burst go back to fcch search phase
}
} else {
d_state = sch_search;
}
break;
}
//in this state receiver is synchronized and it processes bursts according to burst type for given burst number
case synchronized: {
vector_complex channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR);
int burst_start;
int offset = 0;
int to_consume = 0;
unsigned char output_binary[BURST_SIZE];
burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr); //get burst type for given burst number
switch (b_type) {
case fcch_burst: { //if it's FCCH burst
const unsigned first_sample = 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 = compute_freq_offset(input, first_sample, last_sample); //extract frequency offset from it
d_freq_offset_vals.push_front(freq_offset);
//process_normal_burst(d_burst_nr, fc_fb);
if (d_freq_offset_vals.size() >= 10) {
double sum = std::accumulate(d_freq_offset_vals.begin(), d_freq_offset_vals.end(), 0);
double mean_offset = sum / d_freq_offset_vals.size(); //compute mean
d_freq_offset_vals.clear();
if (abs(mean_offset) > FCCH_MAX_FREQ_OFFSET) {
d_freq_offset -= mean_offset; //and adjust frequency if it have changed beyond
set_frequency(d_freq_offset); //some limit
DCOUT("mean_offset: " << mean_offset);
DCOUT("Adjusting frequency, new frequency offset: " << d_freq_offset << "\n");
}
}
}
break;
case sch_burst: { //if it's SCH burst
int t1, t2, t3, d_ncc, d_bcc;
burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits
//process_normal_burst(d_burst_nr, output_binary);
if (decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc) == 0) { //and decode SCH data
// d_burst_nr.set(t1, t2, t3, 0); //but only to check if burst_start value is correct
d_failed_sch = 0;
DCOUT("bcc: " << d_bcc << " ncc: " << d_ncc << " t1: " << t1 << " t2: " << t2 << " t3: " << t3);
offset = burst_start - floor((GUARD_PERIOD) * d_OSR); //compute offset from burst_start - burst should start after a guard period
DCOUT(offset);
to_consume += offset; //adjust with offset number of samples to be consumed
} else {
d_failed_sch++;
if (d_failed_sch >= MAX_SCH_ERRORS) {
// d_state = next_fcch_search; //TODO: this isn't good, the receiver is going wild when it goes back to next_fcch_search from here
// d_freq_offset_vals.clear();
DCOUT("many sch decoding errors");
}
}
}
break;
case normal_burst: //if it's normal burst
burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], d_bcc); //get channel impulse response for given training sequence number - d_bcc
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits
process_normal_burst(d_burst_nr, output_binary); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
break;
case dummy_or_normal: {
burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], TS_DUMMY);
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary);
std::vector<unsigned char> v(20);
std::vector<unsigned char>::iterator it;
it = std::set_difference(output_binary + TRAIN_POS, output_binary + TRAIN_POS + 16, &train_seq[TS_DUMMY][5], &train_seq[TS_DUMMY][21], v.begin());
int different_bits = (it - v.begin());
if (different_bits > 2) {
burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], d_bcc);
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary);
//if (!output_binary[0] && !output_binary[1] && !output_binary[2]) {
COUT("Normal burst");
process_normal_burst(d_burst_nr, output_binary); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
//}
} else {
//process_normal_burst(d_burst_nr, dummy_burst);
}
}
case rach_burst:
//implementation of this channel isn't possible in current gsm_receiver
//it would take some realtime processing, counter of samples from USRP to
//stay synchronized with this device and possibility to switch frequency from uplink
//to C0 (where sch is) back and forth
break;
case dummy: //if it's dummy
burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], TS_DUMMY); //read dummy
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); // but as far as I know it's pointless
break;
case empty: //if it's empty burst
break; //do nothing
}
d_burst_nr++; //go to next burst
to_consume += TS_BITS * d_OSR + d_burst_nr.get_offset(); //consume samples of the burst up to next guard period
//and add offset which is introduced by
//0.25 fractional part of a guard period
//burst_number computes this offset
//but choice of this class to do this was random
consume_each(to_consume);
}
break;
}
return produced_out;
}
bool receiver_impl::find_fcch_burst(const gr_complex *input, const int nitems)
{
circular_buffer_float phase_diff_buffer(FCCH_HITS_NEEDED * d_OSR); //circular buffer used to scan throug signal to find
//best match for FCCH burst
float phase_diff = 0;
gr_complex conjprod;
int start_pos = -1;
int hit_count = 0;
int miss_count = 0;
float min_phase_diff;
float max_phase_diff;
double best_sum = 0;
float lowest_max_min_diff = 99999;
int to_consume = 0;
int sample_number = 0;
bool end = false;
bool result = false;
circular_buffer_float::iterator buffer_iter;
/**@name Possible states of FCCH search algorithm*/
//@{
enum states {
init, ///< initialize variables
search, ///< search for positive samples
found_something, ///< search for FCCH and the best position of it
fcch_found, ///< when FCCH was found
search_fail ///< when there is no FCCH in the input vector
} fcch_search_state;
//@}
fcch_search_state = init;
while (!end) {
switch (fcch_search_state) {
case init: //initialize variables
hit_count = 0;
miss_count = 0;
start_pos = -1;
lowest_max_min_diff = 99999;
phase_diff_buffer.clear();
fcch_search_state = search;
break;
case search: // search for positive samples
sample_number++;
if (sample_number > nitems - FCCH_HITS_NEEDED * d_OSR) { //if it isn't possible to find FCCH because
//there's too few samples left to look into,
to_consume = sample_number; //don't do anything with those samples which are left
//and consume only those which were checked
fcch_search_state = search_fail;
} else {
phase_diff = compute_phase_diff(input[sample_number], input[sample_number-1]);
if (phase_diff > 0) { //if a positive phase difference was found
to_consume = sample_number;
fcch_search_state = found_something; //switch to state in which searches for FCCH
} else {
fcch_search_state = search;
}
}
break;
case found_something: {// search for FCCH and the best position of it
if (phase_diff > 0) {
hit_count++; //positive phase differencies increases hits_count
} else {
miss_count++; //negative increases miss_count
}
if ((miss_count >= FCCH_MAX_MISSES * d_OSR) && (hit_count <= FCCH_HITS_NEEDED * d_OSR)) {
//if miss_count exceeds limit before hit_count
fcch_search_state = init; //go to init
continue;
} else if (((miss_count >= FCCH_MAX_MISSES * d_OSR) && (hit_count > FCCH_HITS_NEEDED * d_OSR)) || (hit_count > 2 * FCCH_HITS_NEEDED * d_OSR)) {
//if hit_count and miss_count exceeds limit then FCCH was found
fcch_search_state = fcch_found;
continue;
} else if ((miss_count < FCCH_MAX_MISSES * d_OSR) && (hit_count > FCCH_HITS_NEEDED * d_OSR)) {
//find difference between minimal and maximal element in the buffer
//for FCCH this value should be low
//this part is searching for a region where this value is lowest
min_phase_diff = * (min_element(phase_diff_buffer.begin(), phase_diff_buffer.end()));
max_phase_diff = * (max_element(phase_diff_buffer.begin(), phase_diff_buffer.end()));
if (lowest_max_min_diff > max_phase_diff - min_phase_diff) {
lowest_max_min_diff = max_phase_diff - min_phase_diff;
start_pos = sample_number - FCCH_HITS_NEEDED * d_OSR - FCCH_MAX_MISSES * d_OSR; //store start pos
best_sum = 0;
for (buffer_iter = phase_diff_buffer.begin();
buffer_iter != (phase_diff_buffer.end());
buffer_iter++) {
best_sum += *buffer_iter - (M_PI / 2) / d_OSR; //store best value of phase offset sum
}
}
}
sample_number++;
if (sample_number >= nitems) { //if there's no single sample left to check
fcch_search_state = search_fail;//FCCH search failed
continue;
}
phase_diff = compute_phase_diff(input[sample_number], input[sample_number-1]);
phase_diff_buffer.push_back(phase_diff);
fcch_search_state = found_something;
}
break;
case fcch_found: {
DCOUT("fcch found on position: " << d_counter + start_pos);
to_consume = start_pos + FCCH_HITS_NEEDED * d_OSR + 1; //consume one FCCH burst
d_fcch_start_pos = d_counter + start_pos;
//compute frequency offset
double phase_offset = best_sum / FCCH_HITS_NEEDED;
double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI);
d_freq_offset -= freq_offset;
DCOUT("freq_offset: " << d_freq_offset);
end = true;
result = true;
break;
}
case search_fail:
end = true;
result = false;
break;
}
}
d_counter += to_consume;
consume_each(to_consume);
return result;
}
double receiver_impl::compute_freq_offset(const gr_complex * input, unsigned first_sample, unsigned last_sample)
{
double phase_sum = 0;
unsigned ii;
for (ii = first_sample; ii < last_sample; ii++) {
double phase_diff = compute_phase_diff(input[ii], input[ii-1]) - (M_PI / 2) / d_OSR;
phase_sum += phase_diff;
}
double phase_offset = phase_sum / (last_sample - first_sample);
double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI);
return freq_offset;
}
void receiver_impl::set_frequency(double freq_offset)
{
d_tuner->calleval(freq_offset);
}
inline float receiver_impl::compute_phase_diff(gr_complex val1, gr_complex val2)
{
gr_complex conjprod = val1 * conj(val2);
return fast_atan2f(imag(conjprod), real(conjprod));
}
bool receiver_impl::reach_sch_burst(const int nitems)
{
//it just consumes samples to get near to a SCH burst
int to_consume = 0;
bool result = false;
unsigned sample_nr_near_sch_start = d_fcch_start_pos + (FRAME_BITS - SAFETY_MARGIN) * d_OSR;
//consume samples until d_counter will be equal to sample_nr_near_sch_start
if (d_counter < sample_nr_near_sch_start) {
if (d_counter + nitems >= sample_nr_near_sch_start) {
to_consume = sample_nr_near_sch_start - d_counter;
} else {
to_consume = nitems;
}
result = false;
} else {
to_consume = 0;
result = true;
}
d_counter += to_consume;
consume_each(to_consume);
return result;
}
int receiver_impl::get_sch_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp)
{
vector_complex correlation_buffer;
vector_float power_buffer;
vector_float window_energy_buffer;
int strongest_window_nr;
int burst_start = 0;
int chan_imp_resp_center = 0;
float max_correlation = 0;
float energy = 0;
for (int ii = SYNC_POS * d_OSR; ii < (SYNC_POS + SYNC_SEARCH_RANGE) *d_OSR; ii++) {
gr_complex correlation = correlate_sequence(&d_sch_training_seq[5], N_SYNC_BITS - 10, &input[ii]);
correlation_buffer.push_back(correlation);
power_buffer.push_back(std::pow(abs(correlation), 2));
}
//compute window energies
vector_float::iterator iter = power_buffer.begin();
bool loop_end = false;
while (iter != power_buffer.end()) {
vector_float::iterator iter_ii = iter;
energy = 0;
for (int ii = 0; ii < (d_chan_imp_length) *d_OSR; ii++, iter_ii++) {
if (iter_ii == power_buffer.end()) {
loop_end = true;
break;
}
energy += (*iter_ii);
}
if (loop_end) {
break;
}
iter++;
window_energy_buffer.push_back(energy);
}
strongest_window_nr = max_element(window_energy_buffer.begin(), window_energy_buffer.end()) - window_energy_buffer.begin();
// d_channel_imp_resp.clear();
max_correlation = 0;
for (int ii = 0; ii < (d_chan_imp_length) *d_OSR; ii++) {
gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
if (abs(correlation) > max_correlation) {
chan_imp_resp_center = ii;
max_correlation = abs(correlation);
}
// d_channel_imp_resp.push_back(correlation);
chan_imp_resp[ii] = correlation;
}
burst_start = strongest_window_nr + chan_imp_resp_center - 48 * d_OSR - 2 * d_OSR + 2 + SYNC_POS * d_OSR;
return burst_start;
}
void receiver_impl::detect_burst(const gr_complex * input, gr_complex * chan_imp_resp, int burst_start, unsigned char * output_binary)
{
float output[BURST_SIZE];
gr_complex rhh_temp[CHAN_IMP_RESP_LENGTH*d_OSR];
gr_complex rhh[CHAN_IMP_RESP_LENGTH];
gr_complex filtered_burst[BURST_SIZE];
int start_state = 3;
unsigned int stop_states[2] = {4, 12};
autocorrelation(chan_imp_resp, rhh_temp, d_chan_imp_length*d_OSR);
for (int ii = 0; ii < (d_chan_imp_length); ii++) {
rhh[ii] = conj(rhh_temp[ii*d_OSR]);
}
mafi(&input[burst_start], BURST_SIZE, chan_imp_resp, d_chan_imp_length*d_OSR, filtered_burst);
viterbi_detector(filtered_burst, BURST_SIZE, rhh, start_state, stop_states, 2, output);
for (int i = 0; i < BURST_SIZE ; i++) {
output_binary[i] = (output[i] > 0);
}
}
//TODO consider placing this funtion in a separate class for signal processing
void receiver_impl::gmsk_mapper(const unsigned char * input, int nitems, gr_complex * gmsk_output, gr_complex start_point)
{
gr_complex j = gr_complex(0.0, 1.0);
int current_symbol;
int encoded_symbol;
int previous_symbol = 2 * input[0] - 1;
gmsk_output[0] = start_point;
for (int i = 1; i < nitems; i++) {
//change bits representation to NRZ
current_symbol = 2 * input[i] - 1;
//differentially encode
encoded_symbol = current_symbol * previous_symbol;
//and do gmsk mapping
gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i-1];
previous_symbol = current_symbol;
}
}
//TODO consider use of some generalized function for correlation and placing it in a separate class for signal processing
gr_complex receiver_impl::correlate_sequence(const gr_complex * sequence, int length, const gr_complex * input)
{
gr_complex result(0.0, 0.0);
int sample_number = 0;
for (int ii = 0; ii < length; ii++) {
sample_number = (ii * d_OSR) ;
result += sequence[ii] * conj(input[sample_number]);
}
result = result / gr_complex(length, 0);
return result;
}
//computes autocorrelation for positive arguments
//TODO consider placing this funtion in a separate class for signal processing
inline void receiver_impl::autocorrelation(const gr_complex * input, gr_complex * out, int nitems)
{
int i, k;
for (k = nitems - 1; k >= 0; k--) {
out[k] = gr_complex(0, 0);
for (i = k; i < nitems; i++) {
out[k] += input[i] * conj(input[i-k]);
}
}
}
//TODO consider use of some generalized function for filtering and placing it in a separate class for signal processing
inline void receiver_impl::mafi(const gr_complex * input, int nitems, gr_complex * filter, int filter_length, gr_complex * output)
{
int ii = 0, n, a;
for (n = 0; n < nitems; n++) {
a = n * d_OSR;
output[n] = 0;
ii = 0;
while (ii < filter_length) {
if ((a + ii) >= nitems*d_OSR)
break;
output[n] += input[a+ii] * filter[ii];
ii++;
}
}
}
//TODO: get_norm_chan_imp_resp is similar to get_sch_chan_imp_resp - consider joining this two functions
//TODO: this is place where most errors are introduced and can be corrected by improvements to this fuction
//especially computations of strongest_window_nr
int receiver_impl::get_norm_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp, int bcc)
{
vector_complex correlation_buffer;
vector_float power_buffer;
vector_float window_energy_buffer;
int strongest_window_nr;
int burst_start = 0;
int chan_imp_resp_center = 0;
float max_correlation = 0;
float energy = 0;
int search_center = (int)((TRAIN_POS + GUARD_PERIOD) * d_OSR);
int search_start_pos = search_center + 1;
// int search_start_pos = search_center - d_chan_imp_length * d_OSR;
int search_stop_pos = search_center + d_chan_imp_length * d_OSR + 2 * d_OSR;
for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
gr_complex correlation = correlate_sequence(&d_norm_training_seq[bcc][TRAIN_BEGINNING], N_TRAIN_BITS - 10, &input[ii]);
correlation_buffer.push_back(correlation);
power_buffer.push_back(std::pow(abs(correlation), 2));
}
//compute window energies
vector_float::iterator iter = power_buffer.begin();
bool loop_end = false;
while (iter != power_buffer.end()) {
vector_float::iterator iter_ii = iter;
energy = 0;
for (int ii = 0; ii < (d_chan_imp_length - 2)*d_OSR; ii++, iter_ii++) {
// for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++, iter_ii++) {
if (iter_ii == power_buffer.end()) {
loop_end = true;
break;
}
energy += (*iter_ii);
}
if (loop_end) {
break;
}
iter++;
window_energy_buffer.push_back(energy);
}
//!why doesn't this work
int strongest_window_nr_new = max_element(window_energy_buffer.begin(), window_energy_buffer.end()) - window_energy_buffer.begin();
strongest_window_nr = 3; //! so I have to override it here
max_correlation = 0;
for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++) {
gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
if (abs(correlation) > max_correlation) {
chan_imp_resp_center = ii;
max_correlation = abs(correlation);
}
// d_channel_imp_resp.push_back(correlation);
chan_imp_resp[ii] = correlation;
}
// We want to use the first sample of the impulseresponse, and the
// corresponding samples of the received signal.
// the variable sync_w should contain the beginning of the used part of
// training sequence, which is 3+57+1+6=67 bits into the burst. That is
// we have that sync_t16 equals first sample in bit number 67.
burst_start = search_start_pos + chan_imp_resp_center + strongest_window_nr - TRAIN_POS * d_OSR;
// GMSK modulator introduces ISI - each bit is expanded for 3*Tb
// and it's maximum value is in the last bit period, so burst starts
// 2*Tb earlier
burst_start -= 2 * d_OSR;
burst_start += 2;
COUT("Poczatek ###############################");
std::cout << " burst_start: " << burst_start << " center: " << ((float)(search_start_pos + strongest_window_nr + chan_imp_resp_center)) / d_OSR << " stronegest window nr: " << strongest_window_nr << "\n";
COUT("burst_start_new: " << (search_start_pos + strongest_window_nr_new - TRAIN_POS * d_OSR));
burst_start=(search_start_pos + strongest_window_nr_new - TRAIN_POS * d_OSR)
return burst_start;
}
void receiver_impl::process_normal_burst(burst_counter burst_nr, const unsigned char * burst_binary)
{
int ii;
//std::cout << "fn:" <<burst_nr.get_frame_nr() << " ts" << burst_nr.get_timeslot_nr() << " ";
for(ii=0;ii<148;ii++){
std::cout << std::setprecision(1) << static_cast<int>(burst_binary[ii]);
}
std::cout << std::endl;
}
//TODO: this shouldn't be here also - the same reason
void receiver_impl::configure_receiver()
{
d_channel_conf.set_multiframe_type(TSC0, multiframe_51);
d_channel_conf.set_burst_types(TIMESLOT0, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_burst_types(TSC0, TEST_CCH_FRAMES, sizeof(TEST_CCH_FRAMES) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_burst_types(TSC0, FCCH_FRAMES, sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst);
// d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_26);
// d_channel_conf.set_burst_types(TIMESLOT1, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
// d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_26);
// d_channel_conf.set_burst_types(TIMESLOT2, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
// d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_26);
// d_channel_conf.set_burst_types(TIMESLOT3, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
// d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_26);
// d_channel_conf.set_burst_types(TIMESLOT4, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
// d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_26);
// d_channel_conf.set_burst_types(TIMESLOT5, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
// d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_26);
// d_channel_conf.set_burst_types(TIMESLOT6, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
// d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_26);
// d_channel_conf.set_burst_types(TIMESLOT7, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_51);
d_channel_conf.set_burst_types(TIMESLOT1, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_51);
d_channel_conf.set_burst_types(TIMESLOT2, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_51);
d_channel_conf.set_burst_types(TIMESLOT3, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_51);
d_channel_conf.set_burst_types(TIMESLOT4, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_51);
d_channel_conf.set_burst_types(TIMESLOT5, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_51);
d_channel_conf.set_burst_types(TIMESLOT6, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_51);
d_channel_conf.set_burst_types(TIMESLOT7, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
}
} /* namespace gsm */
} /* namespace gr */

215
lib/receiver_impl.h Normal file
View File

@ -0,0 +1,215 @@
/* -*- c++ -*- */
/*
* Copyright 2014 <+YOU OR YOUR COMPANY+>.
*
* This 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.
*
* This software 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 software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_GSM_RECEIVER_IMPL_H
#define INCLUDED_GSM_RECEIVER_IMPL_H
#include <gsm/receiver.h>
#include <gsm_constants.h>
#include <receiver_config.h>
namespace gr {
namespace gsm {
typedef std::vector<gr_complex> vector_complex;
class receiver_impl : public receiver
{
private:
/**@name Configuration of the receiver */
//@{
const int d_OSR; ///< oversampling ratio
const int d_chan_imp_length; ///< channel impulse length
//@}
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 bursts and dummy bursts
feval_dd *d_tuner; ///<callback to a python object which is used for frequency tunning
/** Counts samples consumed by the receiver
*
* It is used in beetween find_fcch_burst and reach_sch_burst calls.
* My intention was to synchronize this counter with some internal sample
* counter of the USRP. Simple access to such USRP's counter isn't possible
* so this variable isn't used in the "synchronized" state of the receiver yet.
*/
unsigned d_counter;
/**@name Variables used to store result of the find_fcch_burst fuction */
//@{
unsigned d_fcch_start_pos; ///< position of the first sample of the fcch burst
float d_freq_offset; ///< frequency offset of the received signal
//@}
std::list<double> d_freq_offset_vals;
/**@name Identifiers of the BTS extracted from the SCH burst */
//@{
int d_ncc; ///< network color code
int d_bcc; ///< base station color code
//@}
/**@name Internal state of the gsm receiver */
//@{
enum states {
first_fcch_search, next_fcch_search, sch_search, // synchronization search part
synchronized // receiver is synchronized in this state
} d_state;
//@}
/**@name Variables which make internal state in the "synchronized" state */
//@{
burst_counter d_burst_nr; ///< frame number and timeslot number
channel_configuration d_channel_conf; ///< mapping of burst_counter to burst_type
//@}
unsigned d_failed_sch; ///< number of subsequent erroneous SCH bursts
/** Function whis is used to search a FCCH burst and to compute frequency offset before
* "synchronized" state of the receiver
*
* TODO: Describe the FCCH search algorithm in the documentation
* @param input vector with input signal
* @param nitems number of samples in the input vector
* @return
*/
bool find_fcch_burst(const gr_complex *input, const int nitems);
/** Computes frequency offset from FCCH burst samples
*
* @param input vector with input samples
* @param first_sample number of the first sample of the FCCH busrt
* @param last_sample number of the last sample of the FCCH busrt
* @return frequency offset
*/
double compute_freq_offset(const gr_complex * input, unsigned first_sample, unsigned last_sample);
/** Calls d_tuner's method to set frequency offset from Python level
*
* @param freq_offset absolute frequency offset of the received signal
*/
void set_frequency(double freq_offset);
/** Computes angle between two complex numbers
*
* @param val1 first complex number
* @param val2 second complex number
* @return
*/
inline float compute_phase_diff(gr_complex val1, gr_complex val2);
/** Function whis is used to get near to SCH burst
*
* @param nitems number of samples in the gsm_receiver's buffer
* @return true if SCH burst is near, false otherwise
*/
bool reach_sch_burst(const int nitems);
/** Extracts channel impulse response from a SCH burst and computes first sample number of this burst
*
* @param input vector with input samples
* @param chan_imp_resp complex vector where channel impulse response will be stored
* @return number of first sample of the burst
*/
int get_sch_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp);
/** MLSE detection of a burst bits
*
* Detects bits of burst using viterbi algorithm.
* @param input vector with input samples
* @param chan_imp_resp vector with the channel impulse response
* @param burst_start number of the first sample of the burst
* @param output_binary vector with output bits
*/
void detect_burst(const gr_complex * input, gr_complex * chan_imp_resp, int burst_start, unsigned char * output_binary);
/** Encodes differentially input bits and maps them into MSK states
*
* @param input vector with input bits
* @param nitems number of samples in the "input" vector
* @param gmsk_output bits mapped into MSK states
* @param start_point first state
*/
void gmsk_mapper(const unsigned char * input, int nitems, gr_complex * gmsk_output, gr_complex start_point);
/** Correlates MSK mapped sequence with input signal
*
* @param sequence MKS mapped sequence
* @param length length of the sequence
* @param input_signal vector with input samples
* @return correlation value
*/
gr_complex correlate_sequence(const gr_complex * sequence, int length, const gr_complex * input);
/** Computes autocorrelation of input vector for positive arguments
*
* @param input vector with input samples
* @param out output vector
* @param nitems length of the input vector
*/
inline void autocorrelation(const gr_complex * input, gr_complex * out, int nitems);
/** Filters input signal through channel impulse response
*
* @param input vector with input samples
* @param nitems number of samples to pass through filter
* @param filter filter taps - channel impulse response
* @param filter_length nember of filter taps
* @param output vector with filtered samples
*/
inline void mafi(const gr_complex * input, int nitems, gr_complex * filter, int filter_length, gr_complex * output);
/** Extracts channel impulse response from a normal burst and computes first sample number of this burst
*
* @param input vector with input samples
* @param chan_imp_resp complex vector where channel impulse response will be stored
* @param search_range possible absolute offset of a channel impulse response start
* @param bcc base station color code - number of a training sequence
* @return first sample number of normal burst
*/
int get_norm_chan_imp_resp(const gr_complex * input, gr_complex * chan_imp_resp, int bcc);
/**
*
*/
void process_normal_burst(burst_counter burst_nr, const unsigned char * burst_binary);
/**
*
*/
void configure_receiver();
public:
receiver_impl(feval_dd * tuner, int osr);
~receiver_impl();
void forecast(int noutput_items, gr_vector_int &ninput_items_required);
// Where all the action really happens
int general_work(int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
};
} // namespace gsm
} // namespace gr
#endif /* INCLUDED_GSM_RECEIVER_IMPL_H */

333
lib/sch.c Normal file
View File

@ -0,0 +1,333 @@
#include "system.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "gsm_constants.h"
/*
* Synchronization channel.
*
* Timeslot Repeat length Frame Number (mod repeat length)
* 0 51 1, 11, 21, 31, 41
*/
/*
* Parity (FIRE) for the GSM SCH.
*
* g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1
*/
#define DATA_BLOCK_SIZE 25
#define PARITY_SIZE 10
#define TAIL_BITS_SIZE 4
#define PARITY_OUTPUT_SIZE (DATA_BLOCK_SIZE + PARITY_SIZE + TAIL_BITS_SIZE)
static const unsigned char parity_polynomial[PARITY_SIZE + 1] = {
1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1
};
static const unsigned char parity_remainder[PARITY_SIZE] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
static void parity_encode(unsigned char *d, unsigned char *p)
{
unsigned int i;
unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q;
memcpy(buf, d, DATA_BLOCK_SIZE);
memset(buf + DATA_BLOCK_SIZE, 0, PARITY_SIZE);
for (q = buf; q < buf + DATA_BLOCK_SIZE; q++)
if (*q)
for (i = 0; i < PARITY_SIZE + 1; i++)
q[i] ^= parity_polynomial[i];
for (i = 0; i < PARITY_SIZE; i++)
p[i] = !buf[DATA_BLOCK_SIZE + i];
}
static int parity_check(unsigned char *d)
{
unsigned int i;
unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q;
memcpy(buf, d, DATA_BLOCK_SIZE + PARITY_SIZE);
for (q = buf; q < buf + DATA_BLOCK_SIZE; q++)
if (*q)
for (i = 0; i < PARITY_SIZE + 1; i++)
q[i] ^= parity_polynomial[i];
return memcmp(buf + DATA_BLOCK_SIZE, parity_remainder, PARITY_SIZE);
}
/*
* Convolutional encoding and Viterbi decoding for the GSM SCH.
* (Equivalent to the GSM SACCH.)
*
* G_0 = 1 + x^3 + x^4
* G_1 = 1 + x + x^3 + x^4
*
* i.e.,
*
* c_{2k} = u_k + u_{k - 3} + u_{k - 4}
* c_{2k + 1} = u_k + u_{k - 1} + u_{k - 3} + u_{k - 4}
*/
#define CONV_INPUT_SIZE PARITY_OUTPUT_SIZE
#define CONV_SIZE (2 * CONV_INPUT_SIZE)
#define K 5
#define MAX_ERROR (2 * CONV_INPUT_SIZE + 1)
/*
* Given the current state and input bit, what are the output bits?
*
* encode[current_state][input_bit]
*/
static const unsigned int encode[1 << (K - 1)][2] = {
{0, 3}, {3, 0}, {3, 0}, {0, 3},
{0, 3}, {3, 0}, {3, 0}, {0, 3},
{1, 2}, {2, 1}, {2, 1}, {1, 2},
{1, 2}, {2, 1}, {2, 1}, {1, 2}
};
/*
* Given the current state and input bit, what is the next state?
*
* next_state[current_state][input_bit]
*/
static const unsigned int next_state[1 << (K - 1)][2] = {
{0, 8}, {0, 8}, {1, 9}, {1, 9},
{2, 10}, {2, 10}, {3, 11}, {3, 11},
{4, 12}, {4, 12}, {5, 13}, {5, 13},
{6, 14}, {6, 14}, {7, 15}, {7, 15}
};
/*
* Given the previous state and the current state, what input bit caused
* the transition? If it is impossible to transition between the two
* states, the value is 2.
*
* prev_next_state[previous_state][current_state]
*/
static const unsigned int prev_next_state[1 << (K - 1)][1 << (K - 1)] = {
{ 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2},
{ 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2},
{ 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2},
{ 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2},
{ 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2},
{ 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2},
{ 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2},
{ 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2},
{ 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2},
{ 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2},
{ 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2},
{ 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2},
{ 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2},
{ 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2},
{ 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1},
{ 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1}
};
static inline unsigned int hamming_distance2(unsigned int w)
{
return (w & 1) + !!(w & 2);
}
static void conv_encode(unsigned char *data, unsigned char *output)
{
unsigned int i, state = 0, o;
// encode data
for (i = 0; i < CONV_INPUT_SIZE; i++) {
o = encode[state][data[i]];
state = next_state[state][data[i]];
*output++ = !!(o & 2);
*output++ = o & 1;
}
}
static int conv_decode(unsigned char *data, unsigned char *output)
{
int i, t;
unsigned int rdata, state, nstate, b, o, distance, accumulated_error,
min_state, min_error, cur_state;
unsigned int ae[1 << (K - 1)];
unsigned int nae[1 << (K - 1)]; // next accumulated error
unsigned int state_history[1 << (K - 1)][CONV_INPUT_SIZE + 1];
// initialize accumulated error, assume starting state is 0
for (i = 0; i < (1 << (K - 1)); i++)
ae[i] = nae[i] = MAX_ERROR;
ae[0] = 0;
// build trellis
for (t = 0; t < CONV_INPUT_SIZE; t++) {
// get received data symbol
rdata = (data[2 * t] << 1) | data[2 * t + 1];
// for each state
for (state = 0; state < (1 << (K - 1)); state++) {
// make sure this state is possible
if (ae[state] >= MAX_ERROR)
continue;
// find all states we lead to
for (b = 0; b < 2; b++) {
// get next state given input bit b
nstate = next_state[state][b];
// find output for this transition
o = encode[state][b];
// calculate distance from received data
distance = hamming_distance2(rdata ^ o);
// choose surviving path
accumulated_error = ae[state] + distance;
if (accumulated_error < nae[nstate]) {
// save error for surviving state
nae[nstate] = accumulated_error;
// update state history
state_history[nstate][t + 1] = state;
}
}
}
// get accumulated error ready for next time slice
for (i = 0; i < (1 << (K - 1)); i++) {
ae[i] = nae[i];
nae[i] = MAX_ERROR;
}
}
// the final state is the state with the fewest errors
min_state = (unsigned int) - 1;
min_error = MAX_ERROR;
for (i = 0; i < (1 << (K - 1)); i++) {
if (ae[i] < min_error) {
min_state = i;
min_error = ae[i];
}
}
// trace the path
cur_state = min_state;
for (t = CONV_INPUT_SIZE; t >= 1; t--) {
min_state = cur_state;
cur_state = state_history[cur_state][t]; // get previous
output[t - 1] = prev_next_state[cur_state][min_state];
}
// return the number of errors detected (hard-decision)
return min_error;
}
int decode_sch(const unsigned char *buf, int * t1_o, int * t2_o, int * t3_o, int * ncc_o, int * bcc_o)
{
int errors, t1, t2, t3p, t3, ncc, bcc;
unsigned char data[CONV_SIZE], decoded_data[PARITY_OUTPUT_SIZE];
// extract encoded data from synchronization burst
/* buf, 39 bit */
/* buf + 39 + 64 = 103, 39 */
memcpy(data, buf, SCH_DATA_LEN);
memcpy(data + SCH_DATA_LEN, buf + SCH_DATA_LEN + N_SYNC_BITS, SCH_DATA_LEN);
// Viterbi decode
if (errors = conv_decode(data, decoded_data)) {
// fprintf(stderr, "error: sch: conv_decode (%d)\n", errors);
DEBUGF("ERR: conv_decode %d\n", errors);
return errors;
}
// check parity
if (parity_check(decoded_data)) {
// fprintf(stderr, "error: sch: parity failed\n");
DEBUGF("ERR: parity_check failed\n");
return 1;
}
// Synchronization channel information, 44.018 page 171. (V7.2.0)
ncc =
(decoded_data[ 7] << 2) |
(decoded_data[ 6] << 1) |
(decoded_data[ 5] << 0);
bcc =
(decoded_data[ 4] << 2) |
(decoded_data[ 3] << 1) |
(decoded_data[ 2] << 0);
t1 =
(decoded_data[ 1] << 10) |
(decoded_data[ 0] << 9) |
(decoded_data[15] << 8) |
(decoded_data[14] << 7) |
(decoded_data[13] << 6) |
(decoded_data[12] << 5) |
(decoded_data[11] << 4) |
(decoded_data[10] << 3) |
(decoded_data[ 9] << 2) |
(decoded_data[ 8] << 1) |
(decoded_data[23] << 0);
t2 =
(decoded_data[22] << 4) |
(decoded_data[21] << 3) |
(decoded_data[20] << 2) |
(decoded_data[19] << 1) |
(decoded_data[18] << 0);
t3p =
(decoded_data[17] << 2) |
(decoded_data[16] << 1) |
(decoded_data[24] << 0);
t3 = 10 * t3p + 1;
// modulo arithmetic t3 - t2 mod 26
// tt = ((t3 + 26) - t2) % 26;
// fn = (51 * 26 * t1) + (51 * tt) + t3;
/*
* BSIC: Base Station Identification Code
* BCC: Base station Color Code
* NCC: Network Color Code
*
* FN: Frame Number
*/
// printf("bsic: %x (bcc: %u; ncc: %u)\tFN: %u\n", bsic, bsic & 7,
// (bsic >> 3) & 7, fn);
// if (fn_o)
// *fn_o = fn;
// if (bsic_o)
if (t1_o && t2_o && t3_o && ncc_o && bcc_o) {
*t1_o = t1;
*t2_o = t2;
*t3_o = t3;
*bcc_o = bcc;
*ncc_o = ncc;
}
return 0;
}

19
lib/sch.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __SCH_H__
#define __SCH_H__ 1
#include <gsm/api.h>
#ifdef __cplusplus
extern "C"
{
#endif
GSM_API int decode_sch(const unsigned char *buf, int * t1_o, int * t2_o, int * t3_o, int * ncc, int * bcc);
#ifdef __cplusplus
}
#endif
#endif

10
lib/system.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef __GSMTVOID_SYSTEM_H__
#define __GSMTVOID_SYSTEM_H__ 1
#define DEBUGF(a...) { \
fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \
fprintf(stderr, a); \
} while (0)
#endif

47
lib/test_gsm.cc Normal file
View File

@ -0,0 +1,47 @@
/* -*- c++ -*- */
/*
* Copyright 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <cppunit/TextTestRunner.h>
#include <cppunit/XmlOutputter.h>
#include <gnuradio/unittests.h>
#include "qa_gsm.h"
#include <iostream>
int
main (int argc, char **argv)
{
CppUnit::TextTestRunner runner;
std::ofstream xmlfile(get_unittest_path("gsm.xml").c_str());
CppUnit::XmlOutputter *xmlout = new CppUnit::XmlOutputter(&runner.result(), xmlfile);
runner.addTest(qa_gsm::suite());
runner.setOutputter(xmlout);
bool was_successful = runner.run("", false);
return was_successful ? 0 : 1;
}

554
lib/viterbi_detector.cc Normal file
View File

@ -0,0 +1,554 @@
/* -*- c++ -*- */
/*
* @file
* @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
* @section LICENSE
*
* 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 3, 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
/*
* viterbi_detector:
* This part does the detection of received sequnece.
* Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
* At this moment it gives hard decisions on the output, but
* it was designed with soft decisions in mind.
*
* SYNTAX: void viterbi_detector(
* const gr_complex * input,
* unsigned int samples_num,
* gr_complex * rhh,
* unsigned int start_state,
* const unsigned int * stop_states,
* unsigned int stops_num,
* float * output)
*
* INPUT: input: Complex received signal afted matched filtering.
* samples_num: Number of samples in the input table.
* rhh: The autocorrelation of the estimated channel
* impulse response.
* start_state: Number of the start point. In GSM each burst
* starts with sequence of three bits (0,0,0) which
* indicates start point of the algorithm.
* stop_states: Table with numbers of possible stop states.
* stops_num: Number of possible stop states
*
*
* OUTPUT: output: Differentially decoded hard output of the algorithm:
* -1 for logical "0" and 1 for logical "1"
*
* SUB_FUNC: none
*
* TEST(S): Tested with real world normal burst.
*/
#include <gnuradio/gr_complex.h>
#include <gsm_constants.h>
#define PATHS_NUM (1 << (CHAN_IMP_RESP_LENGTH-1))
void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output)
{
float increment[8];
float path_metrics1[16];
float path_metrics2[16];
float * new_path_metrics;
float * old_path_metrics;
float * tmp;
float trans_table[BURST_SIZE][16];
float pm_candidate1, pm_candidate2;
bool real_imag;
float input_symbol_real, input_symbol_imag;
unsigned int i, sample_nr;
/*
* Setup first path metrics, so only state pointed by start_state is possible.
* Start_state metric is equal to zero, the rest is written with some very low value,
* which makes them practically impossible to occur.
*/
for(i=0; i<PATHS_NUM; i++){
path_metrics1[i]=(-10e30);
}
path_metrics1[start_state]=0;
/*
* Compute Increment - a table of values which does not change for subsequent input samples.
* Increment is table of reference levels for computation of branch metrics:
* branch metric = (+/-)received_sample (+/-) reference_level
*/
increment[0] = -rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
increment[1] = rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
increment[2] = -rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
increment[3] = rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
increment[4] = -rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
increment[5] = rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
increment[6] = -rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
increment[7] = rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
/*
* Computation of path metrics and decisions (Add-Compare-Select).
* It's composed of two parts: one for odd input samples (imaginary numbers)
* and one for even samples (real numbers).
* Each part is composed of independent (parallelisable) statements like
* this one:
* pm_candidate1 = old_path_metrics[0] - input_symbol_real - increment[7];
* pm_candidate2 = old_path_metrics[8] - input_symbol_real + increment[0];
* if(pm_candidate1 > pm_candidate2){
* new_path_metrics[0] = pm_candidate1;
* trans_table[sample_nr][0] = -1.0;
* }
* else{
* new_path_metrics[0] = pm_candidate2;
* trans_table[sample_nr][0] = 1.0;
* }
* This is very good point for optimisations (SIMD or OpenMP) as it's most time
* consuming part of this function.
*/
sample_nr=0;
old_path_metrics=path_metrics1;
new_path_metrics=path_metrics2;
while(sample_nr<samples_num){
//Processing imag states
real_imag=1;
input_symbol_imag = input[sample_nr].imag();
pm_candidate1 = old_path_metrics[0] + input_symbol_imag - increment[2];
pm_candidate2 = old_path_metrics[8] + input_symbol_imag + increment[5];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[0] = pm_candidate1;
trans_table[sample_nr][0] = -1.0;
}
else{
new_path_metrics[0] = pm_candidate2;
trans_table[sample_nr][0] = 1.0;
}
pm_candidate1 = old_path_metrics[0] - input_symbol_imag + increment[2];
pm_candidate2 = old_path_metrics[8] - input_symbol_imag - increment[5];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[1] = pm_candidate1;
trans_table[sample_nr][1] = -1.0;
}
else{
new_path_metrics[1] = pm_candidate2;
trans_table[sample_nr][1] = 1.0;
}
pm_candidate1 = old_path_metrics[1] + input_symbol_imag - increment[3];
pm_candidate2 = old_path_metrics[9] + input_symbol_imag + increment[4];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[2] = pm_candidate1;
trans_table[sample_nr][2] = -1.0;
}
else{
new_path_metrics[2] = pm_candidate2;
trans_table[sample_nr][2] = 1.0;
}
pm_candidate1 = old_path_metrics[1] - input_symbol_imag + increment[3];
pm_candidate2 = old_path_metrics[9] - input_symbol_imag - increment[4];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[3] = pm_candidate1;
trans_table[sample_nr][3] = -1.0;
}
else{
new_path_metrics[3] = pm_candidate2;
trans_table[sample_nr][3] = 1.0;
}
pm_candidate1 = old_path_metrics[2] + input_symbol_imag - increment[0];
pm_candidate2 = old_path_metrics[10] + input_symbol_imag + increment[7];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[4] = pm_candidate1;
trans_table[sample_nr][4] = -1.0;
}
else{
new_path_metrics[4] = pm_candidate2;
trans_table[sample_nr][4] = 1.0;
}
pm_candidate1 = old_path_metrics[2] - input_symbol_imag + increment[0];
pm_candidate2 = old_path_metrics[10] - input_symbol_imag - increment[7];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[5] = pm_candidate1;
trans_table[sample_nr][5] = -1.0;
}
else{
new_path_metrics[5] = pm_candidate2;
trans_table[sample_nr][5] = 1.0;
}
pm_candidate1 = old_path_metrics[3] + input_symbol_imag - increment[1];
pm_candidate2 = old_path_metrics[11] + input_symbol_imag + increment[6];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[6] = pm_candidate1;
trans_table[sample_nr][6] = -1.0;
}
else{
new_path_metrics[6] = pm_candidate2;
trans_table[sample_nr][6] = 1.0;
}
pm_candidate1 = old_path_metrics[3] - input_symbol_imag + increment[1];
pm_candidate2 = old_path_metrics[11] - input_symbol_imag - increment[6];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[7] = pm_candidate1;
trans_table[sample_nr][7] = -1.0;
}
else{
new_path_metrics[7] = pm_candidate2;
trans_table[sample_nr][7] = 1.0;
}
pm_candidate1 = old_path_metrics[4] + input_symbol_imag - increment[6];
pm_candidate2 = old_path_metrics[12] + input_symbol_imag + increment[1];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[8] = pm_candidate1;
trans_table[sample_nr][8] = -1.0;
}
else{
new_path_metrics[8] = pm_candidate2;
trans_table[sample_nr][8] = 1.0;
}
pm_candidate1 = old_path_metrics[4] - input_symbol_imag + increment[6];
pm_candidate2 = old_path_metrics[12] - input_symbol_imag - increment[1];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[9] = pm_candidate1;
trans_table[sample_nr][9] = -1.0;
}
else{
new_path_metrics[9] = pm_candidate2;
trans_table[sample_nr][9] = 1.0;
}
pm_candidate1 = old_path_metrics[5] + input_symbol_imag - increment[7];
pm_candidate2 = old_path_metrics[13] + input_symbol_imag + increment[0];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[10] = pm_candidate1;
trans_table[sample_nr][10] = -1.0;
}
else{
new_path_metrics[10] = pm_candidate2;
trans_table[sample_nr][10] = 1.0;
}
pm_candidate1 = old_path_metrics[5] - input_symbol_imag + increment[7];
pm_candidate2 = old_path_metrics[13] - input_symbol_imag - increment[0];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[11] = pm_candidate1;
trans_table[sample_nr][11] = -1.0;
}
else{
new_path_metrics[11] = pm_candidate2;
trans_table[sample_nr][11] = 1.0;
}
pm_candidate1 = old_path_metrics[6] + input_symbol_imag - increment[4];
pm_candidate2 = old_path_metrics[14] + input_symbol_imag + increment[3];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[12] = pm_candidate1;
trans_table[sample_nr][12] = -1.0;
}
else{
new_path_metrics[12] = pm_candidate2;
trans_table[sample_nr][12] = 1.0;
}
pm_candidate1 = old_path_metrics[6] - input_symbol_imag + increment[4];
pm_candidate2 = old_path_metrics[14] - input_symbol_imag - increment[3];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[13] = pm_candidate1;
trans_table[sample_nr][13] = -1.0;
}
else{
new_path_metrics[13] = pm_candidate2;
trans_table[sample_nr][13] = 1.0;
}
pm_candidate1 = old_path_metrics[7] + input_symbol_imag - increment[5];
pm_candidate2 = old_path_metrics[15] + input_symbol_imag + increment[2];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[14] = pm_candidate1;
trans_table[sample_nr][14] = -1.0;
}
else{
new_path_metrics[14] = pm_candidate2;
trans_table[sample_nr][14] = 1.0;
}
pm_candidate1 = old_path_metrics[7] - input_symbol_imag + increment[5];
pm_candidate2 = old_path_metrics[15] - input_symbol_imag - increment[2];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[15] = pm_candidate1;
trans_table[sample_nr][15] = -1.0;
}
else{
new_path_metrics[15] = pm_candidate2;
trans_table[sample_nr][15] = 1.0;
}
tmp=old_path_metrics;
old_path_metrics=new_path_metrics;
new_path_metrics=tmp;
sample_nr++;
if(sample_nr==samples_num)
break;
//Processing real states
real_imag=0;
input_symbol_real = input[sample_nr].real();
pm_candidate1 = old_path_metrics[0] - input_symbol_real - increment[7];
pm_candidate2 = old_path_metrics[8] - input_symbol_real + increment[0];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[0] = pm_candidate1;
trans_table[sample_nr][0] = -1.0;
}
else{
new_path_metrics[0] = pm_candidate2;
trans_table[sample_nr][0] = 1.0;
}
pm_candidate1 = old_path_metrics[0] + input_symbol_real + increment[7];
pm_candidate2 = old_path_metrics[8] + input_symbol_real - increment[0];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[1] = pm_candidate1;
trans_table[sample_nr][1] = -1.0;
}
else{
new_path_metrics[1] = pm_candidate2;
trans_table[sample_nr][1] = 1.0;
}
pm_candidate1 = old_path_metrics[1] - input_symbol_real - increment[6];
pm_candidate2 = old_path_metrics[9] - input_symbol_real + increment[1];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[2] = pm_candidate1;
trans_table[sample_nr][2] = -1.0;
}
else{
new_path_metrics[2] = pm_candidate2;
trans_table[sample_nr][2] = 1.0;
}
pm_candidate1 = old_path_metrics[1] + input_symbol_real + increment[6];
pm_candidate2 = old_path_metrics[9] + input_symbol_real - increment[1];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[3] = pm_candidate1;
trans_table[sample_nr][3] = -1.0;
}
else{
new_path_metrics[3] = pm_candidate2;
trans_table[sample_nr][3] = 1.0;
}
pm_candidate1 = old_path_metrics[2] - input_symbol_real - increment[5];
pm_candidate2 = old_path_metrics[10] - input_symbol_real + increment[2];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[4] = pm_candidate1;
trans_table[sample_nr][4] = -1.0;
}
else{
new_path_metrics[4] = pm_candidate2;
trans_table[sample_nr][4] = 1.0;
}
pm_candidate1 = old_path_metrics[2] + input_symbol_real + increment[5];
pm_candidate2 = old_path_metrics[10] + input_symbol_real - increment[2];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[5] = pm_candidate1;
trans_table[sample_nr][5] = -1.0;
}
else{
new_path_metrics[5] = pm_candidate2;
trans_table[sample_nr][5] = 1.0;
}
pm_candidate1 = old_path_metrics[3] - input_symbol_real - increment[4];
pm_candidate2 = old_path_metrics[11] - input_symbol_real + increment[3];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[6] = pm_candidate1;
trans_table[sample_nr][6] = -1.0;
}
else{
new_path_metrics[6] = pm_candidate2;
trans_table[sample_nr][6] = 1.0;
}
pm_candidate1 = old_path_metrics[3] + input_symbol_real + increment[4];
pm_candidate2 = old_path_metrics[11] + input_symbol_real - increment[3];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[7] = pm_candidate1;
trans_table[sample_nr][7] = -1.0;
}
else{
new_path_metrics[7] = pm_candidate2;
trans_table[sample_nr][7] = 1.0;
}
pm_candidate1 = old_path_metrics[4] - input_symbol_real - increment[3];
pm_candidate2 = old_path_metrics[12] - input_symbol_real + increment[4];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[8] = pm_candidate1;
trans_table[sample_nr][8] = -1.0;
}
else{
new_path_metrics[8] = pm_candidate2;
trans_table[sample_nr][8] = 1.0;
}
pm_candidate1 = old_path_metrics[4] + input_symbol_real + increment[3];
pm_candidate2 = old_path_metrics[12] + input_symbol_real - increment[4];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[9] = pm_candidate1;
trans_table[sample_nr][9] = -1.0;
}
else{
new_path_metrics[9] = pm_candidate2;
trans_table[sample_nr][9] = 1.0;
}
pm_candidate1 = old_path_metrics[5] - input_symbol_real - increment[2];
pm_candidate2 = old_path_metrics[13] - input_symbol_real + increment[5];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[10] = pm_candidate1;
trans_table[sample_nr][10] = -1.0;
}
else{
new_path_metrics[10] = pm_candidate2;
trans_table[sample_nr][10] = 1.0;
}
pm_candidate1 = old_path_metrics[5] + input_symbol_real + increment[2];
pm_candidate2 = old_path_metrics[13] + input_symbol_real - increment[5];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[11] = pm_candidate1;
trans_table[sample_nr][11] = -1.0;
}
else{
new_path_metrics[11] = pm_candidate2;
trans_table[sample_nr][11] = 1.0;
}
pm_candidate1 = old_path_metrics[6] - input_symbol_real - increment[1];
pm_candidate2 = old_path_metrics[14] - input_symbol_real + increment[6];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[12] = pm_candidate1;
trans_table[sample_nr][12] = -1.0;
}
else{
new_path_metrics[12] = pm_candidate2;
trans_table[sample_nr][12] = 1.0;
}
pm_candidate1 = old_path_metrics[6] + input_symbol_real + increment[1];
pm_candidate2 = old_path_metrics[14] + input_symbol_real - increment[6];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[13] = pm_candidate1;
trans_table[sample_nr][13] = -1.0;
}
else{
new_path_metrics[13] = pm_candidate2;
trans_table[sample_nr][13] = 1.0;
}
pm_candidate1 = old_path_metrics[7] - input_symbol_real - increment[0];
pm_candidate2 = old_path_metrics[15] - input_symbol_real + increment[7];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[14] = pm_candidate1;
trans_table[sample_nr][14] = -1.0;
}
else{
new_path_metrics[14] = pm_candidate2;
trans_table[sample_nr][14] = 1.0;
}
pm_candidate1 = old_path_metrics[7] + input_symbol_real + increment[0];
pm_candidate2 = old_path_metrics[15] + input_symbol_real - increment[7];
if(pm_candidate1 > pm_candidate2){
new_path_metrics[15] = pm_candidate1;
trans_table[sample_nr][15] = -1.0;
}
else{
new_path_metrics[15] = pm_candidate2;
trans_table[sample_nr][15] = 1.0;
}
tmp=old_path_metrics;
old_path_metrics=new_path_metrics;
new_path_metrics=tmp;
sample_nr++;
}
/*
* Find the best from the stop states by comparing their path metrics.
* Not every stop state is always possible, so we are searching in
* a subset of them.
*/
unsigned int best_stop_state;
float stop_state_metric, max_stop_state_metric;
best_stop_state = stop_states[0];
max_stop_state_metric = old_path_metrics[best_stop_state];
for(i=1; i< stops_num; i++){
stop_state_metric = old_path_metrics[stop_states[i]];
if(stop_state_metric > max_stop_state_metric){
max_stop_state_metric = stop_state_metric;
best_stop_state = stop_states[i];
}
}
/*
* This table was generated with hope that it gives a litle speedup during
* traceback stage.
* Received bit is related to the number of state in the trellis.
* I've numbered states so their parity (number of ones) is related
* to a received bit.
*/
static const unsigned int parity_table[PATHS_NUM] = { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, };
/*
* Table of previous states in the trellis diagram.
* For GMSK modulation every state has two previous states.
* Example:
* previous_state_nr1 = prev_table[current_state_nr][0]
* previous_state_nr2 = prev_table[current_state_nr][1]
*/
static const unsigned int prev_table[PATHS_NUM][2] = { {0,8}, {0,8}, {1,9}, {1,9}, {2,10}, {2,10}, {3,11}, {3,11}, {4,12}, {4,12}, {5,13}, {5,13}, {6,14}, {6,14}, {7,15}, {7,15}, };
/*
* Traceback and differential decoding of received sequence.
* Decisions stored in trans_table are used to restore best path in the trellis.
*/
sample_nr=samples_num;
unsigned int state_nr=best_stop_state;
unsigned int decision;
bool out_bit=0;
while(sample_nr>0){
sample_nr--;
decision = (trans_table[sample_nr][state_nr]>0);
if(decision != out_bit)
output[sample_nr]=-trans_table[sample_nr][state_nr];
else
output[sample_nr]=trans_table[sample_nr][state_nr];
out_bit = out_bit ^ real_imag ^ parity_table[state_nr];
state_nr = prev_table[state_nr][decision];
real_imag = !real_imag;
}
}

63
lib/viterbi_detector.h Normal file
View File

@ -0,0 +1,63 @@
/* -*- c++ -*- */
/*
* @file
* @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
* @section LICENSE
*
* 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 3, 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
/*
* viterbi_detector:
* This part does the detection of received sequnece.
* Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
* At this moment it gives hard decisions on the output, but
* it was designed with soft decisions in mind.
*
* SYNTAX: void viterbi_detector(
* const gr_complex * input,
* unsigned int samples_num,
* gr_complex * rhh,
* unsigned int start_state,
* const unsigned int * stop_states,
* unsigned int stops_num,
* float * output)
*
* INPUT: input: Complex received signal afted matched filtering.
* samples_num: Number of samples in the input table.
* rhh: The autocorrelation of the estimated channel
* impulse response.
* start_state: Number of the start point. In GSM each burst
* starts with sequence of three bits (0,0,0) which
* indicates start point of the algorithm.
* stop_states: Table with numbers of possible stop states.
* stops_num: Number of possible stop states
*
*
* OUTPUT: output: Differentially decoded hard output of the algorithm:
* -1 for logical "0" and 1 for logical "1"
*
* SUB_FUNC: none
*
* TEST(S): Tested with real world normal burst.
*/
#ifndef INCLUDED_VITERBI_DETECTOR_H
#define INCLUDED_VITERBI_DETECTOR_H
void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output);
#endif /* INCLUDED_VITERBI_DETECTOR_H */

45
python/CMakeLists.txt Normal file
View File

@ -0,0 +1,45 @@
# Copyright 2011 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.
########################################################################
# Include python install macros
########################################################################
include(GrPython)
if(NOT PYTHONINTERP_FOUND)
return()
endif()
########################################################################
# Install python sources
########################################################################
GR_PYTHON_INSTALL(
FILES
__init__.py
receiver_hier.py DESTINATION ${GR_PYTHON_DIR}/gsm
)
########################################################################
# Handle the unit tests
########################################################################
include(GrTest)
set(GR_TEST_TARGET_DEPS gnuradio-gsm)
set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig)
GR_ADD_TEST(qa_receiver ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_receiver.py)
GR_ADD_TEST(qa_receiver_hier ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_receiver_hier.py)

55
python/__init__.py Normal file
View File

@ -0,0 +1,55 @@
#
# Copyright 2008,2009 Free Software Foundation, Inc.
#
# This application 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.
#
# This application 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.
#
# The presence of this file turns this directory into a Python package
'''
This is the GNU Radio GSM module. Place your Python package
description here (python/__init__.py).
'''
# ----------------------------------------------------------------
# Temporary workaround for ticket:181 (swig+python problem)
import sys
_RTLD_GLOBAL = 0
try:
from dl import RTLD_GLOBAL as _RTLD_GLOBAL
except ImportError:
try:
from DLFCN import RTLD_GLOBAL as _RTLD_GLOBAL
except ImportError:
pass
if _RTLD_GLOBAL != 0:
_dlopenflags = sys.getdlopenflags()
sys.setdlopenflags(_dlopenflags|_RTLD_GLOBAL)
# ----------------------------------------------------------------
# import swig generated symbols into the gsm namespace
from gsm_swig import *
# import any pure python here
from receiver_hier import receiver_hier
#
# ----------------------------------------------------------------
# Tail of workaround
if _RTLD_GLOBAL != 0:
sys.setdlopenflags(_dlopenflags) # Restore original flags
# ----------------------------------------------------------------

226
python/build_utils.py Normal file
View File

@ -0,0 +1,226 @@
#
# Copyright 2004,2009,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.
#
"""Misc utilities used at build time
"""
import re, os, os.path
from build_utils_codes import *
# set srcdir to the directory that contains Makefile.am
try:
srcdir = os.environ['srcdir']
except KeyError, e:
srcdir = "."
srcdir = srcdir + '/'
# set do_makefile to either true or false dependeing on the environment
try:
if os.environ['do_makefile'] == '0':
do_makefile = False
else:
do_makefile = True
except KeyError, e:
do_makefile = False
# set do_sources to either true or false dependeing on the environment
try:
if os.environ['do_sources'] == '0':
do_sources = False
else:
do_sources = True
except KeyError, e:
do_sources = True
name_dict = {}
def log_output_name (name):
(base, ext) = os.path.splitext (name)
ext = ext[1:] # drop the leading '.'
entry = name_dict.setdefault (ext, [])
entry.append (name)
def open_and_log_name (name, dir):
global do_sources
if do_sources:
f = open (name, dir)
else:
f = None
log_output_name (name)
return f
def expand_template (d, template_filename, extra = ""):
'''Given a dictionary D and a TEMPLATE_FILENAME, expand template into output file
'''
global do_sources
output_extension = extract_extension (template_filename)
template = open_src (template_filename, 'r')
output_name = d['NAME'] + extra + '.' + output_extension
log_output_name (output_name)
if do_sources:
output = open (output_name, 'w')
do_substitution (d, template, output)
output.close ()
template.close ()
def output_glue (dirname):
output_makefile_fragment ()
output_ifile_include (dirname)
def output_makefile_fragment ():
global do_makefile
if not do_makefile:
return
# overwrite the source, which must be writable; this should have been
# checked for beforehand in the top-level Makefile.gen.gen .
f = open (os.path.join (os.environ.get('gendir', os.environ.get('srcdir', '.')), 'Makefile.gen'), 'w')
f.write ('#\n# This file is machine generated. All edits will be overwritten\n#\n')
output_subfrag (f, 'h')
output_subfrag (f, 'i')
output_subfrag (f, 'cc')
f.close ()
def output_ifile_include (dirname):
global do_sources
if do_sources:
f = open ('%s_generated.i' % (dirname,), 'w')
f.write ('//\n// This file is machine generated. All edits will be overwritten\n//\n')
files = name_dict.setdefault ('i', [])
files.sort ()
f.write ('%{\n')
for file in files:
f.write ('#include <%s>\n' % (file[0:-1] + 'h',))
f.write ('%}\n\n')
for file in files:
f.write ('%%include <%s>\n' % (file,))
def output_subfrag (f, ext):
files = name_dict.setdefault (ext, [])
files.sort ()
f.write ("GENERATED_%s =" % (ext.upper ()))
for file in files:
f.write (" \\\n\t%s" % (file,))
f.write ("\n\n")
def extract_extension (template_name):
# template name is something like: GrFIRfilterXXX.h.t
# we return everything between the penultimate . and .t
mo = re.search (r'\.([a-z]+)\.t$', template_name)
if not mo:
raise ValueError, "Incorrectly formed template_name '%s'" % (template_name,)
return mo.group (1)
def open_src (name, mode):
global srcdir
return open (os.path.join (srcdir, name), mode)
def do_substitution (d, in_file, out_file):
def repl (match_obj):
key = match_obj.group (1)
# print key
return d[key]
inp = in_file.read ()
out = re.sub (r"@([a-zA-Z0-9_]+)@", repl, inp)
out_file.write (out)
copyright = '''/* -*- c++ -*- */
/*
* Copyright 2003,2004 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.
*/
'''
def is_complex (code3):
if i_code (code3) == 'c' or o_code (code3) == 'c':
return '1'
else:
return '0'
def standard_dict (name, code3, package='gr'):
d = {}
d['NAME'] = name
d['NAME_IMPL'] = name+'_impl'
d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper())
d['GUARD_NAME_IMPL'] = 'INCLUDED_%s_%s_IMPL_H' % (package.upper(), name.upper())
d['BASE_NAME'] = re.sub ('^' + package + '_', '', name)
d['SPTR_NAME'] = '%s_sptr' % name
d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten'
d['COPYRIGHT'] = copyright
d['TYPE'] = i_type (code3)
d['I_TYPE'] = i_type (code3)
d['O_TYPE'] = o_type (code3)
d['TAP_TYPE'] = tap_type (code3)
d['IS_COMPLEX'] = is_complex (code3)
return d
def standard_dict2 (name, code3, package):
d = {}
d['NAME'] = name
d['BASE_NAME'] = name
d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper())
d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten'
d['COPYRIGHT'] = copyright
d['TYPE'] = i_type (code3)
d['I_TYPE'] = i_type (code3)
d['O_TYPE'] = o_type (code3)
d['TAP_TYPE'] = tap_type (code3)
d['IS_COMPLEX'] = is_complex (code3)
return d
def standard_impl_dict2 (name, code3, package):
d = {}
d['NAME'] = name
d['IMPL_NAME'] = name
d['BASE_NAME'] = name.rstrip("impl").rstrip("_")
d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper())
d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten'
d['COPYRIGHT'] = copyright
d['FIR_TYPE'] = "fir_filter_" + code3
d['CFIR_TYPE'] = "fir_filter_" + code3[0:2] + 'c'
d['TYPE'] = i_type (code3)
d['I_TYPE'] = i_type (code3)
d['O_TYPE'] = o_type (code3)
d['TAP_TYPE'] = tap_type (code3)
d['IS_COMPLEX'] = is_complex (code3)
return d

View File

@ -0,0 +1,52 @@
#
# Copyright 2004 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.
#
def i_code (code3):
return code3[0]
def o_code (code3):
if len (code3) >= 2:
return code3[1]
else:
return code3[0]
def tap_code (code3):
if len (code3) >= 3:
return code3[2]
else:
return code3[0]
def i_type (code3):
return char_to_type[i_code (code3)]
def o_type (code3):
return char_to_type[o_code (code3)]
def tap_type (code3):
return char_to_type[tap_code (code3)]
char_to_type = {}
char_to_type['s'] = 'short'
char_to_type['i'] = 'int'
char_to_type['f'] = 'float'
char_to_type['c'] = 'gr_complex'
char_to_type['b'] = 'unsigned char'

BIN
python/cfile Normal file

Binary file not shown.

0
python/cfile2.out Normal file
View File

59
python/receiver_hier.py Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env python
import gsm
from gnuradio.eng_option import eng_option
from gnuradio import gr, gru, blocks
from gnuradio import filter
class tuner(gr.feval_dd):
def __init__(self, top_block):
gr.feval_dd.__init__(self)
self.top_block = top_block
def eval(self, freq_offet):
self.top_block.set_center_frequency(freq_offet)
return freq_offet
class receiver_hier(gr.hier_block2):
def __init__(self, input_rate, osr=4):
gr.hier_block2.__init__(self,
"receiver_hier",
gr.io_signature(1, 1, gr.sizeof_gr_complex),
gr.io_signature(1, 1, 142*gr.sizeof_float))
#set rates
gsm_symb_rate = 1625000/6.0
self.input_rate = input_rate
self.osr = osr
self.sps = input_rate / gsm_symb_rate / osr
#create callbacks
self.tuner_callback = tuner(self)
#create accompaning blocks
self.filtr = self._set_filter()
self.interpolator = self._set_interpolator()
self.receiver = self._set_receiver()
self.connect(self, self.filtr, self.interpolator, self.receiver, self)
def _set_filter(self):
filter_cutoff = 125e3
filter_t_width = 10e3
offset = 0
filter_taps = filter.firdes.low_pass(1.0, self.input_rate, filter_cutoff, filter_t_width, filter.firdes.WIN_HAMMING)
filtr = filter.freq_xlating_fir_filter_ccf(1, filter_taps, offset, self.input_rate)
return filtr
def _set_interpolator(self):
interpolator = filter.fractional_resampler_cc(0, self.sps)
return interpolator
def _set_receiver(self):
receiver = gsm.receiver(self.tuner_callback, self.osr)
return receiver
def set_center_frequency(self, center_freq):
self.filtr.set_center_freq(center_freq)
def set_timing(self, timing_offset):
pass

BIN
python/receiver_hier.pyc Normal file

Binary file not shown.

57
swig/CMakeLists.txt Normal file
View File

@ -0,0 +1,57 @@
# Copyright 2011 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.
########################################################################
# Include swig generation macros
########################################################################
find_package(SWIG)
find_package(PythonLibs 2)
if(NOT SWIG_FOUND OR NOT PYTHONLIBS_FOUND)
return()
endif()
include(GrSwig)
include(GrPython)
########################################################################
# Setup swig generation
########################################################################
foreach(incdir ${GNURADIO_RUNTIME_INCLUDE_DIRS})
list(APPEND GR_SWIG_INCLUDE_DIRS ${incdir}/gnuradio/swig)
endforeach(incdir)
set(GR_SWIG_LIBRARIES gnuradio-gsm)
set(GR_SWIG_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/gsm_swig_doc.i)
set(GR_SWIG_DOC_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../include)
GR_SWIG_MAKE(gsm_swig gsm_swig.i)
########################################################################
# Install the build swig module
########################################################################
GR_SWIG_INSTALL(TARGETS gsm_swig DESTINATION ${GR_PYTHON_DIR}/gsm)
########################################################################
# Install swig .i files for development
########################################################################
install(
FILES
gsm_swig.i
${CMAKE_CURRENT_BINARY_DIR}/gsm_swig_doc.i
DESTINATION ${GR_INCLUDE_DIR}/gsm/swig
)

16
swig/gsm_swig.i Normal file
View File

@ -0,0 +1,16 @@
/* -*- c++ -*- */
#define GSM_API
%include "gnuradio.i" // the common stuff
//load generated python docstrings
%include "gsm_swig_doc.i"
%{
#include "gsm/receiver.h"
%}
%include "gsm/receiver.h"
GR_SWIG_BLOCK_MAGIC2(gsm, receiver);