mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'scanner_average_pss'
commit
fe948dc9e8
@ -0,0 +1,60 @@
|
|||||||
|
# BuildMex.cmake
|
||||||
|
# Author: Kent Williams norman-k-williams at uiowa.edu
|
||||||
|
# Modified by Ismael Gomez, 2014
|
||||||
|
|
||||||
|
include(CMakeParseArguments)
|
||||||
|
|
||||||
|
|
||||||
|
if(NOT MATLAB_FOUND)
|
||||||
|
find_package(MATLAB)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT OCTAVE_FOUND)
|
||||||
|
find_package(OCTAVE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# CMake 2.8.12 & earlier apparently don't define the
|
||||||
|
# Mex script path, so find it.
|
||||||
|
if(NOT MATLAB_MEX_PATH)
|
||||||
|
find_program( MATLAB_MEX_PATH mex
|
||||||
|
HINTS ${MATLAB_ROOT}/bin
|
||||||
|
PATHS ${MATLAB_ROOT}/bin
|
||||||
|
DOC "The mex program path"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# BuildMex -- arguments
|
||||||
|
# MEXNAME = root of mex library name
|
||||||
|
# SOURCE = list of source files
|
||||||
|
# LIBRARIES = libraries needed to link mex library
|
||||||
|
FUNCTION(BuildMex)
|
||||||
|
set(oneValueArgs MEXNAME)
|
||||||
|
set(multiValueArgs SOURCES LIBRARIES)
|
||||||
|
cmake_parse_arguments(BuildMex "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
if (MATLAB_FOUND)
|
||||||
|
add_library(${BuildMex_MEXNAME}-mat SHARED ${BuildMex_SOURCES})
|
||||||
|
target_include_directories(${BuildMex_MEXNAME}-mat PUBLIC ${MATLAB_INCLUDE_DIR})
|
||||||
|
set_target_properties(${BuildMex_MEXNAME}-mat PROPERTIES
|
||||||
|
SUFFIX "${MATLAB_MEX_EXTENSION}"
|
||||||
|
PREFIX "liblte_"
|
||||||
|
OUTPUT_NAME "${BuildMex_MEXNAME}"
|
||||||
|
COMPILE_FLAGS "-fvisibility=default ${MATLAB_MEX_CFLAGS}"
|
||||||
|
)
|
||||||
|
target_link_libraries(${BuildMex_MEXNAME}-mat ${BuildMex_LIBRARIES} ${MATLAB_MEX_LIBRARY})
|
||||||
|
install(TARGETS ${BuildMex_MEXNAME}-mat DESTINATION "${MEX_DIR}/liblte/")
|
||||||
|
endif(MATLAB_FOUND)
|
||||||
|
if (OCTAVE_FOUND)
|
||||||
|
add_library(${BuildMex_MEXNAME}-oct SHARED ${BuildMex_SOURCES})
|
||||||
|
target_include_directories(${BuildMex_MEXNAME}-oct PUBLIC ${OCTAVE_INCLUDE_DIR})
|
||||||
|
set_target_properties(${BuildMex_MEXNAME}-oct PROPERTIES
|
||||||
|
SUFFIX ".${OCTAVE_MEXFILE_EXT}"
|
||||||
|
PREFIX "liblte_"
|
||||||
|
OUTPUT_NAME "${BuildMex_MEXNAME}"
|
||||||
|
COMPILE_FLAGS "-fvisibility=default ${OCTAVE_MEX_CFLAGS} -DUNDEF_BOOL"
|
||||||
|
)
|
||||||
|
target_link_libraries(${BuildMex_MEXNAME}-oct ${BuildMex_LIBRARIES} ${OCTAVE_LIBRARIES})
|
||||||
|
install(TARGETS ${BuildMex_MEXNAME}-oct DESTINATION "${MEX_DIR}/liblte/")
|
||||||
|
endif (OCTAVE_FOUND)
|
||||||
|
ENDFUNCTION(BuildMex)
|
||||||
|
|
@ -0,0 +1,212 @@
|
|||||||
|
# - this module looks for Matlab
|
||||||
|
# Defines:
|
||||||
|
# MATLAB_INCLUDE_DIR: include path for mex.h, engine.h
|
||||||
|
# MATLAB_LIBRARIES: required libraries: libmex, etc
|
||||||
|
# MATLAB_MEX_LIBRARY: path to libmex.lib
|
||||||
|
# MATLAB_MX_LIBRARY: path to libmx.lib
|
||||||
|
# MATLAB_MAT_LIBRARY: path to libmat.lib # added
|
||||||
|
# MATLAB_ENG_LIBRARY: path to libeng.lib
|
||||||
|
# MATLAB_ROOT: path to Matlab's root directory
|
||||||
|
|
||||||
|
# This file is part of Gerardus
|
||||||
|
#
|
||||||
|
# This is a derivative work of file FindMatlab.cmake released with
|
||||||
|
# CMake v2.8, because the original seems to be a bit outdated and
|
||||||
|
# doesn't work with my Windows XP and Visual Studio 10 install
|
||||||
|
#
|
||||||
|
# (Note that the original file does work for Ubuntu Natty)
|
||||||
|
#
|
||||||
|
# Author: Ramon Casero <rcasero at gmail.com>, Tom Doel
|
||||||
|
# Version: 0.2.3
|
||||||
|
# $Rev$
|
||||||
|
# $Date$
|
||||||
|
#
|
||||||
|
# The original file was copied from an Ubuntu Linux install
|
||||||
|
# /usr/share/cmake-2.8/Modules/FindMatlab.cmake
|
||||||
|
|
||||||
|
set(MATLAB_FOUND 0)
|
||||||
|
if(WIN32)
|
||||||
|
# Search for a version of Matlab available, starting from the most modern one to older versions
|
||||||
|
foreach(MATVER "7.14" "7.11" "7.10" "7.9" "7.8" "7.7" "7.6" "7.5" "7.4")
|
||||||
|
if((NOT DEFINED MATLAB_ROOT)
|
||||||
|
OR ("${MATLAB_ROOT}" STREQUAL "")
|
||||||
|
OR ("${MATLAB_ROOT}" STREQUAL "/registry"))
|
||||||
|
get_filename_component(MATLAB_ROOT "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${MATVER};MATLABROOT]"
|
||||||
|
ABSOLUTE)
|
||||||
|
set(MATLAB_VERSION ${MATVER})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Directory name depending on whether the Windows architecture is 32
|
||||||
|
# bit or 64 bit
|
||||||
|
set(CMAKE_SIZEOF_VOID_P 8) # Note: For some wierd reason this variable is undefined in my system...
|
||||||
|
if(CMAKE_SIZEOF_VOID_P MATCHES "4")
|
||||||
|
set(WINDIR "win32")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||||
|
set(WINDIR "win64")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) doesn't indicate a valid platform")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Folder where the MEX libraries are, depending of the Windows compiler
|
||||||
|
if(${CMAKE_GENERATOR} MATCHES "Visual Studio 6")
|
||||||
|
set(MATLAB_LIBRARIES_DIR "${MATLAB_ROOT}/extern/lib/${WINDIR}/microsoft/msvc60")
|
||||||
|
elseif(${CMAKE_GENERATOR} MATCHES "Visual Studio 7")
|
||||||
|
# Assume people are generally using Visual Studio 7.1,
|
||||||
|
# if using 7.0 need to link to: ../extern/lib/${WINDIR}/microsoft/msvc70
|
||||||
|
set(MATLAB_LIBRARIES_DIR "${MATLAB_ROOT}/extern/lib/${WINDIR}/microsoft/msvc71")
|
||||||
|
# set(MATLAB_LIBRARIES_DIR "${MATLAB_ROOT}/extern/lib/${WINDIR}/microsoft/msvc70")
|
||||||
|
elseif(${CMAKE_GENERATOR} MATCHES "Borland")
|
||||||
|
# Assume people are generally using Borland 5.4,
|
||||||
|
# if using 7.0 need to link to ../extern/lib/${WINDIR}/microsoft/msvc70
|
||||||
|
set(MATLAB_LIBRARIES_DIR "${MATLAB_ROOT}/extern/lib/${WINDIR}/microsoft/bcc54")
|
||||||
|
# set(MATLAB_LIBRARIES_DIR "${MATLAB_ROOT}/extern/lib/${WINDIR}/microsoft/bcc50")
|
||||||
|
# set(MATLAB_LIBRARIES_DIR "${MATLAB_ROOT}/extern/lib/${WINDIR}/microsoft/bcc51")
|
||||||
|
elseif(${CMAKE_GENERATOR} MATCHES "Visual Studio*")
|
||||||
|
# If the compiler is Visual Studio, but not any of the specific
|
||||||
|
# versions above, we try our luck with the microsoft directory
|
||||||
|
set(MATLAB_LIBRARIES_DIR "${MATLAB_ROOT}/extern/lib/${WINDIR}/microsoft/")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Generator not compatible: ${CMAKE_GENERATOR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Get paths to the Matlab MEX libraries
|
||||||
|
find_library(MATLAB_MEX_LIBRARY libmex ${MATLAB_LIBRARIES_DIR} )
|
||||||
|
find_library(MATLAB_MX_LIBRARY libm ${MATLAB_LIBRARIES_DIR} )
|
||||||
|
find_library(MATLAB_MAT_LIBRARY libmat ${MATLAB_LIBRARIES_DIR} )
|
||||||
|
find_library(MATLAB_ENG_LIBRARY libeng ${MATLAB_LIBRARIES_DIR} )
|
||||||
|
|
||||||
|
# Get path to the include directory
|
||||||
|
find_path(MATLAB_INCLUDE_DIR "mex.h" "${MATLAB_ROOT}/extern/include" )
|
||||||
|
|
||||||
|
else()
|
||||||
|
|
||||||
|
if((NOT DEFINED MATLAB_ROOT)
|
||||||
|
OR ("${MATLAB_ROOT}" STREQUAL ""))
|
||||||
|
# get path to the Matlab root directory
|
||||||
|
execute_process(
|
||||||
|
COMMAND which matlab
|
||||||
|
COMMAND xargs readlink
|
||||||
|
COMMAND xargs dirname
|
||||||
|
COMMAND xargs dirname
|
||||||
|
COMMAND xargs echo -n
|
||||||
|
OUTPUT_VARIABLE MATLAB_ROOT
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check if this is a Mac
|
||||||
|
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
|
||||||
|
set(LIBRARY_EXTENSION .dylib)
|
||||||
|
|
||||||
|
# If this is a Mac and the attempts to find MATLAB_ROOT have so far failed,
|
||||||
|
# we look in the applications folder
|
||||||
|
if((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL ""))
|
||||||
|
|
||||||
|
# Search for a version of Matlab available, starting from the most modern one to older versions
|
||||||
|
foreach(MATVER "R2013b" "R2013a" "R2012b" "R2012a" "R2011b" "R2011a" "R2010b" "R2010a" "R2009b" "R2009a" "R2008b")
|
||||||
|
if((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL ""))
|
||||||
|
if(EXISTS /Applications/MATLAB_${MATVER}.app)
|
||||||
|
set(MATLAB_ROOT /Applications/MATLAB_${MATVER}.app)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
else()
|
||||||
|
set(LIBRARY_EXTENSION .so)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Get path to the MEX libraries
|
||||||
|
execute_process(
|
||||||
|
#COMMAND find "${MATLAB_ROOT}/extern/lib" -name libmex${LIBRARY_EXTENSION} # Peter
|
||||||
|
COMMAND find "${MATLAB_ROOT}/bin" -name libmex${LIBRARY_EXTENSION} # standard
|
||||||
|
COMMAND xargs echo -n
|
||||||
|
OUTPUT_VARIABLE MATLAB_MEX_LIBRARY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
#COMMAND find "${MATLAB_ROOT}/extern/lib" -name libmx${LIBRARY_EXTENSION} # Peter
|
||||||
|
COMMAND find "${MATLAB_ROOT}/bin" -name libmx${LIBRARY_EXTENSION} # Standard
|
||||||
|
COMMAND xargs echo -n
|
||||||
|
OUTPUT_VARIABLE MATLAB_MX_LIBRARY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
#COMMAND find "${MATLAB_ROOT}/extern/lib" -name libmat${LIBRARY_EXTENSION} # Peter
|
||||||
|
COMMAND find "${MATLAB_ROOT}/bin" -name libmat${LIBRARY_EXTENSION} # Standard
|
||||||
|
COMMAND xargs echo -n
|
||||||
|
OUTPUT_VARIABLE MATLAB_MAT_LIBRARY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
#COMMAND find "${MATLAB_ROOT}/extern/lib" -name libeng${LIBRARY_EXTENSION} # Peter
|
||||||
|
COMMAND find "${MATLAB_ROOT}/bin" -name libeng${LIBRARY_EXTENSION} # Standard
|
||||||
|
COMMAND xargs echo -n
|
||||||
|
OUTPUT_VARIABLE MATLAB_ENG_LIBRARY
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get path to the include directory
|
||||||
|
find_path(MATLAB_INCLUDE_DIR
|
||||||
|
"mex.h"
|
||||||
|
PATHS "${MATLAB_ROOT}/extern/include"
|
||||||
|
)
|
||||||
|
|
||||||
|
find_program( MATLAB_MEX_PATH mex
|
||||||
|
HINTS ${MATLAB_ROOT}/bin
|
||||||
|
PATHS ${MATLAB_ROOT}/bin
|
||||||
|
DOC "The mex program path"
|
||||||
|
)
|
||||||
|
|
||||||
|
find_program( MATLAB_MEXEXT_PATH mexext
|
||||||
|
HINTS ${MATLAB_ROOT}/bin
|
||||||
|
PATHS ${MATLAB_ROOT}/bin
|
||||||
|
DOC "The mexext program path"
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${MATLAB_MEXEXT_PATH}
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
OUTPUT_VARIABLE MATLAB_MEX_EXT
|
||||||
|
)
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# This is common to UNIX and Win32:
|
||||||
|
set(MATLAB_LIBRARIES
|
||||||
|
${MATLAB_MEX_LIBRARY}
|
||||||
|
${MATLAB_MX_LIBRARY}
|
||||||
|
${MATLAB_ENG_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MATLAB_INCLUDE_DIR AND MATLAB_LIBRARIES)
|
||||||
|
set(MATLAB_FOUND 1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 32-bit or 64-bit mex
|
||||||
|
if(WIN32)
|
||||||
|
if (CMAKE_CL_64)
|
||||||
|
SET(MATLAB_MEX_EXTENSION .mexw64)
|
||||||
|
else(CMAKE_CL_64)
|
||||||
|
SET(MATLAB_MEX_EXTENSION .mexw32)
|
||||||
|
endif(CMAKE_CL_64)
|
||||||
|
else(WIN32)
|
||||||
|
if (CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||||
|
SET(MATLAB_MEX_EXTENSION .mexa64)
|
||||||
|
else(CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||||
|
SET(MATLAB_MEX_EXTENSION .mexglx)
|
||||||
|
endif (CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||||
|
endif(WIN32)
|
||||||
|
|
||||||
|
SET(MATLAB_MEX_CFLAGS "-DMATLAB_MEX_FILE -DMX_COMPAT_32")
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
MATLAB_LIBRARIES
|
||||||
|
MATLAB_MEX_LIBRARY
|
||||||
|
MATLAB_MX_LIBRARY
|
||||||
|
MATLAB_ENG_LIBRARY
|
||||||
|
MATLAB_INCLUDE_DIR
|
||||||
|
MATLAB_FOUND
|
||||||
|
MATLAB_ROOT
|
||||||
|
MATLAB_MEX_PATH
|
||||||
|
MATLAB_MEXEXT_PATH
|
||||||
|
MATLAB_MEX_EXT
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,138 @@
|
|||||||
|
# - Try to find a version of Octave and headers/library required by the
|
||||||
|
# used compiler. It determines the right MEX-File extension and add
|
||||||
|
# a macro to help the build of MEX-functions.
|
||||||
|
#
|
||||||
|
# This module defines:
|
||||||
|
# OCTAVE_INCLUDE_DIR: include path for mex.h, mexproto.h
|
||||||
|
# OCTAVE_OCTINTERP_LIBRARY: path to the library octinterp
|
||||||
|
# OCTAVE_OCTAVE_LIBRARY: path to the library octave
|
||||||
|
# OCTAVE_CRUFT_LIBRARY: path to the library cruft
|
||||||
|
# OCTAVE_LIBRARIES: required libraries: octinterp, octave, cruft
|
||||||
|
# OCTAVE_CREATE_MEX: macro to build a MEX-file
|
||||||
|
#
|
||||||
|
# The macro OCTAVE_CREATE_MEX requires in this order:
|
||||||
|
# - function's name which will be called in Octave;
|
||||||
|
# - C/C++ source files;
|
||||||
|
# - third libraries required.
|
||||||
|
|
||||||
|
# Copyright (c) 2009-2013 Arnaud Barré <arnaud.barre@gmail.com>
|
||||||
|
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||||
|
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||||
|
|
||||||
|
IF(OCTAVE_ROOT AND OCTAVE_INCLUDE_DIR AND OCTAVE_LIBRARIES)
|
||||||
|
STRING(COMPARE NOTEQUAL "${OCTAVE_ROOT}" "${OCTAVE_ROOT_LAST}" OCTAVE_CHANGED)
|
||||||
|
IF(OCTAVE_CHANGED)
|
||||||
|
SET(OCTAVE_USE_MINGW32 OCTAVE_USE_MINGW32-NOTFOUND CACHE INTERNAL "")
|
||||||
|
SET(OCTAVE_OCTAVE_LIBRARY OCTAVE_OCTAVE_LIBRARY-NOTFOUND CACHE INTERNAL "")
|
||||||
|
SET(OCTAVE_INCLUDE_DIR OCTAVE_INCLUDE_DIR-NOTFOUND CACHE INTERNAL "")
|
||||||
|
ELSE(OCTAVE_CHANGED)
|
||||||
|
# in cache already
|
||||||
|
SET(Octave_FIND_QUIETLY TRUE)
|
||||||
|
ENDIF(OCTAVE_CHANGED)
|
||||||
|
ENDIF(OCTAVE_ROOT AND OCTAVE_INCLUDE_DIR AND OCTAVE_LIBRARIES)
|
||||||
|
|
||||||
|
SET(OCTAVE_MEXFILE_EXT mex)
|
||||||
|
|
||||||
|
IF(WIN32)
|
||||||
|
SET(OCTAVE_PATHS_L1 )
|
||||||
|
SET(OCTAVE_PATHS_L2 )
|
||||||
|
# Level 0
|
||||||
|
FILE(GLOB OCTAVE_PATHS_L0 "c:/Octave*")
|
||||||
|
# Level 1
|
||||||
|
FOREACH(_file_ ${OCTAVE_PATHS_L0})
|
||||||
|
FILE(GLOB OCTAVE_PATHS_TEMP "${_file_}/*")
|
||||||
|
SET(OCTAVE_PATHS_L1 ${OCTAVE_PATHS_L1};${OCTAVE_PATHS_TEMP})
|
||||||
|
ENDFOREACH(_file_ OCTAVE_PATHS_L0)
|
||||||
|
# Level 2
|
||||||
|
FOREACH(_file_ ${OCTAVE_PATHS_L1})
|
||||||
|
FILE(GLOB OCTAVE_PATHS_TEMP "${_file_}/*")
|
||||||
|
SET(OCTAVE_PATHS_L2 ${OCTAVE_PATHS_L2};${OCTAVE_PATHS_TEMP})
|
||||||
|
ENDFOREACH(_file_ OCTAVE_PATHS_L1)
|
||||||
|
# Merge levels
|
||||||
|
SET(OCTAVE_PATHS ${OCTAVE_PATHS_L0} ${OCTAVE_PATHS_L1} ${OCTAVE_PATHS_L2})
|
||||||
|
|
||||||
|
FIND_PATH(OCTAVE_ROOT "bin/octave.exe" ${OCTAVE_PATHS})
|
||||||
|
FIND_PATH(OCTAVE_USE_MINGW32 "bin/mingw32-make.exe" "${OCTAVE_ROOT}/mingw32")
|
||||||
|
|
||||||
|
IF(MSVC AND OCTAVE_USE_MINGW32)
|
||||||
|
MESSAGE(FATAL_ERROR
|
||||||
|
"You must use the generator \"MinGW Makefiles\" as the "
|
||||||
|
"version of Octave installed on your computer was compiled "
|
||||||
|
"with MinGW. You should also specify the native compiler "
|
||||||
|
"(GCC, G++ and GFortan) and add the path of MinGW in the "
|
||||||
|
"environment variable PATH. Contact the developers of the "
|
||||||
|
"project for more details")
|
||||||
|
ENDIF(MSVC AND OCTAVE_USE_MINGW32)
|
||||||
|
|
||||||
|
FILE(GLOB OCTAVE_INCLUDE_PATHS "${OCTAVE_ROOT}/include/octave-*/octave")
|
||||||
|
FILE(GLOB OCTAVE_LIBRARIES_PATHS "${OCTAVE_ROOT}/lib/octave-*")
|
||||||
|
IF (NOT OCTAVE_LIBRARIES_PATHS)
|
||||||
|
FILE(GLOB OCTAVE_LIBRARIES_PATHS "${OCTAVE_ROOT}/lib/octave/*")
|
||||||
|
ENDIF (NOT OCTAVE_LIBRARIES_PATHS)
|
||||||
|
|
||||||
|
# LIBOCTINTERP, LIBOCTAVE, LIBCRUFT names
|
||||||
|
SET(LIBOCTAVE "liboctave")
|
||||||
|
|
||||||
|
ELSE(WIN32)
|
||||||
|
IF(APPLE)
|
||||||
|
FILE(GLOB OCTAVE_PATHS "/Applications/Octave*")
|
||||||
|
FIND_PATH(OCTAVE_ROOT "Contents/Resources/bin/octave" ${OCTAVE_PATHS})
|
||||||
|
|
||||||
|
FILE(GLOB OCTAVE_INCLUDE_PATHS "${OCTAVE_ROOT}/Contents/Resources/include/octave-*/octave")
|
||||||
|
FILE(GLOB OCTAVE_LIBRARIES_PATHS "${OCTAVE_ROOT}/Contents/Resources/lib/octave-*")
|
||||||
|
|
||||||
|
SET(LIBOCTAVE "liboctave.dylib")
|
||||||
|
ELSE(APPLE)
|
||||||
|
FILE(GLOB OCTAVE_LOCAL_PATHS "/usr/local/lib/octave-*")
|
||||||
|
FILE(GLOB OCTAVE_USR_PATHS "/usr/lib/octave-*")
|
||||||
|
FILE(GLOB OCTAVE_INCLUDE_PATHS "/usr/include/octave-*")
|
||||||
|
|
||||||
|
SET (OCTAVE_INCLUDE_PATHS
|
||||||
|
"/usr/local/include"
|
||||||
|
"/usr/local/include/octave"
|
||||||
|
"/usr/include"
|
||||||
|
"${OCTAVE_INCLUDE_PATHS}"
|
||||||
|
"${OCTAVE_INCLUDE_PATHS}/octave")
|
||||||
|
SET (OCTAVE_LIBRARIES_PATHS
|
||||||
|
"/usr/local/lib"
|
||||||
|
"/usr/local/lib/octave"
|
||||||
|
${OCTAVE_LOCAL_PATHS}
|
||||||
|
"/usr/lib"
|
||||||
|
"/usr/lib/octave"
|
||||||
|
${OCTAVE_USR_PATHS})
|
||||||
|
|
||||||
|
SET (LIBOCTAVE "octave")
|
||||||
|
ENDIF(APPLE)
|
||||||
|
ENDIF(WIN32)
|
||||||
|
|
||||||
|
FIND_LIBRARY(OCTAVE_OCTAVE_LIBRARY
|
||||||
|
${LIBOCTAVE}
|
||||||
|
${OCTAVE_LIBRARIES_PATHS}
|
||||||
|
)
|
||||||
|
FIND_PATH(OCTAVE_INCLUDE_DIR
|
||||||
|
"mex.h"
|
||||||
|
${OCTAVE_INCLUDE_PATHS}
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(OCTAVE_ROOT_LAST "${OCTAVE_ROOT}" CACHE INTERNAL "" FORCE)
|
||||||
|
|
||||||
|
# This is common to UNIX and Win32:
|
||||||
|
SET(OCTAVE_LIBRARIES
|
||||||
|
${OCTAVE_OCTAVE_LIBRARY}
|
||||||
|
CACHE INTERNAL "Octave libraries" FORCE
|
||||||
|
)
|
||||||
|
|
||||||
|
INCLUDE(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
# The variable OCTAVE_ROOT is only relevant for WIN32
|
||||||
|
IF(WIN32)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Octave DEFAULT_MSG OCTAVE_ROOT OCTAVE_INCLUDE_DIR OCTAVE_OCTAVE_LIBRARY )
|
||||||
|
ELSE(WIN32)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Octave DEFAULT_MSG OCTAVE_INCLUDE_DIR OCTAVE_OCTAVE_LIBRARY )
|
||||||
|
ENDIF(WIN32)
|
||||||
|
|
||||||
|
MARK_AS_ADVANCED(
|
||||||
|
OCTAVE_OCTAVE_LIBRARY
|
||||||
|
OCTAVE_LIBRARIES
|
||||||
|
OCTAVE_INCLUDE_DIR
|
||||||
|
)
|
@ -0,0 +1,344 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "liblte/rrc/rrc.h"
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
#include "liblte/cuhd/cuhd.h"
|
||||||
|
#include "cuhd_utils.h"
|
||||||
|
|
||||||
|
#define B210_DEFAULT_GAIN 40.0
|
||||||
|
#define B210_DEFAULT_GAIN_CORREC 110.0 // Gain of the Rx chain when the gain is set to 40
|
||||||
|
|
||||||
|
float gain_offset = B210_DEFAULT_GAIN_CORREC;
|
||||||
|
|
||||||
|
cell_search_cfg_t cell_detect_config = {
|
||||||
|
5000, // maximum number of frames to receive for MIB decoding
|
||||||
|
50, // maximum number of frames to receive for PSS correlation
|
||||||
|
16.0 // early-stops cell detection if mean PSR is above this value
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* Program arguments processing
|
||||||
|
***********************************************************************/
|
||||||
|
typedef struct {
|
||||||
|
int nof_subframes;
|
||||||
|
bool disable_plots;
|
||||||
|
int force_N_id_2;
|
||||||
|
char *uhd_args;
|
||||||
|
float uhd_freq;
|
||||||
|
float uhd_gain;
|
||||||
|
}prog_args_t;
|
||||||
|
|
||||||
|
void args_default(prog_args_t *args) {
|
||||||
|
args->nof_subframes = -1;
|
||||||
|
args->force_N_id_2 = -1; // Pick the best
|
||||||
|
args->uhd_args = "";
|
||||||
|
args->uhd_freq = -1.0;
|
||||||
|
args->uhd_gain = B210_DEFAULT_GAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(prog_args_t *args, char *prog) {
|
||||||
|
printf("Usage: %s [aglnv] -f rx_frequency (in Hz)\n", prog);
|
||||||
|
printf("\t-a UHD args [Default %s]\n", args->uhd_args);
|
||||||
|
printf("\t-g UHD RX gain [Default %.2f dB]\n", args->uhd_gain);
|
||||||
|
printf("\t-l Force N_id_2 [Default best]\n");
|
||||||
|
printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes);
|
||||||
|
printf("\t-v [set verbose to debug, default none]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_args(prog_args_t *args, int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
args_default(args);
|
||||||
|
while ((opt = getopt(argc, argv, "aglnvf")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'a':
|
||||||
|
args->uhd_args = argv[optind];
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
args->uhd_gain = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
args->uhd_freq = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
args->nof_subframes = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
args->force_N_id_2 = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(args, argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (args->uhd_freq < 0) {
|
||||||
|
usage(args, argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/* TODO: Do something with the output data */
|
||||||
|
uint8_t data[10000], data_unpacked[1000];
|
||||||
|
|
||||||
|
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) {
|
||||||
|
DEBUG(" ---- Receive %d samples ---- \n", nsamples);
|
||||||
|
return cuhd_recv(h, data, nsamples, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern float mean_exec_time;
|
||||||
|
|
||||||
|
enum receiver_state { DECODE_MIB, DECODE_SIB, MEASURE} state;
|
||||||
|
|
||||||
|
#define MAX_SINFO 10
|
||||||
|
#define MAX_NEIGHBOUR_CELLS 128
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int ret;
|
||||||
|
cf_t *sf_buffer;
|
||||||
|
prog_args_t prog_args;
|
||||||
|
lte_cell_t cell;
|
||||||
|
int64_t sf_cnt;
|
||||||
|
ue_sync_t ue_sync;
|
||||||
|
ue_mib_t ue_mib;
|
||||||
|
void *uhd;
|
||||||
|
ue_dl_t ue_dl;
|
||||||
|
lte_fft_t fft;
|
||||||
|
chest_dl_t chest;
|
||||||
|
uint32_t nframes=0;
|
||||||
|
uint32_t nof_trials = 0;
|
||||||
|
uint32_t sfn = 0; // system frame number
|
||||||
|
int n;
|
||||||
|
uint8_t bch_payload[BCH_PAYLOAD_LEN], bch_payload_unpacked[BCH_PAYLOAD_LEN];
|
||||||
|
uint32_t sfn_offset;
|
||||||
|
float rssi_utra=0,rssi=0, rsrp=0, rsrq=0, snr=0;
|
||||||
|
cf_t *ce[MAX_PORTS];
|
||||||
|
|
||||||
|
if (parse_args(&prog_args, argc, argv)) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Opening UHD device...\n");
|
||||||
|
if (cuhd_open(prog_args.uhd_args, &uhd)) {
|
||||||
|
fprintf(stderr, "Error opening uhd\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* Set receiver gain */
|
||||||
|
cuhd_set_rx_gain(uhd, prog_args.uhd_gain);
|
||||||
|
|
||||||
|
/* set receiver frequency */
|
||||||
|
cuhd_set_rx_freq(uhd, (double) prog_args.uhd_freq);
|
||||||
|
cuhd_rx_wait_lo_locked(uhd);
|
||||||
|
printf("Tunning receiver to %.3f MHz\n", (double ) prog_args.uhd_freq/1000000);
|
||||||
|
|
||||||
|
ret = cuhd_search_and_decode_mib(uhd, &cell_detect_config, prog_args.force_N_id_2, &cell);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error searching cell\n");
|
||||||
|
return -1;
|
||||||
|
} else if (ret == 0) {
|
||||||
|
printf("Cell not found\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set sampling frequency */
|
||||||
|
int srate = lte_sampling_freq_hz(cell.nof_prb);
|
||||||
|
if (srate != -1) {
|
||||||
|
cuhd_set_rx_srate(uhd, (double) srate);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Invalid number of PRB %d\n", cell.nof_prb);
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Stopping UHD and flushing buffer...\n",0);
|
||||||
|
cuhd_stop_rx_stream(uhd);
|
||||||
|
cuhd_flush_buffer(uhd);
|
||||||
|
|
||||||
|
if (ue_sync_init(&ue_sync, cell, cuhd_recv_wrapper, uhd)) {
|
||||||
|
fprintf(stderr, "Error initiating ue_sync\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ue_dl_init(&ue_dl, cell, 1234)) {
|
||||||
|
fprintf(stderr, "Error initiating UE downlink processing module\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ue_mib_init(&ue_mib, cell)) {
|
||||||
|
fprintf(stderr, "Error initaiting UE MIB decoder\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure downlink receiver for the SI-RNTI since will be the only one we'll use */
|
||||||
|
ue_dl_set_rnti(&ue_dl, SIRNTI);
|
||||||
|
|
||||||
|
/* Initialize subframe counter */
|
||||||
|
sf_cnt = 0;
|
||||||
|
|
||||||
|
if (lte_fft_init(&fft, cell.cp, cell.nof_prb)) {
|
||||||
|
fprintf(stderr, "Error initiating FFT\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (chest_dl_init(&chest, cell)) {
|
||||||
|
fprintf(stderr, "Error initiating channel estimator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sf_re = SF_LEN_RE(cell.nof_prb, cell.cp);
|
||||||
|
|
||||||
|
cf_t *sf_symbols = vec_malloc(sf_re * sizeof(cf_t));
|
||||||
|
|
||||||
|
for (int i=0;i<MAX_PORTS;i++) {
|
||||||
|
ce[i] = vec_malloc(sizeof(cf_t) * sf_re);
|
||||||
|
}
|
||||||
|
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
/* Main loop */
|
||||||
|
while (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1) {
|
||||||
|
|
||||||
|
ret = ue_sync_get_buffer(&ue_sync, &sf_buffer);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error calling ue_sync_work()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ue_sync_get_buffer returns 1 if successfully read 1 aligned subframe */
|
||||||
|
if (ret == 1) {
|
||||||
|
switch (state) {
|
||||||
|
case DECODE_MIB:
|
||||||
|
if (ue_sync_get_sfidx(&ue_sync) == 0) {
|
||||||
|
pbch_decode_reset(&ue_mib.pbch);
|
||||||
|
n = ue_mib_decode(&ue_mib, sf_buffer, bch_payload_unpacked, NULL, &sfn_offset);
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Error decoding UE MIB\n");
|
||||||
|
return -1;
|
||||||
|
} else if (n == MIB_FOUND) {
|
||||||
|
bit_unpack_vector(bch_payload_unpacked, bch_payload, BCH_PAYLOAD_LEN);
|
||||||
|
bcch_bch_unpack(bch_payload, BCH_PAYLOAD_LEN, &cell, &sfn);
|
||||||
|
printf("Decoded MIB. SFN: %d, offset: %d\n", sfn, sfn_offset);
|
||||||
|
sfn = (sfn + sfn_offset)%1024;
|
||||||
|
state = DECODE_SIB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DECODE_SIB:
|
||||||
|
/* We are looking for SI Blocks, search only in appropiate places */
|
||||||
|
if ((ue_sync_get_sfidx(&ue_sync) == 5 && (sfn%2)==0)) {
|
||||||
|
n = ue_dl_decode_sib(&ue_dl, sf_buffer, data, ue_sync_get_sfidx(&ue_sync),
|
||||||
|
((int) ceilf((float)3*(((sfn)/2)%4)/2))%4);
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Error decoding UE DL\n");fflush(stdout);
|
||||||
|
return -1;
|
||||||
|
} else if (n == 0) {
|
||||||
|
printf("CFO: %+6.4f KHz, SFO: %+6.4f Khz, ExecTime: %5.1f us, NOI: %.2f, PDCCH-Det: %.3f\r",
|
||||||
|
ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000,
|
||||||
|
mean_exec_time, pdsch_average_noi(&ue_dl.pdsch),
|
||||||
|
(float) ue_dl.nof_pdcch_detected/nof_trials);
|
||||||
|
nof_trials++;
|
||||||
|
} else {
|
||||||
|
bit_unpack_vector(data, data_unpacked, n);
|
||||||
|
void *dlsch_msg = bcch_dlsch_unpack(data_unpacked, n);
|
||||||
|
if (dlsch_msg) {
|
||||||
|
printf("\n");fflush(stdout);
|
||||||
|
cell_access_info_t cell_info;
|
||||||
|
bcch_dlsch_sib1_get_cell_access_info(dlsch_msg, &cell_info);
|
||||||
|
printf("Decoded SIB1. Cell ID: 0x%x\n", cell_info.cell_id);
|
||||||
|
bcch_dlsch_fprint(dlsch_msg, stdout);
|
||||||
|
state = MEASURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MEASURE:
|
||||||
|
|
||||||
|
if (ue_sync_get_sfidx(&ue_sync) == 5) {
|
||||||
|
/* Run FFT for all subframe data */
|
||||||
|
lte_fft_run_sf(&fft, sf_buffer, sf_symbols);
|
||||||
|
|
||||||
|
chest_dl_estimate(&chest, sf_symbols, ce, ue_sync_get_sfidx(&ue_sync));
|
||||||
|
|
||||||
|
rssi = VEC_CMA(vec_avg_power_cf(sf_buffer,SF_LEN(lte_symbol_sz(cell.nof_prb))),rssi,nframes);
|
||||||
|
rssi_utra = VEC_CMA(chest_dl_get_rssi(&chest),rssi_utra,nframes);
|
||||||
|
rsrq = VEC_EMA(chest_dl_get_rsrq(&chest),rsrq,0.05);
|
||||||
|
rsrp = VEC_EMA(chest_dl_get_rsrp(&chest),rsrp,0.05);
|
||||||
|
snr = VEC_EMA(chest_dl_get_snr(&chest),snr,0.05);
|
||||||
|
nframes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plot and Printf
|
||||||
|
if ((nframes%10) == 0) {
|
||||||
|
printf("CFO: %+8.4f KHz, SFO: %+8.4f Khz, RSSI: %5.1f dBm, RSSI/ref-symbol: %+5.1f dBm, "
|
||||||
|
"RSRP: %+5.1f dBm, RSRQ: %5.1f dB, SNR: %5.1f dB\r",
|
||||||
|
ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000,
|
||||||
|
10*log10(rssi*1000)-gain_offset,
|
||||||
|
10*log10(rssi_utra*1000)-gain_offset,
|
||||||
|
10*log10(rsrp*1000)-gain_offset,
|
||||||
|
10*log10(rsrq), 10*log10(snr));
|
||||||
|
if (verbose != VERBOSE_NONE) {
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ue_sync_get_sfidx(&ue_sync) == 9) {
|
||||||
|
sfn++;
|
||||||
|
if (sfn == 1024) {
|
||||||
|
sfn = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ret == 0) {
|
||||||
|
printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r",
|
||||||
|
sync_get_peak_value(&ue_sync.sfind),
|
||||||
|
ue_sync.frame_total_cnt, ue_sync.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sf_cnt++;
|
||||||
|
} // Main loop
|
||||||
|
|
||||||
|
ue_sync_free(&ue_sync);
|
||||||
|
cuhd_close(uhd);
|
||||||
|
printf("\nBye\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,171 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
#include "liblte/rrc/rrc.h"
|
||||||
|
#include "cuhd_utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
#include "liblte/cuhd/cuhd.h"
|
||||||
|
|
||||||
|
int cuhd_recv_wrapper_cs(void *h, void *data, uint32_t nsamples) {
|
||||||
|
DEBUG(" ---- Receive %d samples ---- \n", nsamples);
|
||||||
|
return cuhd_recv(h, data, nsamples, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This function is simply a wrapper to the ue_cell_search module for cuhd devices
|
||||||
|
* Return 1 if the MIB is decoded, 0 if not or -1 on error.
|
||||||
|
*/
|
||||||
|
int cuhd_mib_decoder(void *uhd, uint32_t max_nof_frames, lte_cell_t *cell) {
|
||||||
|
int ret = LIBLTE_ERROR;
|
||||||
|
ue_mib_sync_t ue_mib;
|
||||||
|
uint8_t bch_payload[BCH_PAYLOAD_LEN], bch_payload_unpacked[BCH_PAYLOAD_LEN];
|
||||||
|
|
||||||
|
if (ue_mib_sync_init(&ue_mib, cell->id, cell->cp, cuhd_recv_wrapper_cs, uhd)) {
|
||||||
|
fprintf(stderr, "Error initiating ue_mib_sync\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srate = lte_sampling_freq_hz(MIB_NOF_PRB);
|
||||||
|
INFO("Setting sampling frequency %.2f MHz for PSS search\n", (float) srate/1000000);
|
||||||
|
cuhd_set_rx_srate(uhd, (float) srate);
|
||||||
|
|
||||||
|
INFO("Starting receiver...\n", 0);
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
/* Find and decody MIB */
|
||||||
|
ret = ue_mib_sync_decode(&ue_mib, max_nof_frames, bch_payload, &cell->nof_ports, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error decoding MIB\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
if (ret == 1) {
|
||||||
|
bit_unpack_vector(bch_payload, bch_payload_unpacked, BCH_PAYLOAD_LEN);
|
||||||
|
bcch_bch_unpack(bch_payload_unpacked, BCH_PAYLOAD_LEN, cell, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
|
||||||
|
cuhd_stop_rx_stream(uhd);
|
||||||
|
ue_mib_sync_free(&ue_mib);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This function is simply a wrapper to the ue_cell_search module for cuhd devices
|
||||||
|
*/
|
||||||
|
int cuhd_cell_search(void *uhd, cell_search_cfg_t *config,
|
||||||
|
int force_N_id_2, lte_cell_t *cell)
|
||||||
|
{
|
||||||
|
int ret = LIBLTE_ERROR;
|
||||||
|
ue_cell_search_t cs;
|
||||||
|
ue_cell_search_result_t found_cells[3];
|
||||||
|
|
||||||
|
bzero(found_cells, 3*sizeof(ue_cell_search_result_t));
|
||||||
|
|
||||||
|
if (ue_cell_search_init(&cs, cuhd_recv_wrapper_cs, uhd)) {
|
||||||
|
fprintf(stderr, "Error initiating UE cell detect\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->max_frames_pss) {
|
||||||
|
ue_cell_search_set_nof_frames_to_scan(&cs, config->max_frames_pss);
|
||||||
|
}
|
||||||
|
if (config->threshold) {
|
||||||
|
ue_cell_search_set_threshold(&cs, config->threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Setting sampling frequency %.2f MHz for PSS search\n", CS_SAMP_FREQ/1000000);
|
||||||
|
cuhd_set_rx_srate(uhd, CS_SAMP_FREQ);
|
||||||
|
|
||||||
|
INFO("Starting receiver...\n", 0);
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
/* Find a cell in the given N_id_2 or go through the 3 of them to find the strongest */
|
||||||
|
uint32_t max_peak_cell = 0;
|
||||||
|
if (force_N_id_2 >= 0) {
|
||||||
|
ret = ue_cell_search_scan_N_id_2(&cs, force_N_id_2, &found_cells[force_N_id_2]);
|
||||||
|
max_peak_cell = force_N_id_2;
|
||||||
|
} else {
|
||||||
|
ret = ue_cell_search_scan(&cs, found_cells, &max_peak_cell);
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error searching cell\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
} else if (ret == 0) {
|
||||||
|
fprintf(stderr, "Could not find any cell in this frequency\n");
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save result
|
||||||
|
if (cell) {
|
||||||
|
cell->id = found_cells[max_peak_cell].cell_id;
|
||||||
|
cell->cp = found_cells[max_peak_cell].cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
cuhd_stop_rx_stream(uhd);
|
||||||
|
ue_cell_search_free(&cs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Finds a cell and decodes MIB from the PBCH.
|
||||||
|
* Returns 1 if the cell is found and MIB is decoded successfully.
|
||||||
|
* 0 if no cell was found or MIB could not be decoded,
|
||||||
|
* -1 on error
|
||||||
|
*/
|
||||||
|
int cuhd_search_and_decode_mib(void *uhd, cell_search_cfg_t *config, int force_N_id_2, lte_cell_t *cell)
|
||||||
|
{
|
||||||
|
int ret = LIBLTE_ERROR;
|
||||||
|
|
||||||
|
printf("Searching for cell...\n");
|
||||||
|
ret = cuhd_cell_search(uhd, config, force_N_id_2, cell);
|
||||||
|
if (ret > 0) {
|
||||||
|
printf("Decoding PBCH for cell %d (N_id_2=%d)\n", cell->id, cell->id%3);
|
||||||
|
ret = cuhd_mib_decoder(uhd, config->max_frames_pbch, cell);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Could not decode PBCH from CELL ID %d\n", cell->id);
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
typedef struct LIBLTE_API {
|
||||||
|
uint32_t max_frames_pbch; // maximum number of 5ms frames to capture for MIB decoding
|
||||||
|
uint32_t max_frames_pss; // maximum number of 5ms frames to capture for PSS correlation
|
||||||
|
float threshold; // early-stops cell detection if mean PSR is above this value
|
||||||
|
}cell_search_cfg_t;
|
||||||
|
|
||||||
|
int cuhd_mib_decoder(void *uhd,
|
||||||
|
uint32_t max_nof_frames,
|
||||||
|
lte_cell_t *cell);
|
||||||
|
|
||||||
|
int cuhd_cell_search(void *uhd,
|
||||||
|
cell_search_cfg_t *config,
|
||||||
|
int force_N_id_2,
|
||||||
|
lte_cell_t *cell);
|
||||||
|
|
||||||
|
int cuhd_search_and_decode_mib(void *uhd,
|
||||||
|
cell_search_cfg_t *config,
|
||||||
|
int force_N_id_2,
|
||||||
|
lte_cell_t *cell);
|
||||||
|
|
||||||
|
|
@ -0,0 +1,418 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "liblte/rrc/rrc.h"
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
#include "liblte/cuhd/cuhd.h"
|
||||||
|
#include "cuhd_utils.h"
|
||||||
|
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
void init_plots();
|
||||||
|
void do_plots(ue_dl_t *q, uint32_t sf_idx, ue_sync_t *qs);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define B210_DEFAULT_GAIN 40.0
|
||||||
|
#define B210_DEFAULT_GAIN_CORREC 110.0 // Gain of the Rx chain when the gain is set to 40
|
||||||
|
|
||||||
|
float gain_offset = B210_DEFAULT_GAIN_CORREC;
|
||||||
|
|
||||||
|
|
||||||
|
cell_search_cfg_t cell_detect_config = {
|
||||||
|
5000,
|
||||||
|
100, // nof_frames_total
|
||||||
|
16.0 // threshold
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* Program arguments processing
|
||||||
|
***********************************************************************/
|
||||||
|
typedef struct {
|
||||||
|
int nof_subframes;
|
||||||
|
bool disable_plots;
|
||||||
|
int force_N_id_2;
|
||||||
|
uint16_t rnti;
|
||||||
|
char *uhd_args;
|
||||||
|
float uhd_freq;
|
||||||
|
float uhd_gain;
|
||||||
|
}prog_args_t;
|
||||||
|
|
||||||
|
void args_default(prog_args_t *args) {
|
||||||
|
args->nof_subframes = -1;
|
||||||
|
args->rnti = SIRNTI;
|
||||||
|
args->force_N_id_2 = -1; // Pick the best
|
||||||
|
args->uhd_args = "";
|
||||||
|
args->uhd_freq = -1.0;
|
||||||
|
args->uhd_gain = 60.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(prog_args_t *args, char *prog) {
|
||||||
|
printf("Usage: %s [agldnrv] -f rx_frequency (in Hz)\n", prog);
|
||||||
|
printf("\t-a UHD args [Default %s]\n", args->uhd_args);
|
||||||
|
printf("\t-g UHD RX gain [Default %.2f dB]\n", args->uhd_gain);
|
||||||
|
printf("\t-r RNTI [Default 0x%x]\n",args->rnti);
|
||||||
|
printf("\t-l Force N_id_2 [Default best]\n");
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
printf("\t-d disable plots [Default enabled]\n");
|
||||||
|
#else
|
||||||
|
printf("\t plots are disabled. Graphics library not available\n");
|
||||||
|
#endif
|
||||||
|
printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes);
|
||||||
|
printf("\t-v [set verbose to debug, default none]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(prog_args_t *args, int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
args_default(args);
|
||||||
|
while ((opt = getopt(argc, argv, "agldnvrf")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'a':
|
||||||
|
args->uhd_args = argv[optind];
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
args->uhd_gain = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
args->uhd_freq = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
args->nof_subframes = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
args->rnti = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
args->force_N_id_2 = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
args->disable_plots = true;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(args, argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (args->uhd_freq < 0) {
|
||||||
|
usage(args, argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
/* TODO: Do something with the output data */
|
||||||
|
uint8_t data[10000], data_unpacked[1000];
|
||||||
|
|
||||||
|
bool go_exit = false;
|
||||||
|
|
||||||
|
void sig_int_handler(int signo)
|
||||||
|
{
|
||||||
|
if (signo == SIGINT) {
|
||||||
|
go_exit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) {
|
||||||
|
DEBUG(" ---- Receive %d samples ---- \n", nsamples);
|
||||||
|
return cuhd_recv(h, data, nsamples, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern float mean_exec_time;
|
||||||
|
|
||||||
|
enum receiver_state { DECODE_MIB, DECODE_SIB} state;
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int ret;
|
||||||
|
cf_t *sf_buffer;
|
||||||
|
prog_args_t prog_args;
|
||||||
|
lte_cell_t cell;
|
||||||
|
int64_t sf_cnt;
|
||||||
|
ue_sync_t ue_sync;
|
||||||
|
ue_mib_t ue_mib;
|
||||||
|
void *uhd;
|
||||||
|
ue_dl_t ue_dl;
|
||||||
|
uint32_t nof_trials = 0;
|
||||||
|
uint32_t sfn = 0; // system frame number
|
||||||
|
int n;
|
||||||
|
uint8_t bch_payload[BCH_PAYLOAD_LEN], bch_payload_unpacked[BCH_PAYLOAD_LEN];
|
||||||
|
uint32_t sfn_offset;
|
||||||
|
parse_args(&prog_args, argc, argv);
|
||||||
|
|
||||||
|
printf("Opening UHD device...\n");
|
||||||
|
if (cuhd_open(prog_args.uhd_args, &uhd)) {
|
||||||
|
fprintf(stderr, "Error opening uhd\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
/* Set receiver gain */
|
||||||
|
cuhd_set_rx_gain(uhd, prog_args.uhd_gain);
|
||||||
|
|
||||||
|
/* set receiver frequency */
|
||||||
|
cuhd_set_rx_freq(uhd, (double) prog_args.uhd_freq);
|
||||||
|
cuhd_rx_wait_lo_locked(uhd);
|
||||||
|
printf("Tunning receiver to %.3f MHz\n", (double ) prog_args.uhd_freq/1000000);
|
||||||
|
|
||||||
|
ret = cuhd_search_and_decode_mib(uhd, &cell_detect_config, prog_args.force_N_id_2, &cell);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error searching for cell\n");
|
||||||
|
exit(-1);
|
||||||
|
} else if (ret == 0) {
|
||||||
|
printf("Cell not found\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set sampling frequency */
|
||||||
|
int srate = lte_sampling_freq_hz(cell.nof_prb);
|
||||||
|
if (srate != -1) {
|
||||||
|
cuhd_set_rx_srate(uhd, (double) srate);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Invalid number of PRB %d\n", cell.nof_prb);
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
INFO("Stopping UHD and flushing buffer...\r",0);
|
||||||
|
cuhd_stop_rx_stream(uhd);
|
||||||
|
cuhd_flush_buffer(uhd);
|
||||||
|
|
||||||
|
if (ue_sync_init(&ue_sync, cell, cuhd_recv_wrapper, uhd)) {
|
||||||
|
fprintf(stderr, "Error initiating ue_sync\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (ue_dl_init(&ue_dl, cell, 1234)) { // This is the User RNTI
|
||||||
|
fprintf(stderr, "Error initiating UE downlink processing module\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (ue_mib_init(&ue_mib, cell)) {
|
||||||
|
fprintf(stderr, "Error initaiting UE MIB decoder\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure downlink receiver for the SI-RNTI since will be the only one we'll use */
|
||||||
|
ue_dl_set_rnti(&ue_dl, prog_args.rnti);
|
||||||
|
|
||||||
|
/* Initialize subframe counter */
|
||||||
|
sf_cnt = 0;
|
||||||
|
|
||||||
|
// Register Ctrl+C handler
|
||||||
|
signal(SIGINT, sig_int_handler);
|
||||||
|
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
if (!prog_args.disable_plots) {
|
||||||
|
init_plots();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
// Variables for measurements
|
||||||
|
uint32_t nframes=0;
|
||||||
|
float rsrp=0.0, rsrq=0.0, snr=0.0;
|
||||||
|
|
||||||
|
/* Main loop */
|
||||||
|
while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) {
|
||||||
|
|
||||||
|
ret = ue_sync_get_buffer(&ue_sync, &sf_buffer);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error calling ue_sync_work()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ue_sync_get_buffer returns 1 if successfully read 1 aligned subframe */
|
||||||
|
if (ret == 1) {
|
||||||
|
switch (state) {
|
||||||
|
case DECODE_MIB:
|
||||||
|
if (ue_sync_get_sfidx(&ue_sync) == 0) {
|
||||||
|
pbch_decode_reset(&ue_mib.pbch);
|
||||||
|
n = ue_mib_decode(&ue_mib, sf_buffer, bch_payload_unpacked, NULL, &sfn_offset);
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Error decoding UE MIB\n");
|
||||||
|
exit(-1);
|
||||||
|
} else if (n == MIB_FOUND) {
|
||||||
|
bit_unpack_vector(bch_payload_unpacked, bch_payload, BCH_PAYLOAD_LEN);
|
||||||
|
bcch_bch_unpack(bch_payload, BCH_PAYLOAD_LEN, &cell, &sfn);
|
||||||
|
printf("Decoded MIB. SFN: %d, offset: %d\n", sfn, sfn_offset);
|
||||||
|
sfn = (sfn + sfn_offset)%1024;
|
||||||
|
state = DECODE_SIB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DECODE_SIB:
|
||||||
|
/* We are looking for SI Blocks, search only in appropiate places */
|
||||||
|
if ((ue_sync_get_sfidx(&ue_sync) == 5 && (sfn%2)==0)) {
|
||||||
|
n = ue_dl_decode_sib(&ue_dl, sf_buffer, data, ue_sync_get_sfidx(&ue_sync),
|
||||||
|
((int) ceilf((float)3*(((sfn)/2)%4)/2))%4);
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Error decoding UE DL\n");fflush(stdout);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
nof_trials++;
|
||||||
|
|
||||||
|
rsrq = VEC_EMA(chest_dl_get_rsrq(&ue_dl.chest), rsrq, 0.05);
|
||||||
|
rsrp = VEC_EMA(chest_dl_get_rsrp(&ue_dl.chest), rsrp, 0.05);
|
||||||
|
snr = VEC_EMA(chest_dl_get_snr(&ue_dl.chest), snr, 0.05);
|
||||||
|
nframes++;
|
||||||
|
if (isnan(rsrq)) {
|
||||||
|
rsrq = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plot and Printf
|
||||||
|
if (ue_sync_get_sfidx(&ue_sync) == 5) {
|
||||||
|
printf("CFO: %+8.4f KHz, SFO: %+8.4f Khz, "
|
||||||
|
"RSRP: %+5.1f dBm, RSRQ: %5.1f dB, SNR: %4.1f dB, "
|
||||||
|
"PDCCH-Miss: %5.2f%%, PDSCH-BLER: %5.2f%% (%d blocks)\r",
|
||||||
|
ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000,
|
||||||
|
10*log10(rsrp*1000)-gain_offset,
|
||||||
|
10*log10(rsrq), 10*log10(snr),
|
||||||
|
100*(1-(float) ue_dl.nof_pdcch_detected/nof_trials),
|
||||||
|
(float) 100*ue_dl.pkt_errors/ue_dl.pkts_total,nof_trials, ue_dl.pkts_total);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ue_sync_get_sfidx(&ue_sync) == 9) {
|
||||||
|
sfn++;
|
||||||
|
if (sfn == 1024) {
|
||||||
|
sfn = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
if (!prog_args.disable_plots && ue_sync_get_sfidx(&ue_sync) == 5) {
|
||||||
|
do_plots(&ue_dl, 5, &ue_sync);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else if (ret == 0) {
|
||||||
|
printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r",
|
||||||
|
sync_get_peak_value(&ue_sync.sfind),
|
||||||
|
ue_sync.frame_total_cnt, ue_sync.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
sf_cnt++;
|
||||||
|
} // Main loop
|
||||||
|
|
||||||
|
ue_dl_free(&ue_dl);
|
||||||
|
ue_mib_free(&ue_mib);
|
||||||
|
ue_sync_free(&ue_sync);
|
||||||
|
cuhd_close(uhd);
|
||||||
|
printf("\nBye\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* Plotting Functions
|
||||||
|
***********************************************************************/
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
|
||||||
|
|
||||||
|
#include "liblte/graphics/plot.h"
|
||||||
|
plot_real_t poutfft, p_sync;
|
||||||
|
plot_real_t pce;
|
||||||
|
plot_scatter_t pscatequal, pscatequal_pdcch;
|
||||||
|
|
||||||
|
float tmp_plot[SLOT_LEN_RE(MAX_PRB, CPNORM)];
|
||||||
|
float tmp_plot2[SLOT_LEN_RE(MAX_PRB, CPNORM)];
|
||||||
|
float tmp_plot3[SLOT_LEN_RE(MAX_PRB, CPNORM)];
|
||||||
|
|
||||||
|
void init_plots() {
|
||||||
|
plot_init();
|
||||||
|
plot_real_init(&poutfft);
|
||||||
|
plot_real_setTitle(&poutfft, "Output FFT - Magnitude");
|
||||||
|
plot_real_setLabels(&poutfft, "Index", "dB");
|
||||||
|
plot_real_setYAxisScale(&poutfft, -60, 0);
|
||||||
|
|
||||||
|
plot_real_init(&pce);
|
||||||
|
plot_real_setTitle(&pce, "Channel Response - Magnitude");
|
||||||
|
plot_real_setLabels(&pce, "Index", "dB");
|
||||||
|
plot_real_setYAxisScale(&pce, -60, 0);
|
||||||
|
|
||||||
|
plot_real_init(&p_sync);
|
||||||
|
plot_real_setTitle(&p_sync, "PSS Cross-Corr abs value");
|
||||||
|
plot_real_setYAxisScale(&p_sync, 0, 1);
|
||||||
|
|
||||||
|
plot_scatter_init(&pscatequal);
|
||||||
|
plot_scatter_setTitle(&pscatequal, "PDSCH - Equalized Symbols");
|
||||||
|
plot_scatter_setXAxisScale(&pscatequal, -2, 2);
|
||||||
|
plot_scatter_setYAxisScale(&pscatequal, -2, 2);
|
||||||
|
|
||||||
|
plot_scatter_init(&pscatequal_pdcch);
|
||||||
|
plot_scatter_setTitle(&pscatequal_pdcch, "PDCCH - Equalized Symbols");
|
||||||
|
plot_scatter_setXAxisScale(&pscatequal_pdcch, -2, 2);
|
||||||
|
plot_scatter_setYAxisScale(&pscatequal_pdcch, -2, 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_plots(ue_dl_t *q, uint32_t sf_idx, ue_sync_t *qs) {
|
||||||
|
int i;
|
||||||
|
uint32_t nof_re = SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp);
|
||||||
|
uint32_t nof_symbols = q->harq_process[0].prb_alloc.re_sf[sf_idx];
|
||||||
|
for (i = 0; i < nof_re; i++) {
|
||||||
|
tmp_plot[i] = 20 * log10f(cabsf(q->sf_symbols[i]));
|
||||||
|
if (isinf(tmp_plot[i])) {
|
||||||
|
tmp_plot[i] = -80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < REFSIGNAL_NUM_SF(q->cell.nof_prb,0); i++) {
|
||||||
|
tmp_plot2[i] = 20 * log10f(cabsf(q->chest.pilot_estimates_average[0][i]));
|
||||||
|
if (isinf(tmp_plot2[i])) {
|
||||||
|
tmp_plot2[i] = -80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plot_real_setNewData(&poutfft, tmp_plot, nof_re);
|
||||||
|
plot_real_setNewData(&pce, tmp_plot2, REFSIGNAL_NUM_SF(q->cell.nof_prb,0));
|
||||||
|
int max = vec_max_fi(qs->strack.pss.conv_output_avg, qs->strack.pss.frame_size+qs->strack.pss.fft_size-1);
|
||||||
|
vec_sc_prod_fff(qs->strack.pss.conv_output_avg,
|
||||||
|
1/qs->strack.pss.conv_output_avg[max],
|
||||||
|
tmp_plot2,
|
||||||
|
qs->strack.pss.frame_size+qs->strack.pss.fft_size-1);
|
||||||
|
plot_real_setNewData(&p_sync, tmp_plot2, qs->strack.pss.frame_size);
|
||||||
|
|
||||||
|
plot_scatter_setNewData(&pscatequal, q->pdsch.pdsch_d, nof_symbols);
|
||||||
|
plot_scatter_setNewData(&pscatequal_pdcch, q->pdcch.pdcch_d, 36*q->pdcch.nof_cce);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,218 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include "liblte/phy/phy.h"
|
|
||||||
#include "liblte/cuhd/cuhd.h"
|
|
||||||
#include "cell_search_utils.h"
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* Program arguments processing
|
|
||||||
***********************************************************************/
|
|
||||||
typedef struct {
|
|
||||||
int nof_subframes;
|
|
||||||
bool disable_plots;
|
|
||||||
int force_N_id_2;
|
|
||||||
char *uhd_args;
|
|
||||||
float uhd_freq;
|
|
||||||
float uhd_gain;
|
|
||||||
}prog_args_t;
|
|
||||||
|
|
||||||
void args_default(prog_args_t *args) {
|
|
||||||
args->nof_subframes = -1;
|
|
||||||
args->force_N_id_2 = -1; // Pick the best
|
|
||||||
args->uhd_args = "";
|
|
||||||
args->uhd_freq = -1.0;
|
|
||||||
args->uhd_gain = 60.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void usage(prog_args_t *args, char *prog) {
|
|
||||||
printf("Usage: %s [aglnv] -f rx_frequency (in Hz)\n", prog);
|
|
||||||
printf("\t-a UHD args [Default %s]\n", args->uhd_args);
|
|
||||||
printf("\t-g UHD RX gain [Default %.2f dB]\n", args->uhd_gain);
|
|
||||||
printf("\t-l Force N_id_2 [Default best]\n");
|
|
||||||
printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes);
|
|
||||||
printf("\t-v [set verbose to debug, default none]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_args(prog_args_t *args, int argc, char **argv) {
|
|
||||||
int opt;
|
|
||||||
args_default(args);
|
|
||||||
while ((opt = getopt(argc, argv, "aglnvf")) != -1) {
|
|
||||||
switch (opt) {
|
|
||||||
case 'a':
|
|
||||||
args->uhd_args = argv[optind];
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
args->uhd_gain = atof(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
args->uhd_freq = atof(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
args->nof_subframes = atoi(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
args->force_N_id_2 = atoi(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbose++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage(args, argv[0]);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (args->uhd_freq < 0) {
|
|
||||||
usage(args, argv[0]);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**********************************************************************/
|
|
||||||
|
|
||||||
/* TODO: Do something with the output data */
|
|
||||||
char data[10000];
|
|
||||||
|
|
||||||
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) {
|
|
||||||
DEBUG(" ---- Receive %d samples ---- \n", nsamples);
|
|
||||||
return cuhd_recv(h, data, nsamples, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
int ret;
|
|
||||||
cf_t *sf_buffer;
|
|
||||||
prog_args_t prog_args;
|
|
||||||
lte_cell_t cell;
|
|
||||||
int64_t sf_cnt;
|
|
||||||
pbch_mib_t mib;
|
|
||||||
ue_sync_t ue_sync;
|
|
||||||
void *uhd;
|
|
||||||
|
|
||||||
parse_args(&prog_args, argc, argv);
|
|
||||||
|
|
||||||
printf("Opening UHD device...\n");
|
|
||||||
if (cuhd_open(prog_args.uhd_args, &uhd)) {
|
|
||||||
fprintf(stderr, "Error opening uhd\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
/* Set receiver gain */
|
|
||||||
cuhd_set_rx_gain(uhd, prog_args.uhd_gain);
|
|
||||||
|
|
||||||
/* set receiver frequency */
|
|
||||||
cuhd_set_rx_freq(uhd, (double) prog_args.uhd_freq);
|
|
||||||
cuhd_rx_wait_lo_locked(uhd);
|
|
||||||
printf("Tunning receiver to %.3f MHz\n", (double ) prog_args.uhd_freq/1000000);
|
|
||||||
|
|
||||||
if (cell_search(uhd, prog_args.force_N_id_2, &cell, &mib)) {
|
|
||||||
fprintf(stderr, "Cell not found\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
cuhd_start_rx_stream(uhd);
|
|
||||||
|
|
||||||
if (ue_sync_init(&ue_sync, cell, cuhd_recv_wrapper, uhd)) {
|
|
||||||
fprintf(stderr, "Error initiating ue_sync\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize subframe counter */
|
|
||||||
sf_cnt = 0;
|
|
||||||
|
|
||||||
lte_fft_t fft;
|
|
||||||
chest_t chest;
|
|
||||||
|
|
||||||
if (lte_fft_init(&fft, cell.cp, cell.nof_prb)) {
|
|
||||||
fprintf(stderr, "Error initiating FFT\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (chest_init_LTEDL(&chest, cell)) {
|
|
||||||
fprintf(stderr, "Error initiating channel estimator\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sf_re = SF_LEN_RE(cell.nof_prb, cell.cp);
|
|
||||||
cf_t *sf_symbols = vec_malloc(sf_re * sizeof(cf_t));
|
|
||||||
unsigned int nframes=0;
|
|
||||||
|
|
||||||
/* Main loop */
|
|
||||||
while (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1) {
|
|
||||||
|
|
||||||
ret = ue_sync_get_buffer(&ue_sync, &sf_buffer);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error calling ue_sync_work()\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
float rssi=0, rsrp=0, rsrq=0;
|
|
||||||
|
|
||||||
/* iodev_receive returns 1 if successfully read 1 aligned subframe */
|
|
||||||
if (ret == 1) {
|
|
||||||
|
|
||||||
/* Run FFT for all subframe data */
|
|
||||||
lte_fft_run_sf(&fft, sf_buffer, sf_symbols);
|
|
||||||
|
|
||||||
chest_measure_sf(&chest, sf_symbols, ue_sync_get_sfidx(&ue_sync));
|
|
||||||
rssi = VEC_CMA(chest_rssi_sf(&chest, sf_symbols),rssi,nframes);
|
|
||||||
rsrq = VEC_CMA(chest_rsrq_sf(&chest, sf_symbols, ue_sync_get_sfidx(&ue_sync)),rsrq,nframes);
|
|
||||||
rsrp = VEC_CMA(chest_rsrp_sf(&chest, ue_sync_get_sfidx(&ue_sync)),rsrp,nframes);
|
|
||||||
nframes++;
|
|
||||||
|
|
||||||
// Plot and Printf
|
|
||||||
if ((nframes%10) == 0) {
|
|
||||||
printf("CFO: %+6.4f KHz, SFO: %+6.4f Khz, RSSI: %+5.2f dBm, RSRP: %+4.2f dBm, RSRQ: %4.2f dB\r",
|
|
||||||
ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000,
|
|
||||||
10*log10(rssi*1000/4/cell.nof_prb/12/2)-prog_args.uhd_gain,
|
|
||||||
10*log10(rsrp*1000)-prog_args.uhd_gain,
|
|
||||||
10*log10(rsrq));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (ret == 0) {
|
|
||||||
printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r",
|
|
||||||
sync_get_peak_value(&ue_sync.sfind),
|
|
||||||
ue_sync.frame_total_cnt, ue_sync.state);
|
|
||||||
}
|
|
||||||
sf_cnt++;
|
|
||||||
} // Main loop
|
|
||||||
|
|
||||||
ue_sync_free(&ue_sync);
|
|
||||||
cuhd_close(uhd);
|
|
||||||
printf("\nBye\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,272 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "liblte/phy/phy.h"
|
|
||||||
|
|
||||||
#ifndef DISABLE_UHD
|
|
||||||
#include "liblte/cuhd/cuhd.h"
|
|
||||||
|
|
||||||
int decode_pbch(void *uhd, ue_celldetect_result_t *found_cell, uint32_t nof_frames_total, pbch_mib_t *mib)
|
|
||||||
{
|
|
||||||
ue_mib_t uemib;
|
|
||||||
int n;
|
|
||||||
int ret = LIBLTE_ERROR;
|
|
||||||
|
|
||||||
uint32_t nof_frames = 0;
|
|
||||||
uint32_t flen = MIB_FRAME_SIZE;
|
|
||||||
|
|
||||||
cf_t *buffer = vec_malloc(sizeof(cf_t) * flen);
|
|
||||||
if (!buffer) {
|
|
||||||
perror("malloc");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
bzero(mib, sizeof(pbch_mib_t));
|
|
||||||
|
|
||||||
if (ue_mib_init(&uemib, found_cell->cell_id, found_cell->cp)) {
|
|
||||||
fprintf(stderr, "Error initiating PBCH decoder\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO("Setting sampling frequency 1.92 MHz for PBCH decoding\n", 0);
|
|
||||||
cuhd_set_rx_srate(uhd, 1920000.0);
|
|
||||||
INFO("Starting receiver...\n", 0);
|
|
||||||
cuhd_start_rx_stream(uhd);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (cuhd_recv(uhd, buffer, flen, 1)<0) {
|
|
||||||
fprintf(stderr, "Error receiving from USRP\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG("Calling ue_mib_decode() %d/%d\n", nof_frames, nof_frames_total);
|
|
||||||
|
|
||||||
n = ue_mib_decode(&uemib, buffer, flen, mib);
|
|
||||||
if (n == LIBLTE_ERROR || n == LIBLTE_ERROR_INVALID_INPUTS) {
|
|
||||||
fprintf(stderr, "Error calling ue_mib_decode()\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
if (n == MIB_FRAME_UNALIGNED) {
|
|
||||||
printf("Realigning frame\n");
|
|
||||||
if (cuhd_recv(uhd, buffer, 1500, 1)<0) {
|
|
||||||
fprintf(stderr, "Error receiving from USRP\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
bzero(buffer, flen * sizeof(cf_t));
|
|
||||||
}
|
|
||||||
nof_frames++;
|
|
||||||
} while (n != MIB_FOUND && nof_frames < 2*nof_frames_total);
|
|
||||||
|
|
||||||
if (n == MIB_FOUND) {
|
|
||||||
printf("\n\nMIB decoded in %d ms (%d half frames)\n", nof_frames*5, nof_frames);
|
|
||||||
pbch_mib_fprint(stdout, mib, found_cell->cell_id);
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
} else {
|
|
||||||
ret = LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
free_and_exit:
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
cuhd_stop_rx_stream(uhd);
|
|
||||||
cuhd_flush_buffer(uhd);
|
|
||||||
|
|
||||||
ue_mib_free(&uemib);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_cell(void *uhd, ue_celldetect_result_t *found_cell, uint32_t N_id_2)
|
|
||||||
{
|
|
||||||
int ret = LIBLTE_ERROR;
|
|
||||||
ue_celldetect_t cd;
|
|
||||||
|
|
||||||
cf_t *buffer = vec_malloc(sizeof(cf_t) * 96000);
|
|
||||||
if (!buffer) {
|
|
||||||
perror("malloc");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ue_celldetect_init(&cd)) {
|
|
||||||
fprintf(stderr, "Error initiating UE cell detect\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
ue_celldetect_set_nof_frames_detected(&cd, 50);
|
|
||||||
|
|
||||||
ue_celldetect_set_nof_frames_total(&cd, 500);
|
|
||||||
|
|
||||||
INFO("Setting sampling frequency 960 KHz for PSS search\n", 0);
|
|
||||||
cuhd_set_rx_srate(uhd, 960000.0);
|
|
||||||
INFO("Starting receiver...\n", 0);
|
|
||||||
cuhd_start_rx_stream(uhd);
|
|
||||||
|
|
||||||
uint32_t flen = 4800;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (cuhd_recv(uhd, buffer, flen, 1)<0) {
|
|
||||||
fprintf(stderr, "Error receiving from USRP\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG("Scanning cell at N_id_2=%d\n",N_id_2);
|
|
||||||
|
|
||||||
n = ue_celldetect_scan(&cd, buffer, flen, found_cell, N_id_2);
|
|
||||||
switch(n) {
|
|
||||||
case CS_FRAME_UNALIGNED:
|
|
||||||
printf("Realigning frame\n");
|
|
||||||
if (cuhd_recv(uhd, buffer, flen/2, 1)<0) {
|
|
||||||
fprintf(stderr, "Error receiving from USRP\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
/* FIXME: What should we do here?? */
|
|
||||||
ret = -1;
|
|
||||||
goto free_and_exit;
|
|
||||||
case CS_CELL_DETECTED:
|
|
||||||
if (found_cell->peak > 0) {
|
|
||||||
printf("\n\tCELL ID: %d, CP: %s, Peak: %.2f, Mode: %d/%d\n",
|
|
||||||
found_cell->cell_id,
|
|
||||||
lte_cp_string(found_cell->cp),
|
|
||||||
found_cell->peak, found_cell->mode,
|
|
||||||
cd.nof_frames_detected);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 1;
|
|
||||||
INFO("Cell found at N_id_2=%d\n",N_id_2);
|
|
||||||
break;
|
|
||||||
case CS_CELL_NOT_DETECTED:
|
|
||||||
ret = 0;
|
|
||||||
DEBUG("No cell found at N_id_2=%d\n",N_id_2);
|
|
||||||
break;
|
|
||||||
case LIBLTE_ERROR:
|
|
||||||
case LIBLTE_ERROR_INVALID_INPUTS:
|
|
||||||
ret = LIBLTE_ERROR;
|
|
||||||
fprintf(stderr, "Error calling cellsearch_scan()\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
} while(n == 0);
|
|
||||||
|
|
||||||
free_and_exit:
|
|
||||||
free(buffer);
|
|
||||||
ue_celldetect_free(&cd);
|
|
||||||
INFO("Stopping receiver...\n", 0);
|
|
||||||
cuhd_stop_rx_stream(uhd);
|
|
||||||
cuhd_flush_buffer(uhd);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int find_all_cells(void *uhd, ue_celldetect_result_t found_cell[3])
|
|
||||||
{
|
|
||||||
|
|
||||||
uint32_t N_id_2;
|
|
||||||
int ret;
|
|
||||||
int nof_detected_cells = 0;
|
|
||||||
|
|
||||||
for (N_id_2=0;N_id_2<3;N_id_2++) {
|
|
||||||
ret = find_cell(uhd, &found_cell[N_id_2], N_id_2);
|
|
||||||
if (ret == 1) {
|
|
||||||
nof_detected_cells++;
|
|
||||||
} else if (ret == LIBLTE_ERROR) {
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nof_detected_cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cell_search(void *uhd, int force_N_id_2, lte_cell_t *cell, pbch_mib_t *mib)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ue_celldetect_result_t found_cells[3];
|
|
||||||
bzero(found_cells, 3*sizeof(ue_celldetect_result_t));
|
|
||||||
|
|
||||||
if (force_N_id_2 >= 0) {
|
|
||||||
ret = find_cell(uhd, &found_cells[force_N_id_2], force_N_id_2);
|
|
||||||
} else {
|
|
||||||
ret = find_all_cells(uhd, found_cells);
|
|
||||||
}
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error searching cell\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int max_peak_cell = 0;
|
|
||||||
float max_peak_value = -1.0;
|
|
||||||
if (ret > 0) {
|
|
||||||
if (force_N_id_2 >= 0) {
|
|
||||||
max_peak_cell = force_N_id_2;
|
|
||||||
} else {
|
|
||||||
for (int i=0;i<3;i++) {
|
|
||||||
if (found_cells[i].peak > max_peak_value) {
|
|
||||||
max_peak_value = found_cells[i].peak;
|
|
||||||
max_peak_cell = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Decoding PBCH for cell %d (N_id_2=%d)\n", found_cells[max_peak_cell].cell_id, max_peak_cell);
|
|
||||||
if (decode_pbch(uhd, &found_cells[max_peak_cell], 400, mib)) {
|
|
||||||
fprintf(stderr, "Could not decode PBCH from CELL ID %d\n", found_cells[max_peak_cell].cell_id);
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Could not find any cell in this frequency\n");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
cell->cp = found_cells[max_peak_cell].cp;
|
|
||||||
cell->id = found_cells[max_peak_cell].cell_id;
|
|
||||||
cell->nof_prb = mib->nof_prb;
|
|
||||||
cell->nof_ports = mib->nof_ports;
|
|
||||||
|
|
||||||
/* set sampling frequency */
|
|
||||||
int srate = lte_sampling_freq_hz(cell->nof_prb);
|
|
||||||
if (srate != -1) {
|
|
||||||
cuhd_set_rx_srate(uhd, (double) srate);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid number of PRB %d\n", cell->nof_prb);
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,204 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "iodev.h"
|
|
||||||
|
|
||||||
#include "liblte/phy/io/filesource.h"
|
|
||||||
#include "liblte/phy/ue/ue_sync.h"
|
|
||||||
#include "liblte/phy/utils/debug.h"
|
|
||||||
#include "liblte/phy/utils/vector.h"
|
|
||||||
|
|
||||||
#ifndef DISABLE_UHD
|
|
||||||
#include "liblte/cuhd/cuhd.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "cell_search_utils.h"
|
|
||||||
|
|
||||||
|
|
||||||
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) {
|
|
||||||
DEBUG(" ---- Receive %d samples ---- \n", nsamples);
|
|
||||||
return cuhd_recv(h, data, nsamples, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup USRP or input file */
|
|
||||||
int iodev_init(iodev_t *q, iodev_cfg_t *config, lte_cell_t *cell, pbch_mib_t *mib) {
|
|
||||||
|
|
||||||
if (config->input_file_name) {
|
|
||||||
|
|
||||||
mib->phich_resources = R_1;
|
|
||||||
mib->phich_length = PHICH_NORM;
|
|
||||||
|
|
||||||
cell->id = config->cell_id_file;
|
|
||||||
cell->cp = CPNORM;
|
|
||||||
cell->nof_ports = config->nof_ports_file;
|
|
||||||
cell->nof_prb = config->nof_prb_file;
|
|
||||||
|
|
||||||
if (filesource_init(&q->fsrc, config->input_file_name, COMPLEX_FLOAT_BIN)) {
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
q->mode = FILESOURCE;
|
|
||||||
int symbol_sz = lte_symbol_sz(cell->nof_prb);
|
|
||||||
if (symbol_sz > 0) {
|
|
||||||
q->sf_len = SF_LEN(symbol_sz);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid number of PRB %d\n", cell->nof_prb);
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
q->input_buffer_file = vec_malloc(q->sf_len * sizeof(cf_t));
|
|
||||||
if (!q->input_buffer_file) {
|
|
||||||
perror("malloc");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
q->sf_idx = 9;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
#ifndef DISABLE_UHD
|
|
||||||
|
|
||||||
printf("Opening UHD device...\n");
|
|
||||||
if (cuhd_open(config->uhd_args, &q->uhd)) {
|
|
||||||
fprintf(stderr, "Error opening uhd\n");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
/* Set receiver gain */
|
|
||||||
cuhd_set_rx_gain(q->uhd, config->uhd_gain);
|
|
||||||
|
|
||||||
/* set receiver frequency */
|
|
||||||
cuhd_set_rx_freq(q->uhd, (double) config->uhd_freq);
|
|
||||||
cuhd_rx_wait_lo_locked(q->uhd);
|
|
||||||
DEBUG("Set uhd_freq to %.3f MHz\n", (double ) config->uhd_freq);
|
|
||||||
|
|
||||||
if (cell_search(q->uhd, config->force_N_id_2, cell, mib)) {
|
|
||||||
fprintf(stderr, "Cell not found\n");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
cuhd_start_rx_stream(q->uhd);
|
|
||||||
|
|
||||||
if (ue_sync_init(&q->sframe, *cell, cuhd_recv_wrapper, q->uhd)) {
|
|
||||||
fprintf(stderr, "Error initiating ue_sync\n");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here, the subframe length and input buffer is managed by ue_sync
|
|
||||||
q->mode = UHD;
|
|
||||||
|
|
||||||
#else
|
|
||||||
printf("Error UHD not available. Select an input file\n");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&q->config, config, sizeof(iodev_cfg_t));
|
|
||||||
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void iodev_free(iodev_t *q) {
|
|
||||||
|
|
||||||
if (q->mode == FILESOURCE) {
|
|
||||||
filesource_free(&q->fsrc);
|
|
||||||
} else {
|
|
||||||
#ifndef DISABLE_UHD
|
|
||||||
cuhd_close(q->uhd);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Receive samples from the USRP or read from file */
|
|
||||||
int iodev_receive(iodev_t *q, cf_t **buffer) {
|
|
||||||
int n;
|
|
||||||
if (q->mode == FILESOURCE) {
|
|
||||||
INFO(" ----- READING %d SAMPLES ---- \n", q->sf_len);
|
|
||||||
n = filesource_read(&q->fsrc, q->input_buffer_file, q->sf_len);
|
|
||||||
*buffer = q->input_buffer_file;
|
|
||||||
if (n == -1) {
|
|
||||||
fprintf(stderr, "Error reading file\n");
|
|
||||||
/* wrap file if arrive to end */
|
|
||||||
} else if (n < q->sf_len) {
|
|
||||||
DEBUG("Read %d from file. Seeking to 0\n",n);
|
|
||||||
filesource_seek(&q->fsrc, 0);
|
|
||||||
n = filesource_read(&q->fsrc, q->input_buffer_file, q->sf_len);
|
|
||||||
if (n == -1) {
|
|
||||||
fprintf(stderr, "Error reading file\n");
|
|
||||||
/* wrap file if arrive to end */
|
|
||||||
} else {
|
|
||||||
n = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n = 1;
|
|
||||||
}
|
|
||||||
q->sf_idx++;
|
|
||||||
if (q->sf_idx == 10) {
|
|
||||||
q->sf_idx = 0;
|
|
||||||
}
|
|
||||||
usleep(5000);
|
|
||||||
} else {
|
|
||||||
/* Use ue_sync_work which returns a synchronized buffer of subframe samples */
|
|
||||||
#ifndef DISABLE_UHD
|
|
||||||
n = ue_sync_get_buffer(&q->sframe, buffer);
|
|
||||||
if (n < 0) {
|
|
||||||
fprintf(stderr, "Error calling ue_sync_work()\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* iodev_get_cuhd(iodev_t *q) {
|
|
||||||
if (q->mode == UHD) {
|
|
||||||
return q->uhd;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool iodev_isfile(iodev_t *q) {
|
|
||||||
return q->mode == FILESOURCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool iodev_isUSRP(iodev_t *q) {
|
|
||||||
return q->mode == UHD;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t iodev_get_sfidx(iodev_t *q) {
|
|
||||||
if (iodev_isfile(q)) {
|
|
||||||
return q->sf_idx;
|
|
||||||
} else {
|
|
||||||
return ue_sync_get_sfidx(&q->sframe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IODEF_H
|
|
||||||
#define IODEF_H
|
|
||||||
|
|
||||||
#include "liblte/config.h"
|
|
||||||
|
|
||||||
#include "liblte/phy/ue/ue_sync.h"
|
|
||||||
#include "liblte/phy/io/filesource.h"
|
|
||||||
|
|
||||||
#ifndef DISABLE_UHD
|
|
||||||
#include "liblte/cuhd/cuhd.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*********
|
|
||||||
*
|
|
||||||
* This component is a wrapper to the cuhd or filesource modules. It uses
|
|
||||||
* sync_frame_t to read aligned subframes from the USRP or filesource to read
|
|
||||||
* subframes from a file.
|
|
||||||
*
|
|
||||||
* When created, it starts receiving/reading at 1.92 MHz. The sampling frequency
|
|
||||||
* can then be changed using iodev_set_srate()
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum LIBLTE_API {FILESOURCE, UHD} iodev_mode_t;
|
|
||||||
|
|
||||||
typedef _Complex float cf_t;
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
|
||||||
char *input_file_name;
|
|
||||||
uint32_t cell_id_file;
|
|
||||||
uint32_t nof_prb_file;
|
|
||||||
uint32_t nof_ports_file;
|
|
||||||
int force_N_id_2;
|
|
||||||
|
|
||||||
float uhd_freq;
|
|
||||||
float uhd_gain;
|
|
||||||
char *uhd_args;
|
|
||||||
float find_threshold;
|
|
||||||
} iodev_cfg_t;
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
|
||||||
#ifndef DISABLE_UHD
|
|
||||||
void *uhd;
|
|
||||||
ue_sync_t sframe;
|
|
||||||
#endif
|
|
||||||
uint32_t sf_len;
|
|
||||||
uint32_t sf_idx;
|
|
||||||
cf_t *input_buffer_file; // for UHD mode, the input buffer is managed by sync_frame_t
|
|
||||||
filesource_t fsrc;
|
|
||||||
iodev_cfg_t config;
|
|
||||||
iodev_mode_t mode;
|
|
||||||
} iodev_t;
|
|
||||||
|
|
||||||
|
|
||||||
LIBLTE_API int iodev_init(iodev_t *q,
|
|
||||||
iodev_cfg_t *config,
|
|
||||||
lte_cell_t *cell,
|
|
||||||
pbch_mib_t *mib);
|
|
||||||
|
|
||||||
LIBLTE_API void iodev_free(iodev_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API int iodev_receive(iodev_t *q,
|
|
||||||
cf_t **buffer);
|
|
||||||
|
|
||||||
LIBLTE_API void* iodev_get_cuhd(iodev_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API uint32_t iodev_get_sfidx(iodev_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API bool iodev_isfile(iodev_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API bool iodev_isUSRP(iodev_t *q);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,309 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include "liblte/phy/phy.h"
|
|
||||||
#include "iodev.h"
|
|
||||||
|
|
||||||
#ifndef DISABLE_GRAPHICS
|
|
||||||
void init_plots();
|
|
||||||
void do_plots(ue_dl_t *q, uint32_t sf_idx);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int go_exit = 0;
|
|
||||||
|
|
||||||
/* Local function definitions */
|
|
||||||
void init_plots();
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* Program arguments processing
|
|
||||||
***********************************************************************/
|
|
||||||
typedef struct {
|
|
||||||
uint16_t rnti;
|
|
||||||
int nof_subframes;
|
|
||||||
bool disable_plots;
|
|
||||||
iodev_cfg_t io_config;
|
|
||||||
}prog_args_t;
|
|
||||||
|
|
||||||
void args_default(prog_args_t *args) {
|
|
||||||
args->io_config.cell_id_file = 195;
|
|
||||||
args->io_config.nof_prb_file = 50;
|
|
||||||
args->io_config.nof_ports_file = 2;
|
|
||||||
args->rnti = SIRNTI;
|
|
||||||
args->nof_subframes = -1;
|
|
||||||
args->disable_plots = false;
|
|
||||||
args->io_config.find_threshold = -1.0;
|
|
||||||
args->io_config.input_file_name = NULL;
|
|
||||||
args->io_config.force_N_id_2 = -1; // Pick the best
|
|
||||||
args->io_config.uhd_args = "";
|
|
||||||
args->io_config.uhd_freq = -1.0;
|
|
||||||
args->io_config.uhd_gain = 60.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void usage(prog_args_t *args, char *prog) {
|
|
||||||
printf("Usage: %s [cargndvtbl] [-i input_file | -f rx_frequency (in Hz)]\n", prog);
|
|
||||||
printf("\t-c cell_id if reading from file [Default %d]\n", args->io_config.cell_id_file);
|
|
||||||
printf("\t-p nof_prb if reading from file [Default %d]\n", args->io_config.nof_prb_file);
|
|
||||||
printf("\t-o nof_ports if reading from file [Default %d]\n", args->io_config.nof_ports_file);
|
|
||||||
printf("\t-r RNTI to look for [Default 0x%x]\n", args->rnti);
|
|
||||||
#ifndef DISABLE_UHD
|
|
||||||
printf("\t-a UHD args [Default %s]\n", args->io_config.uhd_args);
|
|
||||||
printf("\t-g UHD RX gain [Default %.2f dB]\n", args->io_config.uhd_gain);
|
|
||||||
#else
|
|
||||||
printf("\t UHD is disabled. CUHD library not available\n");
|
|
||||||
#endif
|
|
||||||
printf("\t-l Force N_id_2 [Default best]\n");
|
|
||||||
printf("\t-b Decode PBCH only [Default All channels]\n");
|
|
||||||
printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes);
|
|
||||||
printf("\t-t PSS threshold [Default %f]\n", args->io_config.find_threshold);
|
|
||||||
#ifndef DISABLE_GRAPHICS
|
|
||||||
printf("\t-d disable plots [Default enabled]\n");
|
|
||||||
#else
|
|
||||||
printf("\t plots are disabled. Graphics library not available\n");
|
|
||||||
#endif
|
|
||||||
printf("\t-v [set verbose to debug, default none]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_args(prog_args_t *args, int argc, char **argv) {
|
|
||||||
int opt;
|
|
||||||
args_default(args);
|
|
||||||
while ((opt = getopt(argc, argv, "icagfndvtbprol")) != -1) {
|
|
||||||
switch (opt) {
|
|
||||||
case 'i':
|
|
||||||
args->io_config.input_file_name = argv[optind];
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
args->io_config.cell_id_file = atoi(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
args->io_config.nof_prb_file = atoi(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
args->io_config.nof_ports_file = atoi(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
args->io_config.uhd_args = argv[optind];
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
args->io_config.uhd_gain = atof(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
args->io_config.uhd_freq = atof(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
args->io_config.find_threshold = atof(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
args->nof_subframes = atoi(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
args->io_config.force_N_id_2 = atoi(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
args->rnti= atoi(argv[optind]);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
args->disable_plots = true;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbose++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage(args, argv[0]);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (args->io_config.uhd_freq < 0 && args->io_config.input_file_name == NULL) {
|
|
||||||
usage(args, argv[0]);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**********************************************************************/
|
|
||||||
|
|
||||||
void sigintHandler(int x) {
|
|
||||||
go_exit = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Do something with the output data */
|
|
||||||
char data[10000];
|
|
||||||
|
|
||||||
extern float mean_exec_time;
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
int ret;
|
|
||||||
cf_t *sf_buffer;
|
|
||||||
iodev_t iodev;
|
|
||||||
prog_args_t prog_args;
|
|
||||||
lte_cell_t cell;
|
|
||||||
ue_dl_t ue_dl;
|
|
||||||
int64_t sf_cnt;
|
|
||||||
pbch_mib_t mib;
|
|
||||||
bool printed_sib = false;
|
|
||||||
int rlen;
|
|
||||||
|
|
||||||
parse_args(&prog_args, argc, argv);
|
|
||||||
|
|
||||||
#ifndef DISABLE_GRAPHICS
|
|
||||||
if (!prog_args.disable_plots) {
|
|
||||||
init_plots();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Initialize subframe counter */
|
|
||||||
sf_cnt = 0;
|
|
||||||
|
|
||||||
if (iodev_init(&iodev, &prog_args.io_config, &cell, &mib)) {
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ue_dl_init(&ue_dl, cell, mib.phich_resources, mib.phich_length, 1234)) {
|
|
||||||
fprintf(stderr, "Error initiating UE downlink processing module\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
pdsch_set_rnti(&ue_dl.pdsch, prog_args.rnti);
|
|
||||||
|
|
||||||
/* Main loop */
|
|
||||||
while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) {
|
|
||||||
|
|
||||||
ret = iodev_receive(&iodev, &sf_buffer);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error reading from input device (%d)\n", ret);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* iodev_receive returns 1 if successfully read 1 aligned subframe */
|
|
||||||
if (ret == 1) {
|
|
||||||
rlen = ue_dl_decode(&ue_dl, sf_buffer, data, iodev_get_sfidx(&iodev), prog_args.rnti);
|
|
||||||
if (rlen < 0) {
|
|
||||||
fprintf(stderr, "\nError running receiver\n");fflush(stdout);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
if (prog_args.rnti == SIRNTI && !printed_sib && rlen > 0) {
|
|
||||||
printf("\n\nDecoded SIB1 Message: ");
|
|
||||||
vec_fprint_hex(stdout, data, rlen);
|
|
||||||
printf("\n");fflush(stdout);
|
|
||||||
printed_sib = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plot and Printf
|
|
||||||
if (!(sf_cnt % 10)) {
|
|
||||||
printf("CFO: %+.4f KHz, SFO: %+.4f Khz, NOI: %.2f Errors: %4d/%4d/%4d, BLER: %.1e, Texec: %.2f\r",
|
|
||||||
ue_sync_get_cfo(&iodev.sframe)/1000, ue_sync_get_sfo(&iodev.sframe)/1000,
|
|
||||||
pdsch_average_noi(&ue_dl.pdsch),
|
|
||||||
(int) ue_dl.pkt_errors, (int) ue_dl.pkts_total, (int) ue_dl.nof_trials,
|
|
||||||
(float) ue_dl.pkt_errors / ue_dl.pkts_total,
|
|
||||||
mean_exec_time);
|
|
||||||
|
|
||||||
}
|
|
||||||
#ifndef DISABLE_GRAPHICS
|
|
||||||
if (!prog_args.disable_plots && iodev_get_sfidx(&iodev) == 5) {
|
|
||||||
do_plots(&ue_dl, 5);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else if (ret == 0) {
|
|
||||||
printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r",
|
|
||||||
sync_get_peak_value(&iodev.sframe.sfind),
|
|
||||||
iodev.sframe.frame_total_cnt, iodev.sframe.state);
|
|
||||||
}
|
|
||||||
sf_cnt++;
|
|
||||||
} // Main loop
|
|
||||||
|
|
||||||
ue_dl_free(&ue_dl);
|
|
||||||
iodev_free(&iodev);
|
|
||||||
|
|
||||||
printf("\nBye\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* Plotting Functions
|
|
||||||
***********************************************************************/
|
|
||||||
#ifndef DISABLE_GRAPHICS
|
|
||||||
|
|
||||||
|
|
||||||
#include "liblte/graphics/plot.h"
|
|
||||||
plot_real_t poutfft;
|
|
||||||
plot_complex_t pce;
|
|
||||||
plot_scatter_t pscatrecv, pscatequal;
|
|
||||||
|
|
||||||
float tmp_plot[SLOT_LEN_RE(MAX_PRB, CPNORM)];
|
|
||||||
|
|
||||||
void init_plots() {
|
|
||||||
plot_init();
|
|
||||||
plot_real_init(&poutfft);
|
|
||||||
plot_real_setTitle(&poutfft, "Output FFT - Magnitude");
|
|
||||||
plot_real_setLabels(&poutfft, "Index", "dB");
|
|
||||||
plot_real_setYAxisScale(&poutfft, -30, 20);
|
|
||||||
|
|
||||||
plot_complex_init(&pce);
|
|
||||||
plot_complex_setTitle(&pce, "Channel Estimates");
|
|
||||||
plot_complex_setYAxisScale(&pce, Ip, -3, 3);
|
|
||||||
plot_complex_setYAxisScale(&pce, Q, -3, 3);
|
|
||||||
plot_complex_setYAxisScale(&pce, Magnitude, 0, 4);
|
|
||||||
plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI);
|
|
||||||
|
|
||||||
plot_scatter_init(&pscatrecv);
|
|
||||||
plot_scatter_setTitle(&pscatrecv, "Received Symbols");
|
|
||||||
plot_scatter_setXAxisScale(&pscatrecv, -4, 4);
|
|
||||||
plot_scatter_setYAxisScale(&pscatrecv, -4, 4);
|
|
||||||
|
|
||||||
plot_scatter_init(&pscatequal);
|
|
||||||
plot_scatter_setTitle(&pscatequal, "Equalized Symbols");
|
|
||||||
plot_scatter_setXAxisScale(&pscatequal, -2, 2);
|
|
||||||
plot_scatter_setYAxisScale(&pscatequal, -2, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_plots(ue_dl_t *q, uint32_t sf_idx) {
|
|
||||||
int i;
|
|
||||||
uint32_t nof_re = SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp);
|
|
||||||
uint32_t nof_symbols = q->harq_process[0].prb_alloc.re_sf[sf_idx];
|
|
||||||
for (i = 0; i < nof_re; i++) {
|
|
||||||
tmp_plot[i] = 10 * log10f(cabsf(q->sf_symbols[i]));
|
|
||||||
if (isinf(tmp_plot[i])) {
|
|
||||||
tmp_plot[i] = -80;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
plot_real_setNewData(&poutfft, tmp_plot, nof_re);
|
|
||||||
plot_complex_setNewData(&pce, q->ce[0], nof_re);
|
|
||||||
plot_scatter_setNewData(&pscatrecv, q->pdsch.pdsch_symbols[0], nof_symbols);
|
|
||||||
plot_scatter_setNewData(&pscatequal, q->pdsch.pdsch_d, nof_symbols);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,238 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef CHEST_
|
|
||||||
#define CHEST_
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "liblte/config.h"
|
|
||||||
|
|
||||||
#include "liblte/phy/resampling/interp.h"
|
|
||||||
#include "liblte/phy/ch_estimation/refsignal.h"
|
|
||||||
#include "liblte/phy/common/phy_common.h"
|
|
||||||
|
|
||||||
typedef _Complex float cf_t; /* this is only a shortcut */
|
|
||||||
|
|
||||||
typedef void (*interpolate_fnc_t) (cf_t *input,
|
|
||||||
cf_t *output,
|
|
||||||
uint32_t M,
|
|
||||||
uint32_t len,
|
|
||||||
uint32_t off_st,
|
|
||||||
uint32_t off_end);
|
|
||||||
|
|
||||||
/** This is an OFDM channel estimator.
|
|
||||||
* It works with any reference signal pattern, provided by the object
|
|
||||||
* refsignal_t
|
|
||||||
* A 2-D filter is used for freq and time channel interpolation.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Low-level API */
|
|
||||||
typedef struct LIBLTE_API {
|
|
||||||
uint32_t nof_ports;
|
|
||||||
uint32_t nof_re;
|
|
||||||
uint32_t nof_symbols;
|
|
||||||
|
|
||||||
refsignal_t refsignal[MAX_PORTS][NSLOTS_X_FRAME];
|
|
||||||
interp_t interp_time[MAX_PORTS];
|
|
||||||
interp_t interp_freq[MAX_PORTS];
|
|
||||||
|
|
||||||
}chest_t;
|
|
||||||
|
|
||||||
LIBLTE_API int chest_init(chest_t *q,
|
|
||||||
uint32_t nof_re,
|
|
||||||
uint32_t nof_symbols,
|
|
||||||
uint32_t nof_ports);
|
|
||||||
|
|
||||||
LIBLTE_API void chest_free(chest_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_set_nof_ports(chest_t *q,
|
|
||||||
uint32_t nof_ports);
|
|
||||||
|
|
||||||
|
|
||||||
LIBLTE_API float chest_rsrp(chest_t *q,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
LIBLTE_API float chest_rsrp_sf(chest_t *q,
|
|
||||||
uint32_t sf_idx);
|
|
||||||
|
|
||||||
LIBLTE_API float chest_rssi(chest_t *q,
|
|
||||||
cf_t *input);
|
|
||||||
|
|
||||||
LIBLTE_API float chest_rssi_sf(chest_t *q,
|
|
||||||
cf_t *input);
|
|
||||||
|
|
||||||
LIBLTE_API float chest_rsrq(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
LIBLTE_API float chest_rsrq_sf(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
uint32_t sf_idx);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_measure_ref(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id,
|
|
||||||
uint32_t nref);
|
|
||||||
|
|
||||||
LIBLTE_API void chest_measure_slot(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
uint32_t nslot);
|
|
||||||
|
|
||||||
LIBLTE_API void chest_measure_sf(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
uint32_t sf_idx);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ce_slot_port(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
cf_t *ce,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ce_sf_port(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
cf_t *ce,
|
|
||||||
uint32_t sf_idx,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ce_slot(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
cf_t *ce[MAX_PORTS],
|
|
||||||
uint32_t nslot);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ce_sf(chest_t *q,
|
|
||||||
cf_t *input,
|
|
||||||
cf_t *ce[MAX_PORTS],
|
|
||||||
uint32_t sf_idx);
|
|
||||||
|
|
||||||
LIBLTE_API void chest_fprint(chest_t *q,
|
|
||||||
FILE *stream,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
LIBLTE_API void chest_ref_fprint(chest_t *q,
|
|
||||||
FILE *stream,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
LIBLTE_API void chest_recvsig_fprint(chest_t *q,
|
|
||||||
FILE *stream,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
LIBLTE_API void chest_ce_fprint(chest_t *q,
|
|
||||||
FILE *stream,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ref_get_symbols(chest_t *q,
|
|
||||||
uint32_t port_id,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t l[2]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
*
|
|
||||||
* Downlink Channel Estimator
|
|
||||||
*
|
|
||||||
*********************************************************/
|
|
||||||
LIBLTE_API int chest_init_LTEDL(chest_t *q,
|
|
||||||
lte_cell_t cell);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ref_set_LTEDL_slot_port(chest_t *q,
|
|
||||||
uint32_t nslot,
|
|
||||||
uint32_t port_id,
|
|
||||||
lte_cell_t cell);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ref_set_LTEDL_slot(chest_t *q,
|
|
||||||
uint32_t nslot,
|
|
||||||
lte_cell_t cell);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ref_set_LTEDL(chest_t *q,
|
|
||||||
lte_cell_t cell);
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
*
|
|
||||||
* Uplink Channel Estimator
|
|
||||||
*
|
|
||||||
*********************************************************/
|
|
||||||
LIBLTE_API int chest_init_LTEUL(chest_t *q,
|
|
||||||
lte_cell_t cell);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ref_set_LTEUL_slot(chest_t *q,
|
|
||||||
uint32_t nslot,
|
|
||||||
lte_cell_t cell);
|
|
||||||
|
|
||||||
LIBLTE_API int chest_ref_set_LTEUL(chest_t *q,
|
|
||||||
lte_cell_t cell);
|
|
||||||
|
|
||||||
|
|
||||||
/* High-level API */
|
|
||||||
|
|
||||||
/** TODO: The high-level API has N interfaces, one for each port */
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API{
|
|
||||||
chest_t obj;
|
|
||||||
struct chest_init {
|
|
||||||
int nof_symbols; // 7 for normal cp, 6 for extended
|
|
||||||
int nof_ports;
|
|
||||||
int nof_prb;
|
|
||||||
int cell_id; // set to -1 to init at runtime
|
|
||||||
} init;
|
|
||||||
cf_t *input;
|
|
||||||
int in_len;
|
|
||||||
struct chest_ctrl_in {
|
|
||||||
int sf_idx; // subframe id in the 10ms frame
|
|
||||||
} ctrl_in;
|
|
||||||
cf_t *output[MAX_PORTS];
|
|
||||||
int out_len[MAX_PORTS];
|
|
||||||
}chest_hl;
|
|
||||||
|
|
||||||
#define DEFAULT_FRAME_SIZE 2048
|
|
||||||
|
|
||||||
LIBLTE_API int chest_initialize(chest_hl* h);
|
|
||||||
LIBLTE_API int chest_work(chest_hl* hl);
|
|
||||||
LIBLTE_API int chest_stop(chest_hl* hl);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CHEST_DL_
|
||||||
|
#define CHEST_DL_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
|
||||||
|
#include "liblte/phy/resampling/interp.h"
|
||||||
|
#include "liblte/phy/filter/filter2d.h"
|
||||||
|
#include "liblte/phy/ch_estimation/refsignal_dl.h"
|
||||||
|
#include "liblte/phy/common/phy_common.h"
|
||||||
|
|
||||||
|
/** 3GPP LTE Downlink channel estimator and equalizer.
|
||||||
|
* Estimates the channel in the resource elements transmitting references and interpolates for the rest
|
||||||
|
* of the resource grid.
|
||||||
|
*
|
||||||
|
* The equalizer uses the channel estimates to produce an estimation of the transmitted symbol.
|
||||||
|
*
|
||||||
|
* This object depends on the refsignal_t object for creating the LTE CSR signal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CHEST_MAX_FILTER_FREQ_LEN 10
|
||||||
|
#define CHEST_MAX_FILTER_TIME_LEN 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
lte_cell_t cell;
|
||||||
|
refsignal_cs_t csr_signal;
|
||||||
|
cf_t *pilot_estimates[MAX_PORTS];
|
||||||
|
cf_t *pilot_estimates_average[MAX_PORTS];
|
||||||
|
cf_t *pilot_recv_signal[MAX_PORTS];
|
||||||
|
|
||||||
|
uint32_t filter_freq_len;
|
||||||
|
float filter_freq[CHEST_MAX_FILTER_FREQ_LEN];
|
||||||
|
uint32_t filter_time_len;
|
||||||
|
float filter_time[CHEST_MAX_FILTER_TIME_LEN];
|
||||||
|
|
||||||
|
cf_t *tmp_noise;
|
||||||
|
cf_t *tmp_freqavg;
|
||||||
|
cf_t *tmp_timeavg[CHEST_MAX_FILTER_TIME_LEN];
|
||||||
|
|
||||||
|
interp_linvec_t interp_linvec;
|
||||||
|
interp_lin_t interp_lin;
|
||||||
|
|
||||||
|
float rssi[MAX_PORTS];
|
||||||
|
float rsrp[MAX_PORTS];
|
||||||
|
float noise_estimate[MAX_PORTS];
|
||||||
|
} chest_dl_t;
|
||||||
|
|
||||||
|
|
||||||
|
LIBLTE_API int chest_dl_init(chest_dl_t *q,
|
||||||
|
lte_cell_t cell);
|
||||||
|
|
||||||
|
LIBLTE_API void chest_dl_free(chest_dl_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API int chest_dl_set_filter_freq(chest_dl_t *q,
|
||||||
|
float *filter,
|
||||||
|
uint32_t filter_len);
|
||||||
|
|
||||||
|
LIBLTE_API int chest_dl_set_filter_time(chest_dl_t *q,
|
||||||
|
float *filter,
|
||||||
|
uint32_t filter_len);
|
||||||
|
|
||||||
|
LIBLTE_API int chest_dl_estimate(chest_dl_t *q,
|
||||||
|
cf_t *input,
|
||||||
|
cf_t *ce[MAX_PORTS],
|
||||||
|
uint32_t sf_idx);
|
||||||
|
|
||||||
|
LIBLTE_API int chest_dl_estimate_port(chest_dl_t *q,
|
||||||
|
cf_t *input,
|
||||||
|
cf_t *ce,
|
||||||
|
uint32_t sf_idx,
|
||||||
|
uint32_t port_id);
|
||||||
|
|
||||||
|
LIBLTE_API float chest_dl_get_snr(chest_dl_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API float chest_dl_get_noise_estimate(chest_dl_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API float chest_dl_get_rssi(chest_dl_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API float chest_dl_get_rsrq(chest_dl_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API float chest_dl_get_rsrp(chest_dl_t *q);
|
||||||
|
|
||||||
|
#endif
|
@ -1,92 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef REFSIGNAL_
|
|
||||||
#define REFSIGNAL_
|
|
||||||
|
|
||||||
|
|
||||||
/* Object to manage reference signals for OFDM channel equalization.
|
|
||||||
*
|
|
||||||
* It generates the reference signals for LTE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "liblte/config.h"
|
|
||||||
#include "liblte/phy/common/phy_common.h"
|
|
||||||
|
|
||||||
typedef _Complex float cf_t;
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API{
|
|
||||||
uint32_t time_idx;
|
|
||||||
uint32_t freq_idx;
|
|
||||||
cf_t symbol;
|
|
||||||
}ref_t;
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API{
|
|
||||||
uint32_t nof_refs; // number of reference signals
|
|
||||||
uint32_t *symbols_ref; // symbols with at least one reference
|
|
||||||
uint32_t nsymbols; // number of symbols with at least one reference
|
|
||||||
uint32_t voffset; // offset of the first reference in the freq domain
|
|
||||||
uint32_t nof_prb;
|
|
||||||
ref_t *refs;
|
|
||||||
cf_t *ch_est;
|
|
||||||
cf_t *recv_symbol;
|
|
||||||
} refsignal_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
|
||||||
float beta; // amplitude scaling
|
|
||||||
uint32_t delta_ss; // Set to 0 for PUCCH
|
|
||||||
uint32_t cyclic_shift;
|
|
||||||
uint32_t cyclic_shift_for_drms; /* From DCI 0. Set to 0 if no PDCCH with DCI 0 for the same TB
|
|
||||||
or if the initial PUSCH is semi-persisently scheduled or
|
|
||||||
if the initial PUSCH is scheduled by the RA response grant */
|
|
||||||
bool group_hopping_en;
|
|
||||||
bool sequence_hopping_en;
|
|
||||||
} refsignal_ul_cfg_t;
|
|
||||||
|
|
||||||
|
|
||||||
LIBLTE_API int refsignal_init_LTEDL(refsignal_t *q,
|
|
||||||
uint32_t port_id,
|
|
||||||
uint32_t nslot,
|
|
||||||
lte_cell_t cell);
|
|
||||||
|
|
||||||
LIBLTE_API int refsignal_init_LTEUL_drms_pusch(refsignal_t *q,
|
|
||||||
uint32_t nof_prb,
|
|
||||||
uint32_t prb_start,
|
|
||||||
uint32_t nslot,
|
|
||||||
lte_cell_t cell,
|
|
||||||
refsignal_ul_cfg_t *drms_cfg);
|
|
||||||
|
|
||||||
LIBLTE_API void refsignal_free(refsignal_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API int refsignal_put(refsignal_t *q,
|
|
||||||
cf_t *slot_symbols);
|
|
||||||
|
|
||||||
#endif
|
|
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REFSIGNAL_DL_
|
||||||
|
#define REFSIGNAL_DL_
|
||||||
|
|
||||||
|
/* Object to manage Downlink reference signals for channel estimation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
#include "liblte/phy/common/phy_common.h"
|
||||||
|
|
||||||
|
typedef _Complex float cf_t;
|
||||||
|
|
||||||
|
// Number of references in a subframe: there are 2 symbols for port_id=0,1 x 2 slots x 2 refs per prb
|
||||||
|
#define REFSIGNAL_NUM_SF(nof_prb, port_id) (((port_id)<2?8:4)*(nof_prb))
|
||||||
|
#define REFSIGNAL_MAX_NUM_SF(nof_prb) REFSIGNAL_NUM_SF(nof_prb, 0)
|
||||||
|
|
||||||
|
#define REFSIGNAL_PILOT_IDX(i,l,cell) (2*cell.nof_prb*(l)+(i))
|
||||||
|
|
||||||
|
|
||||||
|
/** Cell-Specific Reference Signal */
|
||||||
|
typedef struct LIBLTE_API {
|
||||||
|
lte_cell_t cell;
|
||||||
|
cf_t *pilots[2][NSUBFRAMES_X_FRAME]; // Saves the reference signal per subframe for ports 0,1 and ports 2,3
|
||||||
|
} refsignal_cs_t;
|
||||||
|
|
||||||
|
|
||||||
|
LIBLTE_API int refsignal_cs_generate(refsignal_cs_t *q,
|
||||||
|
lte_cell_t cell);
|
||||||
|
|
||||||
|
LIBLTE_API void refsignal_cs_free(refsignal_cs_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API int refsignal_cs_put_sf(lte_cell_t cell,
|
||||||
|
uint32_t port_id,
|
||||||
|
cf_t *pilots,
|
||||||
|
cf_t *sf_symbols);
|
||||||
|
|
||||||
|
LIBLTE_API int refsignal_cs_get_sf(lte_cell_t cell,
|
||||||
|
uint32_t port_id,
|
||||||
|
cf_t *sf_symbols,
|
||||||
|
cf_t *pilots);
|
||||||
|
|
||||||
|
LIBLTE_API uint32_t refsignal_fidx(lte_cell_t cell,
|
||||||
|
uint32_t l,
|
||||||
|
uint32_t port_id,
|
||||||
|
uint32_t m);
|
||||||
|
|
||||||
|
LIBLTE_API uint32_t refsignal_nsymbol(uint32_t l,
|
||||||
|
lte_cp_t cp,
|
||||||
|
uint32_t port_id);
|
||||||
|
|
||||||
|
LIBLTE_API uint32_t refsignal_cs_v(uint32_t port_id,
|
||||||
|
uint32_t ref_symbol_idx);
|
||||||
|
|
||||||
|
LIBLTE_API uint32_t refsignal_cs_nof_symbols(uint32_t port_id);
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UE_CELLSEARCH_
|
||||||
|
#define UE_CELLSEARCH_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
#include "liblte/phy/ue/ue_sync.h"
|
||||||
|
#include "liblte/phy/ue/ue_mib.h"
|
||||||
|
#include "liblte/phy/sync/cfo.h"
|
||||||
|
#include "liblte/phy/ch_estimation/chest_dl.h"
|
||||||
|
#include "liblte/phy/phch/pbch.h"
|
||||||
|
#include "liblte/phy/common/fft.h"
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
*
|
||||||
|
* This object is a wrapper to the ue_sync object. It receives
|
||||||
|
* several synchronized frames and obtains the most common cell_id
|
||||||
|
* and cp length.
|
||||||
|
*
|
||||||
|
* The I/O stream device sampling frequency must be set to 1.92 MHz (CS_SAMP_FREQ constant)
|
||||||
|
* before calling to ue_cell_search_scan() functions.
|
||||||
|
*
|
||||||
|
************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Check also peak offset
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CS_DEFAULT_MAXFRAMES_TOTAL 500
|
||||||
|
#define CS_DEFAULT_MAXFRAMES_DETECTED 50
|
||||||
|
|
||||||
|
#define CS_DEFAULT_NOFFRAMES_TOTAL 50
|
||||||
|
#define CS_DEFAULT_NOFFRAMES_DETECTED 10
|
||||||
|
|
||||||
|
#define CS_NOF_PRB 6
|
||||||
|
#define CS_SAMP_FREQ 1920000.0
|
||||||
|
|
||||||
|
typedef struct LIBLTE_API {
|
||||||
|
uint32_t cell_id;
|
||||||
|
lte_cp_t cp;
|
||||||
|
float peak;
|
||||||
|
float mode;
|
||||||
|
float psr;
|
||||||
|
} ue_cell_search_result_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct LIBLTE_API {
|
||||||
|
ue_sync_t ue_sync;
|
||||||
|
|
||||||
|
uint32_t max_frames;
|
||||||
|
uint32_t nof_frames_to_scan; // number of 5 ms frames to scan
|
||||||
|
float detect_threshold; // early-stops scan if mean PSR above this threshold
|
||||||
|
|
||||||
|
uint32_t *mode_ntimes;
|
||||||
|
uint8_t *mode_counted;
|
||||||
|
|
||||||
|
ue_cell_search_result_t *candidates;
|
||||||
|
} ue_cell_search_t;
|
||||||
|
|
||||||
|
|
||||||
|
LIBLTE_API int ue_cell_search_init(ue_cell_search_t *q,
|
||||||
|
int (recv_callback)(void*, void*, uint32_t),
|
||||||
|
void *stream_handler);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_cell_search_init_max(ue_cell_search_t *q,
|
||||||
|
uint32_t max_frames_total,
|
||||||
|
int (recv_callback)(void*, void*, uint32_t),
|
||||||
|
void *stream_handler);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_cell_search_free(ue_cell_search_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_cell_search_scan_N_id_2(ue_cell_search_t *q,
|
||||||
|
uint32_t N_id_2,
|
||||||
|
ue_cell_search_result_t *found_cell);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_cell_search_scan(ue_cell_search_t * q,
|
||||||
|
ue_cell_search_result_t found_cells[3],
|
||||||
|
uint32_t *max_N_id_2);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_cell_search_set_nof_frames_to_scan(ue_cell_search_t *q,
|
||||||
|
uint32_t nof_frames);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_cell_search_set_threshold(ue_cell_search_t *q,
|
||||||
|
float threshold);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SYNC_FRAME_
|
||||||
|
|
@ -1,130 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef UE_CELLSEARCH_
|
|
||||||
#define UE_CELLSEARCH_
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "liblte/config.h"
|
|
||||||
#include "liblte/phy/sync/sync.h"
|
|
||||||
#include "liblte/phy/sync/cfo.h"
|
|
||||||
#include "liblte/phy/ch_estimation/chest.h"
|
|
||||||
#include "liblte/phy/phch/pbch.h"
|
|
||||||
#include "liblte/phy/common/fft.h"
|
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
*
|
|
||||||
* This object scans a signal for LTE cells using the known PSS
|
|
||||||
* and SSS sequences.
|
|
||||||
*
|
|
||||||
* The function ue_celldetect_scan() shall be called multiple times,
|
|
||||||
* each passing a number of samples multiple of 4800, sampled at 960 KHz
|
|
||||||
* (that is, 5 ms of samples).
|
|
||||||
*
|
|
||||||
* The function returns 0 until a signal is found nof_frames_detected times or
|
|
||||||
* after nof_frames_total with no signal detected.
|
|
||||||
*
|
|
||||||
* See ue_cell_detect.c for an example.
|
|
||||||
*
|
|
||||||
************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Check also peak offset
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define CS_DEFAULT_MAXFRAMES_TOTAL 500
|
|
||||||
#define CS_DEFAULT_MAXFRAMES_DETECTED 50
|
|
||||||
|
|
||||||
#define CS_DEFAULT_NOFFRAMES_TOTAL 100
|
|
||||||
#define CS_DEFAULT_NOFFRAMES_DETECTED 10
|
|
||||||
|
|
||||||
#define CS_FIND_THRESHOLD 0.6
|
|
||||||
|
|
||||||
#define CS_FRAME_UNALIGNED -3
|
|
||||||
#define CS_CELL_DETECTED 2
|
|
||||||
#define CS_CELL_NOT_DETECTED 3
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
|
||||||
uint32_t cell_id;
|
|
||||||
lte_cp_t cp;
|
|
||||||
float peak;
|
|
||||||
uint32_t mode;
|
|
||||||
} ue_celldetect_result_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
|
||||||
sync_t sfind;
|
|
||||||
uint32_t max_frames_total;
|
|
||||||
uint32_t max_frames_detected;
|
|
||||||
uint32_t nof_frames_total;
|
|
||||||
uint32_t nof_frames_detected;
|
|
||||||
|
|
||||||
uint32_t current_nof_detected;
|
|
||||||
uint32_t current_nof_total;
|
|
||||||
|
|
||||||
uint32_t *mode_ntimes;
|
|
||||||
char *mode_counted;
|
|
||||||
|
|
||||||
ue_celldetect_result_t *candidates;
|
|
||||||
} ue_celldetect_t;
|
|
||||||
|
|
||||||
|
|
||||||
LIBLTE_API int ue_celldetect_init(ue_celldetect_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API int ue_celldetect_init_max(ue_celldetect_t *q,
|
|
||||||
uint32_t max_frames_total,
|
|
||||||
uint32_t max_frames_detected);
|
|
||||||
|
|
||||||
LIBLTE_API void ue_celldetect_free(ue_celldetect_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API void ue_celldetect_reset(ue_celldetect_t *q);
|
|
||||||
|
|
||||||
LIBLTE_API int ue_celldetect_scan(ue_celldetect_t *q,
|
|
||||||
cf_t *signal,
|
|
||||||
uint32_t nsamples,
|
|
||||||
ue_celldetect_result_t *found_cell,
|
|
||||||
uint32_t N_id_2);
|
|
||||||
|
|
||||||
LIBLTE_API int ue_celldetect_set_nof_frames_total(ue_celldetect_t *q,
|
|
||||||
uint32_t nof_frames);
|
|
||||||
|
|
||||||
LIBLTE_API int ue_celldetect_set_nof_frames_detected(ue_celldetect_t *q,
|
|
||||||
uint32_t nof_frames);
|
|
||||||
|
|
||||||
LIBLTE_API void ue_celldetect_set_threshold(ue_celldetect_t *q,
|
|
||||||
float threshold);
|
|
||||||
|
|
||||||
LIBLTE_API void ue_celldetect_reset(ue_celldetect_t *q);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // SYNC_FRAME_
|
|
||||||
|
|
@ -1,503 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <strings.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <complex.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "liblte/phy/ch_estimation/chest.h"
|
|
||||||
#include "liblte/phy/utils/vector.h"
|
|
||||||
#include "liblte/phy/utils/debug.h"
|
|
||||||
|
|
||||||
#define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz)
|
|
||||||
#define SF_SZ(q) (2 * SLOT_SZ(q))
|
|
||||||
|
|
||||||
//#define VOLK_INTERP
|
|
||||||
|
|
||||||
void chest_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
|
||||||
chest_ref_fprint(q, stream, nslot, port_id);
|
|
||||||
chest_recvsig_fprint(q, stream, nslot, port_id);
|
|
||||||
chest_ce_fprint(q, stream, nslot, port_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sets the number of ports to estimate. nof_ports must be smaler than nof_ports
|
|
||||||
* used during the call to chest_init().
|
|
||||||
*/
|
|
||||||
int chest_set_nof_ports(chest_t *q, uint32_t nof_ports) {
|
|
||||||
if (nof_ports < q->nof_ports) {
|
|
||||||
q->nof_ports = nof_ports;
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
} else {
|
|
||||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void chest_ref_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
|
||||||
int i;
|
|
||||||
fprintf(stream, "refs%d=[",port_id);
|
|
||||||
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
|
|
||||||
fprintf(stream, "%3.3f%+3.3fi, ", __real__ q->refsignal[port_id][nslot].refs[i].symbol,
|
|
||||||
__imag__ q->refsignal[port_id][nslot].refs[i].symbol);
|
|
||||||
}
|
|
||||||
fprintf(stream, "];\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void chest_recvsig_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
|
||||||
int i;
|
|
||||||
fprintf(stream, "recvsig%d=[",port_id);
|
|
||||||
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
|
|
||||||
fprintf(stream, "%3.3f%+3.3fi, ", __real__ q->refsignal[port_id][nslot].recv_symbol[i],
|
|
||||||
__imag__ q->refsignal[port_id][nslot].recv_symbol[i]);
|
|
||||||
}
|
|
||||||
fprintf(stream, "];\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void chest_ce_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
|
||||||
int i;
|
|
||||||
fprintf(stream, "mag%d=[",port_id);
|
|
||||||
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
|
|
||||||
fprintf(stream, "%3.3f, ", cabsf(q->refsignal[port_id][nslot].ch_est[i]));
|
|
||||||
}
|
|
||||||
fprintf(stream, "];\nphase%d=[",port_id);
|
|
||||||
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
|
|
||||||
fprintf(stream, "%3.3f, ", atan2f(__imag__ q->refsignal[port_id][nslot].ch_est[i],
|
|
||||||
__real__ q->refsignal[port_id][nslot].ch_est[i]));
|
|
||||||
}
|
|
||||||
fprintf(stream, "];\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
float chest_rsrp(chest_t *q, uint32_t nslot, uint32_t port_id) {
|
|
||||||
int nof_refs = q->refsignal[port_id][nslot].nof_refs;
|
|
||||||
cf_t *ch_est = q->refsignal[port_id][nslot].ch_est;
|
|
||||||
return crealf(vec_dot_prod_conj_ccc(ch_est, ch_est, nof_refs))/nof_refs;
|
|
||||||
}
|
|
||||||
|
|
||||||
float chest_rsrp_sf(chest_t *q, uint32_t sf_idx) {
|
|
||||||
int n,p;
|
|
||||||
float rsrp=0;
|
|
||||||
for (p=0;p<q->nof_ports;p++) {
|
|
||||||
for (n=0;n<2;n++) {
|
|
||||||
rsrp+=chest_rsrp(q, 2*sf_idx+n, p)/(2*q->nof_ports);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rsrp;
|
|
||||||
}
|
|
||||||
|
|
||||||
float chest_rssi(chest_t *q, cf_t *input) {
|
|
||||||
float rssi = 0;
|
|
||||||
int i;
|
|
||||||
int l[2];
|
|
||||||
if (q->nof_symbols == CPNORM_NSYMB) {
|
|
||||||
l[0] = 0; l[1] = 4;
|
|
||||||
} else {
|
|
||||||
l[0] = 0; l[1] = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0;i<2;i++) {
|
|
||||||
cf_t *tmp = &input[l[i]*q->nof_re];
|
|
||||||
rssi += crealf(vec_dot_prod_conj_ccc(tmp, tmp, q->nof_re));
|
|
||||||
}
|
|
||||||
return rssi;
|
|
||||||
}
|
|
||||||
|
|
||||||
float chest_rssi_sf(chest_t *q, cf_t *input) {
|
|
||||||
int n;
|
|
||||||
int slotsz = q->nof_symbols*q->nof_re;
|
|
||||||
float rssi=0;
|
|
||||||
for (n=0;n<2;n++) {
|
|
||||||
rssi += chest_rssi(q, &input[n*slotsz]);
|
|
||||||
}
|
|
||||||
return rssi;
|
|
||||||
}
|
|
||||||
|
|
||||||
float chest_rsrq(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id) {
|
|
||||||
return (q->nof_re/RE_X_RB) * chest_rsrp(q, nslot, port_id) / chest_rssi(q, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
float chest_rsrq_sf(chest_t *q, cf_t *input, uint32_t sf_idx) {
|
|
||||||
return (4*q->nof_ports*q->nof_re/RE_X_RB) * chest_rsrp_sf(q, sf_idx) / chest_rssi_sf(q, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
int chest_measure_ref(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id, uint32_t nref) {
|
|
||||||
int fidx, tidx;
|
|
||||||
cf_t known_ref, channel_ref;
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
|
|
||||||
if (q != NULL &&
|
|
||||||
input != NULL &&
|
|
||||||
nslot < NSLOTS_X_FRAME &&
|
|
||||||
port_id < q->nof_ports)
|
|
||||||
{
|
|
||||||
if (nref < q->refsignal[port_id][nslot].nof_refs) {
|
|
||||||
|
|
||||||
fidx = q->refsignal[port_id][nslot].refs[nref].freq_idx; // reference frequency index
|
|
||||||
tidx = q->refsignal[port_id][nslot].refs[nref].time_idx; // reference time index
|
|
||||||
|
|
||||||
known_ref = q->refsignal[port_id][nslot].refs[nref].symbol;
|
|
||||||
channel_ref = input[tidx * q->nof_re + fidx];
|
|
||||||
q->refsignal[port_id][nslot].recv_symbol[nref] = channel_ref;
|
|
||||||
|
|
||||||
DEBUG("Reference %2d pos (%2d,%2d)=%3d %.2f dB %.2f/%.2f=%.2f\n", nref, tidx, fidx, tidx * q->nof_re + fidx,
|
|
||||||
10*log10f(cabsf(channel_ref/known_ref)),
|
|
||||||
cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI,
|
|
||||||
cargf(channel_ref/known_ref)/M_PI);
|
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: compare with threshold */
|
|
||||||
if (channel_ref != 0) {
|
|
||||||
q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref;
|
|
||||||
} else {
|
|
||||||
q->refsignal[port_id][nslot].ch_est[nref] = 1e-6;
|
|
||||||
}
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void chest_measure_slot_port(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
refsignal_t *r = &q->refsignal[port_id][nslot];
|
|
||||||
|
|
||||||
DEBUG("Estimating channel slot=%d port=%d using %d reference signals\n",
|
|
||||||
nslot, port_id, r->nof_refs);
|
|
||||||
|
|
||||||
for (i=0;i<r->nof_refs;i++) {
|
|
||||||
chest_measure_ref(q, input, nslot, port_id, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void chest_measure_slot(chest_t *q, cf_t *input, uint32_t nslot) {
|
|
||||||
int p;
|
|
||||||
for (p=0;p<q->nof_ports;p++) {
|
|
||||||
chest_measure_slot_port(q, input, nslot, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void chest_measure_sf(chest_t *q, cf_t *input, uint32_t sf_idx) {
|
|
||||||
int p, n, slotsz;
|
|
||||||
slotsz = q->nof_symbols*q->nof_re;
|
|
||||||
for (p=0;p<q->nof_ports;p++) {
|
|
||||||
for (n=0;n<2;n++) {
|
|
||||||
chest_measure_slot_port(q, &input[n*slotsz], 2*sf_idx+n, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Computes channel estimates for each reference in a slot and port.
|
|
||||||
* Saves the nof_prb * 12 * nof_symbols channel estimates in the array ce
|
|
||||||
*/
|
|
||||||
int chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t nslot, uint32_t port_id)
|
|
||||||
{
|
|
||||||
uint32_t i, j;
|
|
||||||
cf_t x[2], y[MAX_NSYMB];
|
|
||||||
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
|
|
||||||
if (q != NULL &&
|
|
||||||
input != NULL &&
|
|
||||||
nslot < NSLOTS_X_FRAME &&
|
|
||||||
port_id < q->nof_ports)
|
|
||||||
{
|
|
||||||
if (q->refsignal[port_id][nslot].nsymbols <= 2) {
|
|
||||||
refsignal_t *r = &q->refsignal[port_id][nslot];
|
|
||||||
|
|
||||||
chest_measure_slot_port(q, input, nslot, port_id);
|
|
||||||
|
|
||||||
/* interpolate the symbols with references
|
|
||||||
* in the freq domain */
|
|
||||||
for (i=0;i<r->nsymbols;i++) {
|
|
||||||
#ifdef VOLK_INTERP
|
|
||||||
interp_run_offset(&q->interp_freq[port_id],
|
|
||||||
&r->ch_est[i * r->nof_refs/2], &ce[r->symbols_ref[i] * q->nof_re],
|
|
||||||
r->voffset, RE_X_RB/2-r->voffset);
|
|
||||||
#else
|
|
||||||
interp_linear_offset(&r->ch_est[i * r->nof_refs/2],
|
|
||||||
&ce[r->symbols_ref[i] * q->nof_re], RE_X_RB/2,
|
|
||||||
r->nof_refs/2, r->voffset, RE_X_RB/2-r->voffset);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
/* now interpolate in the time domain */
|
|
||||||
for (i=0;i<q->nof_re; i++) {
|
|
||||||
if (r->nsymbols > 1) {
|
|
||||||
for (j=0;j<r->nsymbols;j++) {
|
|
||||||
x[j] = ce[r->symbols_ref[j] * q->nof_re + i];
|
|
||||||
}
|
|
||||||
#ifdef VOLK_INTERP
|
|
||||||
interp_run_offset(&q->interp_time[port_id], x, y,
|
|
||||||
r->symbols_ref[0], 3);
|
|
||||||
#else
|
|
||||||
interp_linear_offset(x, y, r->symbols_ref[1]-r->symbols_ref[0],
|
|
||||||
2, r->symbols_ref[0], 3);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
for (j=0;j<MAX_NSYMB;j++) {
|
|
||||||
y[j] = ce[r->symbols_ref[0] * q->nof_re + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (j=0;j<q->nof_symbols;j++) {
|
|
||||||
ce[j * q->nof_re + i] = y[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Computes channel estimates for each reference in a subframe and port id.
|
|
||||||
*/
|
|
||||||
int chest_ce_sf_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id) {
|
|
||||||
int n, slotsz, ret;
|
|
||||||
slotsz = q->nof_symbols*q->nof_re;
|
|
||||||
for (n=0;n<2;n++) {
|
|
||||||
ret = chest_ce_slot_port(q, &input[n*slotsz], &ce[n*slotsz], 2*sf_idx+n, port_id);
|
|
||||||
if (ret != LIBLTE_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Computes channel estimates for each reference in a slot for all ports.
|
|
||||||
*/
|
|
||||||
int chest_ce_slot(chest_t *q, cf_t *input, cf_t **ce, uint32_t nslot) {
|
|
||||||
int p, ret;
|
|
||||||
for (p=0;p<q->nof_ports;p++) {
|
|
||||||
ret = chest_ce_slot_port(q, input, ce[p], nslot, p);
|
|
||||||
if (ret != LIBLTE_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Computes channel estimates for each reference in a subframe for all ports.
|
|
||||||
*/
|
|
||||||
int chest_ce_sf(chest_t *q, cf_t *input, cf_t *ce[MAX_PORTS], uint32_t sf_idx) {
|
|
||||||
int p, n, slotsz, ret;
|
|
||||||
slotsz = q->nof_symbols*q->nof_re;
|
|
||||||
for (p=0;p<q->nof_ports;p++) {
|
|
||||||
for (n=0;n<2;n++) {
|
|
||||||
ret = chest_ce_slot_port(q, &input[n*slotsz], &ce[p][n*slotsz], 2*sf_idx+n, p);
|
|
||||||
if (ret != LIBLTE_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int chest_init(chest_t *q, uint32_t nof_re, uint32_t nof_symbols, uint32_t nof_ports) {
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
|
|
||||||
if (q != NULL &&
|
|
||||||
nof_ports <= MAX_PORTS)
|
|
||||||
{
|
|
||||||
bzero(q, sizeof(chest_t));
|
|
||||||
|
|
||||||
q->nof_ports = nof_ports;
|
|
||||||
q->nof_symbols = nof_symbols;
|
|
||||||
q->nof_re = nof_re;
|
|
||||||
|
|
||||||
INFO("Initializing channel estimator size %dx%d, nof_ports=%d\n",
|
|
||||||
q->nof_symbols, q->nof_re, nof_ports);
|
|
||||||
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void chest_free(chest_t *q) {
|
|
||||||
int p, n;
|
|
||||||
for (p=0;p<q->nof_ports;p++) {
|
|
||||||
for (n=0;n<NSLOTS_X_FRAME;n++) {
|
|
||||||
refsignal_free(&q->refsignal[p][n]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef VOLK_INTERP
|
|
||||||
for (p=0;p<MAX_PORTS;p++) {
|
|
||||||
interp_free(&q->interp_freq[p]);
|
|
||||||
interp_free(&q->interp_time[p]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
bzero(q, sizeof(chest_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fills l[2] with the symbols in the slot nslot that contain references.
|
|
||||||
* returns the number of symbols with references (in the slot)
|
|
||||||
*/
|
|
||||||
int chest_ref_get_symbols(chest_t *q, uint32_t port_id, uint32_t nslot, uint32_t l[2]) {
|
|
||||||
|
|
||||||
if (q != NULL &&
|
|
||||||
port_id < MAX_PORTS &&
|
|
||||||
nslot < NSLOTS_X_FRAME)
|
|
||||||
{
|
|
||||||
memcpy(l, q->refsignal[port_id][nslot].symbols_ref, sizeof(uint32_t) * q->refsignal[port_id][nslot].nsymbols);
|
|
||||||
return q->refsignal[port_id][nslot].nsymbols;
|
|
||||||
} else {
|
|
||||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* Downlink Channel estimator
|
|
||||||
*
|
|
||||||
*********************************************************************/
|
|
||||||
int chest_init_LTEDL(chest_t *q, lte_cell_t cell) {
|
|
||||||
int ret;
|
|
||||||
ret = chest_init(q, cell.nof_prb * RE_X_RB, CP_NSYMB(cell.cp), cell.nof_ports);
|
|
||||||
if (ret != LIBLTE_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
return chest_ref_set_LTEDL(q, cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int chest_ref_set_LTEDL_slot_port(chest_t *q, uint32_t nslot, uint32_t port_id, lte_cell_t cell) {
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
|
|
||||||
if (q != NULL &&
|
|
||||||
port_id < MAX_PORTS &&
|
|
||||||
nslot < NSLOTS_X_FRAME)
|
|
||||||
{
|
|
||||||
ret = refsignal_init_LTEDL(&q->refsignal[port_id][nslot], port_id, nslot, cell);
|
|
||||||
|
|
||||||
#ifdef VOLK_INTERP
|
|
||||||
if (ret == LIBLTE_SUCCESS) {
|
|
||||||
if (nslot == 0) {
|
|
||||||
ret = interp_init(&q->interp_freq[port_id], LINEAR, q->refsignal[port_id][nslot].nof_refs/2, RE_X_RB/2);
|
|
||||||
if (ret == LIBLTE_SUCCESS) {
|
|
||||||
ret = interp_init(&q->interp_time[port_id], LINEAR, 2,
|
|
||||||
q->refsignal[port_id][nslot].symbols_ref[1] - q->refsignal[port_id][nslot].symbols_ref[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int chest_ref_set_LTEDL_slot(chest_t *q, uint32_t nslot, lte_cell_t cell) {
|
|
||||||
int p, ret;
|
|
||||||
for (p=0;p<q->nof_ports;p++) {
|
|
||||||
ret = chest_ref_set_LTEDL_slot_port(q, nslot, p, cell);
|
|
||||||
if (ret != LIBLTE_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int chest_ref_set_LTEDL(chest_t *q, lte_cell_t cell) {
|
|
||||||
int n, ret;
|
|
||||||
for (n=0;n<NSLOTS_X_FRAME;n++) {
|
|
||||||
ret = chest_ref_set_LTEDL_slot(q, n, cell);
|
|
||||||
if (ret != LIBLTE_SUCCESS) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* TODO: Uplink Channel estimator
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*********************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** High-level API
|
|
||||||
*/
|
|
||||||
int chest_initialize(chest_hl* h) {
|
|
||||||
|
|
||||||
lte_cell_t cell;
|
|
||||||
|
|
||||||
if (!h->init.nof_symbols) {
|
|
||||||
h->init.nof_symbols = CPNORM_NSYMB; // Normal CP
|
|
||||||
}
|
|
||||||
if (!h->init.nof_prb) {
|
|
||||||
h->init.nof_prb = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
cell.id = h->init.cell_id;
|
|
||||||
cell.nof_ports = h->init.nof_ports;
|
|
||||||
cell.nof_prb = h->init.nof_prb;
|
|
||||||
cell.cp = h->init.nof_symbols == CPNORM_NSYMB ? CPNORM : CPEXT;
|
|
||||||
|
|
||||||
if (chest_init_LTEDL(&h->obj, cell)) {
|
|
||||||
fprintf(stderr, "Error initializing equalizer\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This function must be called in an subframe basis (1ms) for LTE */
|
|
||||||
int chest_work(chest_hl* hl) {
|
|
||||||
chest_t *q = &hl->obj;
|
|
||||||
chest_ce_sf(q, hl->input, hl->output, hl->ctrl_in.sf_idx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int chest_stop(chest_hl* hl) {
|
|
||||||
chest_free(&hl->obj);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,385 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <complex.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
|
||||||
|
#include "liblte/phy/ch_estimation/chest_dl.h"
|
||||||
|
#include "liblte/phy/utils/vector.h"
|
||||||
|
#include "liblte/phy/utils/convolution.h"
|
||||||
|
|
||||||
|
#define CHEST_RS_AVERAGE_TIME 2
|
||||||
|
#define CHEST_RS_AVERAGE_FREQ 3
|
||||||
|
|
||||||
|
|
||||||
|
/** 3GPP LTE Downlink channel estimator and equalizer.
|
||||||
|
* Estimates the channel in the resource elements transmitting references and interpolates for the rest
|
||||||
|
* of the resource grid.
|
||||||
|
*
|
||||||
|
* The equalizer uses the channel estimates to produce an estimation of the transmitted symbol.
|
||||||
|
*
|
||||||
|
* This object depends on the refsignal_t object for creating the LTE CSR signal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int chest_dl_init(chest_dl_t *q, lte_cell_t cell)
|
||||||
|
{
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
if (q != NULL &&
|
||||||
|
lte_cell_isvalid(&cell))
|
||||||
|
{
|
||||||
|
bzero(q, sizeof(chest_dl_t));
|
||||||
|
|
||||||
|
ret = refsignal_cs_generate(&q->csr_signal, cell);
|
||||||
|
if (ret != LIBLTE_SUCCESS) {
|
||||||
|
fprintf(stderr, "Error initializing CSR signal (%d)\n",ret);
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->tmp_freqavg = vec_malloc(sizeof(cf_t) * REFSIGNAL_MAX_NUM_SF(cell.nof_prb));
|
||||||
|
if (!q->tmp_freqavg) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
q->tmp_noise = vec_malloc(sizeof(cf_t) * REFSIGNAL_MAX_NUM_SF(cell.nof_prb));
|
||||||
|
if (!q->tmp_noise) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
for (int i=0;i<CHEST_MAX_FILTER_TIME_LEN;i++) {
|
||||||
|
q->tmp_timeavg[i] = vec_malloc(sizeof(cf_t) * 2*cell.nof_prb);
|
||||||
|
if (!q->tmp_timeavg[i]) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
bzero(q->tmp_timeavg[i], sizeof(cf_t) * 2*cell.nof_prb);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0;i<cell.nof_ports;i++) {
|
||||||
|
q->pilot_estimates[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_NUM_SF(cell.nof_prb, i));
|
||||||
|
if (!q->pilot_estimates[i]) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
q->pilot_estimates_average[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_NUM_SF(cell.nof_prb, i));
|
||||||
|
if (!q->pilot_estimates_average[i]) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
q->pilot_recv_signal[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_NUM_SF(cell.nof_prb, i));
|
||||||
|
if (!q->pilot_recv_signal[i]) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interp_linear_vector_init(&q->interp_linvec, RE_X_RB*cell.nof_prb)) {
|
||||||
|
fprintf(stderr, "Error initializing vector interpolator\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interp_linear_init(&q->interp_lin, 2*cell.nof_prb, RE_X_RB/2)) {
|
||||||
|
fprintf(stderr, "Error initializing interpolator\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set default time/freq filters */
|
||||||
|
//float f[3]={0.1, 0.8, 0.1};
|
||||||
|
//chest_dl_set_filter_freq(q, f, 3);
|
||||||
|
|
||||||
|
float f[5]={0.05, 0.15, 0.6, 0.15, 0.05};
|
||||||
|
chest_dl_set_filter_freq(q, f, 5);
|
||||||
|
|
||||||
|
float t[2]={0.1, 0.9};
|
||||||
|
chest_dl_set_filter_time(q, t, 2);
|
||||||
|
|
||||||
|
q->cell = cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = LIBLTE_SUCCESS;
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
if (ret != LIBLTE_SUCCESS) {
|
||||||
|
chest_dl_free(q);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void chest_dl_free(chest_dl_t *q)
|
||||||
|
{
|
||||||
|
refsignal_cs_free(&q->csr_signal);
|
||||||
|
|
||||||
|
if (q->tmp_freqavg) {
|
||||||
|
free(q->tmp_freqavg);
|
||||||
|
}
|
||||||
|
if (q->tmp_noise) {
|
||||||
|
free(q->tmp_noise);
|
||||||
|
}
|
||||||
|
for (int i=0;i<CHEST_MAX_FILTER_TIME_LEN;i++) {
|
||||||
|
if (q->tmp_timeavg[i]) {
|
||||||
|
free(q->tmp_timeavg[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interp_linear_vector_free(&q->interp_linvec);
|
||||||
|
interp_linear_free(&q->interp_lin);
|
||||||
|
|
||||||
|
for (int i=0;i<MAX_PORTS;i++) {
|
||||||
|
if (q->pilot_estimates[i]) {
|
||||||
|
free(q->pilot_estimates[i]);
|
||||||
|
}
|
||||||
|
if (q->pilot_estimates_average[i]) {
|
||||||
|
free(q->pilot_estimates_average[i]);
|
||||||
|
}
|
||||||
|
if (q->pilot_recv_signal[i]) {
|
||||||
|
free(q->pilot_recv_signal[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bzero(q, sizeof(chest_dl_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
int chest_dl_set_filter_freq(chest_dl_t *q, float *filter, uint32_t filter_len) {
|
||||||
|
if (filter_len <= CHEST_MAX_FILTER_FREQ_LEN) {
|
||||||
|
q->filter_freq_len = filter_len;
|
||||||
|
for (int i=0;i<filter_len;i++) {
|
||||||
|
q->filter_freq[i] = filter[i];
|
||||||
|
}
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int chest_dl_set_filter_time(chest_dl_t *q, float *filter, uint32_t filter_len) {
|
||||||
|
if (filter_len <= CHEST_MAX_FILTER_TIME_LEN) {
|
||||||
|
q->filter_time_len = filter_len;
|
||||||
|
for (int i=0;i<filter_len;i++) {
|
||||||
|
q->filter_time[i] = filter[i];
|
||||||
|
}
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static float estimate_noise_port(chest_dl_t *q, uint32_t port_id, cf_t *avg_pilots) {
|
||||||
|
/* Use difference between averaged and noisy LS pilot estimates */
|
||||||
|
vec_sub_ccc(avg_pilots, q->pilot_estimates[port_id],
|
||||||
|
q->tmp_noise, REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
|
||||||
|
|
||||||
|
return vec_avg_power_cf(q->tmp_noise, REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define pilot_est(idx) q->pilot_estimates[port_id][REFSIGNAL_PILOT_IDX(idx,l,q->cell)]
|
||||||
|
#define pilot_avg(idx) q->pilot_estimates_average[port_id][REFSIGNAL_PILOT_IDX(idx,l,q->cell)]
|
||||||
|
#define pilot_tmp(idx) q->tmp_freqavg[REFSIGNAL_PILOT_IDX(idx,l,q->cell)]
|
||||||
|
|
||||||
|
static void average_pilots(chest_dl_t *q, uint32_t port_id)
|
||||||
|
{
|
||||||
|
int nref=2*q->cell.nof_prb;
|
||||||
|
uint32_t l, i;
|
||||||
|
|
||||||
|
/* For each symbol with pilots in a slot */
|
||||||
|
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
|
||||||
|
if (q->filter_freq_len > 0) {
|
||||||
|
/* Filter pilot estimates in frequency */
|
||||||
|
conv_same_cf(&pilot_est(0), q->filter_freq, &pilot_tmp(0), nref, q->filter_freq_len);
|
||||||
|
|
||||||
|
/* Adjust extremes using linear interpolation */
|
||||||
|
pilot_tmp(0) += interp_linear_onesample(pilot_est(1), pilot_est(0))
|
||||||
|
* q->filter_freq[q->filter_freq_len/2-1];
|
||||||
|
pilot_tmp(nref-1) += interp_linear_onesample(pilot_est(nref-2), pilot_est(nref-1))
|
||||||
|
* q->filter_freq[q->filter_freq_len/2+1];
|
||||||
|
} else {
|
||||||
|
memcpy(&pilot_tmp(0), &pilot_est(0), nref * sizeof(cf_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute noise estimation before time averaging.
|
||||||
|
* FIXME: Apparently the noise estimation performance is better with frequency averaging only
|
||||||
|
*/
|
||||||
|
q->noise_estimate[port_id] = estimate_noise_port(q, port_id, q->tmp_freqavg);
|
||||||
|
|
||||||
|
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
|
||||||
|
/* Filter in time domain. */
|
||||||
|
if (q->filter_time_len > 0) {
|
||||||
|
/* Move last symbols */
|
||||||
|
for (i=0;i<q->filter_time_len-1;i++) {
|
||||||
|
memcpy(q->tmp_timeavg[i], q->tmp_timeavg[i+1], nref*sizeof(cf_t));
|
||||||
|
}
|
||||||
|
/* Put last symbol to buffer */
|
||||||
|
memcpy(q->tmp_timeavg[i], &pilot_tmp(0), nref*sizeof(cf_t));
|
||||||
|
|
||||||
|
/* Multiply all symbols by filter and add them */
|
||||||
|
bzero(&pilot_avg(0), nref * sizeof(cf_t));
|
||||||
|
for (i=0;i<q->filter_time_len;i++) {
|
||||||
|
vec_sc_prod_cfc(q->tmp_timeavg[i], q->filter_time[i], q->tmp_timeavg[i], nref);
|
||||||
|
vec_sum_ccc(q->tmp_timeavg[i], &pilot_avg(0), &pilot_avg(0), nref);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(&pilot_avg(0), &pilot_tmp(0), nref * sizeof(cf_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define cesymb(i) ce[RE_IDX(q->cell.nof_prb,i,0)]
|
||||||
|
|
||||||
|
static void interpolate_pilots(chest_dl_t *q, cf_t *ce, uint32_t port_id)
|
||||||
|
{
|
||||||
|
/* interpolate the symbols with references in the freq domain */
|
||||||
|
uint32_t l;
|
||||||
|
uint32_t nsymbols = refsignal_cs_nof_symbols(port_id);
|
||||||
|
|
||||||
|
/* Interpolate in the frequency domain */
|
||||||
|
for (l=0;l<nsymbols;l++) {
|
||||||
|
uint32_t fidx_offset = refsignal_fidx(q->cell, l, port_id, 0);
|
||||||
|
interp_linear_offset(&q->interp_lin, &pilot_avg(0),
|
||||||
|
&ce[refsignal_nsymbol(l,q->cell.cp, port_id) * q->cell.nof_prb * RE_X_RB],
|
||||||
|
fidx_offset, RE_X_RB/2-fidx_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now interpolate in the time domain between symbols */
|
||||||
|
if (CP_ISNORM(q->cell.cp)) {
|
||||||
|
if (nsymbols == 4) {
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(0), &cesymb(4), &cesymb(1), 3);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(4), &cesymb(7), &cesymb(5), 2);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(7), &cesymb(11), &cesymb(8), 3);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(7), &cesymb(11), &cesymb(12), 2);
|
||||||
|
} else {
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(8), &cesymb(1), &cesymb(0), 1);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(1), &cesymb(8), &cesymb(2), 6);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(1), &cesymb(8), &cesymb(9), 5);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (nsymbols == 4) {
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(0), &cesymb(3), &cesymb(1), 2);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(3), &cesymb(6), &cesymb(4), 2);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(6), &cesymb(9), &cesymb(7), 2);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(6), &cesymb(9), &cesymb(9), 2);
|
||||||
|
} else {
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(7), &cesymb(1), &cesymb(0), 1);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(1), &cesymb(7), &cesymb(2), 5);
|
||||||
|
interp_linear_vector(&q->interp_linvec, &cesymb(1), &cesymb(7), &cesymb(8), 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float chest_dl_rssi(chest_dl_t *q, cf_t *input, uint32_t port_id) {
|
||||||
|
uint32_t l;
|
||||||
|
|
||||||
|
float rssi = 0;
|
||||||
|
uint32_t nsymbols = refsignal_cs_nof_symbols(port_id);
|
||||||
|
for (l=0;l<nsymbols;l++) {
|
||||||
|
cf_t *tmp = &input[refsignal_nsymbol(l,q->cell.cp, port_id) * q->cell.nof_prb * RE_X_RB];
|
||||||
|
rssi += vec_dot_prod_conj_ccc(tmp,tmp,q->cell.nof_prb * RE_X_RB);
|
||||||
|
}
|
||||||
|
return rssi/nsymbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
float chest_dl_rsrp(chest_dl_t *q, uint32_t port_id) {
|
||||||
|
#ifdef RSRP_FROM_ESTIMATES
|
||||||
|
return vec_avg_power_cf(q->pilot_estimates[port_id],
|
||||||
|
REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
|
||||||
|
#else
|
||||||
|
return vec_avg_power_cf(q->pilot_estimates_average[port_id],
|
||||||
|
REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int chest_dl_estimate_port(chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id)
|
||||||
|
{
|
||||||
|
/* Get references from the input signal */
|
||||||
|
refsignal_cs_get_sf(q->cell, port_id, input, q->pilot_recv_signal[port_id]);
|
||||||
|
|
||||||
|
/* Compute RSRP for the references in this port */
|
||||||
|
q->rsrp[port_id] = chest_dl_rsrp(q, port_id);
|
||||||
|
if (port_id == 0) {
|
||||||
|
/* compute rssi only for port 0 */
|
||||||
|
q->rssi[port_id] = chest_dl_rssi(q, input, port_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use the known CSR signal to compute Least-squares estimates */
|
||||||
|
vec_prod_conj_ccc(q->pilot_recv_signal[port_id], q->csr_signal.pilots[port_id/2][sf_idx],
|
||||||
|
q->pilot_estimates[port_id], REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
|
||||||
|
|
||||||
|
/* Average pilot estimates */
|
||||||
|
average_pilots(q, port_id);
|
||||||
|
|
||||||
|
/* Interpolate to create channel estimates for all resource grid */
|
||||||
|
if (ce != NULL) {
|
||||||
|
interpolate_pilots(q, ce, port_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chest_dl_estimate(chest_dl_t *q, cf_t *input, cf_t *ce[MAX_PORTS], uint32_t sf_idx)
|
||||||
|
{
|
||||||
|
uint32_t port_id;
|
||||||
|
|
||||||
|
for (port_id=0;port_id<q->cell.nof_ports;port_id++) {
|
||||||
|
chest_dl_estimate_port(q, input, ce[port_id], sf_idx, port_id);
|
||||||
|
}
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
float chest_dl_get_noise_estimate(chest_dl_t *q) {
|
||||||
|
return vec_acc_ff(q->noise_estimate, q->cell.nof_ports)/q->cell.nof_ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
float chest_dl_get_snr(chest_dl_t *q) {
|
||||||
|
// Uses RSRP as an estimation of the useful signal power
|
||||||
|
return chest_dl_get_rsrp(q)/chest_dl_get_noise_estimate(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
float chest_dl_get_rssi(chest_dl_t *q) {
|
||||||
|
return 4*q->rssi[0]/q->cell.nof_prb/RE_X_RB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* q->rssi[0] is the average power in all RE in all symbol containing references for port 0 . q->rssi[0]/q->cell.nof_prb is the average power per PRB
|
||||||
|
* q->rsrp[0] is the average power of RE containing references only (for port 0).
|
||||||
|
*/
|
||||||
|
float chest_dl_get_rsrq(chest_dl_t *q) {
|
||||||
|
return q->cell.nof_prb*q->rsrp[0] / q->rssi[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float chest_dl_get_rsrp(chest_dl_t *q) {
|
||||||
|
// return linear average from port 0 only
|
||||||
|
//return q->rsrp[0];
|
||||||
|
|
||||||
|
// return linear average from all ports
|
||||||
|
return vec_acc_ff(q->rsrp, q->cell.nof_ports)/q->cell.nof_ports;
|
||||||
|
}
|
||||||
|
|
@ -1,382 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <complex.h>
|
|
||||||
|
|
||||||
#include "liblte/phy/common/phy_common.h"
|
|
||||||
#include "liblte/phy/ch_estimation/refsignal.h"
|
|
||||||
#include "liblte/phy/utils/vector.h"
|
|
||||||
#include "liblte/phy/utils/debug.h"
|
|
||||||
#include "liblte/phy/common/sequence.h"
|
|
||||||
|
|
||||||
#include "ul_rs_tables.h"
|
|
||||||
|
|
||||||
#define idx(x, y) (l*nof_refs_x_symbol+i)
|
|
||||||
|
|
||||||
int refsignal_v(uint32_t port_id, uint32_t ns, uint32_t symbol_id)
|
|
||||||
{
|
|
||||||
int v = -1;
|
|
||||||
switch (port_id) {
|
|
||||||
case 0:
|
|
||||||
if (symbol_id == 0) {
|
|
||||||
v = 0;
|
|
||||||
} else {
|
|
||||||
v = 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (symbol_id == 0) {
|
|
||||||
v = 3;
|
|
||||||
} else {
|
|
||||||
v = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
v = 3 * (ns % 2);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
v = 3 + 3 * (ns % 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t refsignal_k(uint32_t m, uint32_t v, uint32_t cell_id)
|
|
||||||
{
|
|
||||||
return 6 * m + ((v + (cell_id % 6)) % 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
int refsignal_put(refsignal_t * q, cf_t * slot_symbols)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
uint32_t fidx, tidx;
|
|
||||||
if (q != NULL && slot_symbols != NULL) {
|
|
||||||
for (i = 0; i < q->nof_refs; i++) {
|
|
||||||
fidx = q->refs[i].freq_idx; // reference frequency index
|
|
||||||
tidx = q->refs[i].time_idx; // reference time index
|
|
||||||
slot_symbols[SAMPLE_IDX(q->nof_prb, tidx, fidx)] = q->refs[i].symbol;
|
|
||||||
}
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
} else {
|
|
||||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initializes refsignal_t object according to 3GPP 36.211 6.10.1
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int refsignal_init_LTEDL(refsignal_t * q, uint32_t port_id, uint32_t nslot,
|
|
||||||
lte_cell_t cell)
|
|
||||||
{
|
|
||||||
|
|
||||||
uint32_t c_init;
|
|
||||||
uint32_t ns, l, lp[2];
|
|
||||||
uint32_t N_cp;
|
|
||||||
uint32_t i;
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
sequence_t seq;
|
|
||||||
int v;
|
|
||||||
uint32_t mp;
|
|
||||||
uint32_t nof_refs_x_symbol, nof_ref_symbols;
|
|
||||||
|
|
||||||
if (q != NULL &&
|
|
||||||
port_id < MAX_PORTS &&
|
|
||||||
nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
|
|
||||||
|
|
||||||
bzero(q, sizeof(refsignal_t));
|
|
||||||
bzero(&seq, sizeof(sequence_t));
|
|
||||||
|
|
||||||
if (CP_ISNORM(cell.cp)) {
|
|
||||||
N_cp = 1;
|
|
||||||
} else {
|
|
||||||
N_cp = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (port_id < 2) {
|
|
||||||
nof_ref_symbols = 2;
|
|
||||||
lp[0] = 0;
|
|
||||||
lp[1] = CP_NSYMB(cell.cp) - 3;
|
|
||||||
} else {
|
|
||||||
nof_ref_symbols = 1;
|
|
||||||
lp[0] = 1;
|
|
||||||
}
|
|
||||||
nof_refs_x_symbol = 2 * cell.nof_prb;
|
|
||||||
|
|
||||||
q->nof_refs = nof_refs_x_symbol * nof_ref_symbols;
|
|
||||||
q->nsymbols = nof_ref_symbols;
|
|
||||||
q->voffset = cell.id % 6;
|
|
||||||
q->nof_prb = cell.nof_prb;
|
|
||||||
|
|
||||||
q->symbols_ref = malloc(sizeof(uint32_t) * nof_ref_symbols);
|
|
||||||
if (!q->symbols_ref) {
|
|
||||||
perror("malloc");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(q->symbols_ref, lp, sizeof(uint32_t) * nof_ref_symbols);
|
|
||||||
|
|
||||||
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
|
|
||||||
if (!q->refs) {
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
|
|
||||||
if (!q->ch_est) {
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
q->recv_symbol = vec_malloc(q->nof_refs * sizeof(cf_t));
|
|
||||||
if (!q->recv_symbol) {
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
ns = nslot;
|
|
||||||
for (l = 0; l < nof_ref_symbols; l++) {
|
|
||||||
|
|
||||||
c_init = 1024 * (7 * (ns + 1) + lp[l] + 1) * (2 * cell.id + 1)
|
|
||||||
+ 2 * cell.id + N_cp;
|
|
||||||
ret = sequence_LTE_pr(&seq, 2 * 2 * MAX_PRB, c_init);
|
|
||||||
if (ret != LIBLTE_SUCCESS) {
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
v = refsignal_v(port_id, ns, lp[l]);
|
|
||||||
|
|
||||||
for (i = 0; i < nof_refs_x_symbol; i++) {
|
|
||||||
mp = i + MAX_PRB - cell.nof_prb;
|
|
||||||
|
|
||||||
/* generate signal */
|
|
||||||
__real__ q->refs[idx(l, i)].symbol =
|
|
||||||
(1 - 2 * (float) seq.c[2 * mp]) / sqrt(2);
|
|
||||||
__imag__ q->refs[idx(l, i)].symbol =
|
|
||||||
(1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2);
|
|
||||||
|
|
||||||
/* mapping to resource elements */
|
|
||||||
q->refs[idx(l, i)].freq_idx = refsignal_k(i, (uint32_t) v, cell.id);
|
|
||||||
q->refs[idx(l, i)].time_idx = lp[l];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
free_and_exit:
|
|
||||||
if (ret != LIBLTE_ERROR_INVALID_INPUTS) {
|
|
||||||
sequence_free(&seq);
|
|
||||||
}
|
|
||||||
if (ret == LIBLTE_ERROR) {
|
|
||||||
refsignal_free(q);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// n_drms_2 table 5.5.2.1.1-1 from 36.211
|
|
||||||
uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 };
|
|
||||||
|
|
||||||
// n_drms_1 table 5.5.2.1.1-2 from 36.211
|
|
||||||
uint32_t n_drms_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 };
|
|
||||||
|
|
||||||
|
|
||||||
/* Generation of the reference signal sequence according to Section 5.5.1 of 36.211 */
|
|
||||||
int rs_sequence(ref_t * refs, uint32_t len, float alpha, uint32_t ns, uint32_t cell_id,
|
|
||||||
refsignal_ul_cfg_t * cfg)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
// Calculate u and v
|
|
||||||
uint32_t u, v;
|
|
||||||
uint32_t f_ss = (((cell_id % 30) + cfg->delta_ss) % 30);
|
|
||||||
if (cfg->group_hopping_en) {
|
|
||||||
sequence_t seq;
|
|
||||||
sequence_LTE_pr(&seq, cell_id / 30, 160);
|
|
||||||
uint32_t f_gh = 0;
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
f_gh += seq.c[8 * ns + i] << i;
|
|
||||||
}
|
|
||||||
sequence_free(&seq);
|
|
||||||
u = ((f_gh%30) + f_ss) % 30;
|
|
||||||
} else {
|
|
||||||
u = f_ss % 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 6 * RE_X_RB) {
|
|
||||||
v = 0;
|
|
||||||
} else {
|
|
||||||
if (!cfg->group_hopping_en && cfg->sequence_hopping_en) {
|
|
||||||
sequence_t seq;
|
|
||||||
sequence_LTE_pr(&seq, ((cell_id / 30) << 5) + f_ss, 20);
|
|
||||||
v = seq.c[ns];
|
|
||||||
sequence_free(&seq);
|
|
||||||
} else {
|
|
||||||
v = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (len >= 3 * RE_X_RB) {
|
|
||||||
uint32_t n_sz;
|
|
||||||
uint32_t q;
|
|
||||||
float q_hat;
|
|
||||||
/* get largest prime n_zc<len */
|
|
||||||
for (i = NOF_PRIME_NUMBERS - 1; i > 0; i--) {
|
|
||||||
if (prime_numbers[i] < len) {
|
|
||||||
n_sz = prime_numbers[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
q_hat = (float) n_sz *(u + 1) / 31;
|
|
||||||
if ((((uint32_t) (2 * q_hat)) % 2) == 0) {
|
|
||||||
q = (uint32_t) (q_hat + 0.5) + v;
|
|
||||||
} else {
|
|
||||||
q = (uint32_t) (q_hat + 0.5) - v;
|
|
||||||
}
|
|
||||||
cf_t *x_q = malloc(sizeof(cf_t) * n_sz);
|
|
||||||
if (!x_q) {
|
|
||||||
perror("malloc");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
for (i = 0; i < n_sz; i++) {
|
|
||||||
x_q[i] =
|
|
||||||
cexpf(-I * M_PI * (float) q * (float) i * ((float) i + 1) / n_sz);
|
|
||||||
}
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
refs[i].symbol = cfg->beta * cexpf(I * alpha * i) * x_q[i % n_sz];
|
|
||||||
}
|
|
||||||
free(x_q);
|
|
||||||
} else {
|
|
||||||
if (len == RE_X_RB) {
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
refs[i].symbol = cfg->beta * cexpf(I * (phi_M_sc_12[u][i] * M_PI / 4 + alpha * i));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
refs[i].symbol = cfg->beta * cexpf(I * (phi_M_sc_24[u][i] * M_PI / 4 + alpha * i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initializes refsignal_t object according to 3GPP 36.211 5.5.2
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int refsignal_init_LTEUL_drms_pusch(refsignal_t * q, uint32_t nof_prb, uint32_t prb_start,
|
|
||||||
uint32_t nslot, lte_cell_t cell, refsignal_ul_cfg_t * cfg)
|
|
||||||
{
|
|
||||||
|
|
||||||
uint32_t i;
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
uint32_t n_prs;
|
|
||||||
uint32_t M_sc;
|
|
||||||
float alpha;
|
|
||||||
|
|
||||||
if (q != NULL && nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
|
|
||||||
|
|
||||||
bzero(q, sizeof(refsignal_t));
|
|
||||||
|
|
||||||
M_sc = nof_prb * RE_X_RB;
|
|
||||||
|
|
||||||
q->nof_refs = M_sc;
|
|
||||||
q->nsymbols = 1;
|
|
||||||
q->voffset = cell.id % 6;
|
|
||||||
q->nof_prb = cell.nof_prb;
|
|
||||||
|
|
||||||
q->symbols_ref = malloc(sizeof(uint32_t) * 1);
|
|
||||||
if (!q->symbols_ref) {
|
|
||||||
perror("malloc");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CP_ISNORM(cell.cp)) {
|
|
||||||
q->symbols_ref[0] = 3;
|
|
||||||
} else {
|
|
||||||
q->symbols_ref[0] = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
|
|
||||||
if (!q->refs) {
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
|
|
||||||
if (!q->ch_est) {
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate n_prs */
|
|
||||||
uint32_t c_init;
|
|
||||||
sequence_t seq;
|
|
||||||
c_init = ((cell.id / 30) << 5) + (((cell.id % 30) + cfg->delta_ss) % 30);
|
|
||||||
ret = sequence_LTE_pr(&seq, 8 * CP_NSYMB(cell.cp) * 20, c_init);
|
|
||||||
if (ret != LIBLTE_SUCCESS) {
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
n_prs = 0;
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
n_prs += (seq.c[8 * CP_NSYMB(cell.cp) * nslot + i] << i);
|
|
||||||
}
|
|
||||||
sequence_free(&seq);
|
|
||||||
|
|
||||||
// Calculate cyclic shift alpha
|
|
||||||
uint32_t n_cs =
|
|
||||||
(n_drms_1[cfg->cyclic_shift] +
|
|
||||||
n_drms_2[cfg->cyclic_shift_for_drms] + n_prs) % 12;
|
|
||||||
alpha = 2 * M_PI * (n_cs) / 12;
|
|
||||||
|
|
||||||
if (rs_sequence(q->refs, M_sc, alpha, cell.id, nslot, cfg)) {
|
|
||||||
fprintf(stderr, "Error generating RS sequence\n");
|
|
||||||
goto free_and_exit;
|
|
||||||
}
|
|
||||||
/* mapping to resource elements */
|
|
||||||
for (i=0;i<M_sc;i++) {
|
|
||||||
q->refs[i].freq_idx = prb_start*RE_X_RB + i;
|
|
||||||
q->refs[i].time_idx = q->symbols_ref[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
free_and_exit:
|
|
||||||
if (ret == LIBLTE_ERROR) {
|
|
||||||
refsignal_free(q);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void refsignal_free(refsignal_t * q)
|
|
||||||
{
|
|
||||||
if (q->symbols_ref) {
|
|
||||||
free(q->symbols_ref);
|
|
||||||
}
|
|
||||||
if (q->refs) {
|
|
||||||
free(q->refs);
|
|
||||||
}
|
|
||||||
if (q->ch_est) {
|
|
||||||
free(q->ch_est);
|
|
||||||
}
|
|
||||||
bzero(q, sizeof(refsignal_t));
|
|
||||||
}
|
|
@ -0,0 +1,251 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <complex.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/common/phy_common.h"
|
||||||
|
#include "liblte/phy/ch_estimation/refsignal_dl.h"
|
||||||
|
#include "liblte/phy/utils/vector.h"
|
||||||
|
#include "liblte/phy/utils/debug.h"
|
||||||
|
#include "liblte/phy/common/sequence.h"
|
||||||
|
|
||||||
|
uint32_t refsignal_cs_v(uint32_t port_id, uint32_t ref_symbol_idx)
|
||||||
|
{
|
||||||
|
uint32_t v = 0;
|
||||||
|
switch (port_id) {
|
||||||
|
case 0:
|
||||||
|
if (!(ref_symbol_idx % 2)) {
|
||||||
|
v = 0;
|
||||||
|
} else {
|
||||||
|
v = 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!(ref_symbol_idx % 2)) {
|
||||||
|
v = 3;
|
||||||
|
} else {
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (ref_symbol_idx < 2) {
|
||||||
|
v = 0;
|
||||||
|
} else {
|
||||||
|
v = 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (ref_symbol_idx < 2) {
|
||||||
|
v = 3;
|
||||||
|
} else {
|
||||||
|
v = 6;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t refsignal_cs_nof_symbols(uint32_t port_id)
|
||||||
|
{
|
||||||
|
if (port_id < 2) {
|
||||||
|
return 4;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t refsignal_fidx(lte_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m) {
|
||||||
|
return 6*m + ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t refsignal_nsymbol(uint32_t l, lte_cp_t cp, uint32_t port_id) {
|
||||||
|
if (port_id < 2) {
|
||||||
|
if (l % 2) {
|
||||||
|
return (l/2+1)*CP_NSYMB(cp) - 3;
|
||||||
|
} else {
|
||||||
|
return (l/2)*CP_NSYMB(cp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 1+l*CP_NSYMB(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Allocates and precomputes the Cell-Specific Reference (CSR) signal for
|
||||||
|
* the 20 slots in a subframe
|
||||||
|
*/
|
||||||
|
int refsignal_cs_generate(refsignal_cs_t * q, lte_cell_t cell)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t c_init;
|
||||||
|
uint32_t i, ns, l, p;
|
||||||
|
uint32_t N_cp, mp;
|
||||||
|
sequence_t seq;
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
|
if (q != NULL &&
|
||||||
|
lte_cell_isvalid(&cell))
|
||||||
|
{
|
||||||
|
ret = LIBLTE_ERROR;
|
||||||
|
|
||||||
|
bzero(q, sizeof(refsignal_cs_t));
|
||||||
|
bzero(&seq, sizeof(sequence_t));
|
||||||
|
if (sequence_init(&seq, 2 * 2 * MAX_PRB)) {
|
||||||
|
goto free_and_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CP_ISNORM(cell.cp)) {
|
||||||
|
N_cp = 1;
|
||||||
|
} else {
|
||||||
|
N_cp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->cell = cell;
|
||||||
|
|
||||||
|
for (p=0;p<2;p++) {
|
||||||
|
for (i=0;i<NSUBFRAMES_X_FRAME;i++) {
|
||||||
|
q->pilots[p][i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_NUM_SF(q->cell.nof_prb, 2*p));
|
||||||
|
if (!q->pilots[p][i]) {
|
||||||
|
perror("malloc");
|
||||||
|
goto free_and_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ns=0;ns<NSLOTS_X_FRAME;ns++) {
|
||||||
|
for (p=0;p<2;p++) {
|
||||||
|
uint32_t nsymbols = refsignal_cs_nof_symbols(2*p)/2;
|
||||||
|
for (l = 0; l < nsymbols; l++) {
|
||||||
|
/* Compute sequence init value */
|
||||||
|
uint32_t lp = refsignal_nsymbol(l, cell.cp, 2*p);
|
||||||
|
c_init = 1024 * (7 * (ns + 1) + lp + 1) * (2 * cell.id + 1)
|
||||||
|
+ 2 * cell.id + N_cp;
|
||||||
|
|
||||||
|
/* generate sequence for this symbol and slot */
|
||||||
|
sequence_set_LTE_pr(&seq, c_init);
|
||||||
|
|
||||||
|
/* Compute signal */
|
||||||
|
for (i = 0; i < 2*q->cell.nof_prb; i++) {
|
||||||
|
mp = i + MAX_PRB - cell.nof_prb;
|
||||||
|
/* save signal */
|
||||||
|
q->pilots[p][ns/2][REFSIGNAL_PILOT_IDX(i,(ns%2)*nsymbols+l,q->cell)] =
|
||||||
|
(1 - 2 * (float) seq.c[2 * mp]) / sqrt(2) +
|
||||||
|
_Complex_I * (1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sequence_free(&seq);
|
||||||
|
ret = LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
free_and_exit:
|
||||||
|
if (ret == LIBLTE_ERROR) {
|
||||||
|
sequence_free(&seq);
|
||||||
|
refsignal_cs_free(q);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deallocates a refsignal_cs_t object allocated with refsignal_cs_init */
|
||||||
|
void refsignal_cs_free(refsignal_cs_t * q)
|
||||||
|
{
|
||||||
|
int i, p;
|
||||||
|
|
||||||
|
for (p=0;p<2;p++) {
|
||||||
|
for (i=0;i<NSUBFRAMES_X_FRAME;i++) {
|
||||||
|
if (q->pilots[p][i]) {
|
||||||
|
free(q->pilots[p][i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bzero(q, sizeof(refsignal_cs_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Maps a reference signal initialized with refsignal_cs_init() into an array of subframe symbols */
|
||||||
|
int refsignal_cs_put_sf(lte_cell_t cell, uint32_t port_id, cf_t *pilots, cf_t *sf_symbols)
|
||||||
|
{
|
||||||
|
uint32_t i, l;
|
||||||
|
uint32_t fidx;
|
||||||
|
|
||||||
|
if (lte_cell_isvalid(&cell) &&
|
||||||
|
lte_portid_isvalid(port_id) &&
|
||||||
|
pilots != NULL &&
|
||||||
|
sf_symbols != NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
|
||||||
|
uint32_t nsymbol = refsignal_nsymbol(l, cell.cp, port_id);
|
||||||
|
/* Compute offset frequency index */
|
||||||
|
fidx = ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
|
||||||
|
for (i = 0; i < 2*cell.nof_prb; i++) {
|
||||||
|
sf_symbols[RE_IDX(cell.nof_prb, nsymbol, fidx)] = pilots[REFSIGNAL_PILOT_IDX(i,l,cell)];
|
||||||
|
fidx += RE_X_RB/2; // 1 reference every 6 RE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Copies the RE containing references from an array of subframe symbols into the csr_signal[][].
|
||||||
|
* csr_signal[0] is the signal for the first OFDM symbol containing references and csr_signal[1] is the
|
||||||
|
* second OFDM symbol containing references (symbol 4 or 3 if port_id < 2)
|
||||||
|
*/
|
||||||
|
int refsignal_cs_get_sf(lte_cell_t cell, uint32_t port_id, cf_t *sf_symbols, cf_t *pilots)
|
||||||
|
{
|
||||||
|
uint32_t i, l;
|
||||||
|
uint32_t fidx;
|
||||||
|
|
||||||
|
if (lte_cell_isvalid(&cell) &&
|
||||||
|
lte_portid_isvalid(port_id) &&
|
||||||
|
pilots != NULL &&
|
||||||
|
sf_symbols != NULL)
|
||||||
|
{
|
||||||
|
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
|
||||||
|
uint32_t nsymbol = refsignal_nsymbol(l, cell.cp, port_id);
|
||||||
|
/* Compute offset frequency index */
|
||||||
|
fidx = ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
|
||||||
|
for (i = 0; i < 2*cell.nof_prb; i++) {
|
||||||
|
pilots[REFSIGNAL_PILOT_IDX(i,l,cell)] = sf_symbols[RE_IDX(cell.nof_prb, nsymbol, fidx)];
|
||||||
|
fidx += RE_X_RB/2; // 2 references per PRB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,183 @@
|
|||||||
|
|
||||||
|
#ifdef nocompile
|
||||||
|
// n_drms_2 table 5.5.2.1.1-1 from 36.211
|
||||||
|
uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 };
|
||||||
|
|
||||||
|
// n_drms_1 table 5.5.2.1.1-2 from 36.211
|
||||||
|
uint32_t n_drms_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 };
|
||||||
|
|
||||||
|
|
||||||
|
/* Generation of the reference signal sequence according to Section 5.5.1 of 36.211 */
|
||||||
|
int rs_sequence(ref_t * refs, uint32_t len, float alpha, uint32_t ns, uint32_t cell_id,
|
||||||
|
refsignal_ul_cfg_t * cfg)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
// Calculate u and v
|
||||||
|
uint32_t u, v;
|
||||||
|
uint32_t f_ss = (((cell_id % 30) + cfg->delta_ss) % 30);
|
||||||
|
printf("f_ss: %d\n", f_ss);
|
||||||
|
if (cfg->group_hopping_en) {
|
||||||
|
sequence_t seq;
|
||||||
|
bzero(&seq, sizeof(sequence_t));
|
||||||
|
sequence_LTE_pr(&seq, 160, cell_id / 30);
|
||||||
|
uint32_t f_gh = 0;
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
f_gh += (((uint32_t) seq.c[8 * ns + i]) << i);
|
||||||
|
}
|
||||||
|
printf("f_gh: %u\n", f_gh);
|
||||||
|
sequence_free(&seq);
|
||||||
|
u = ((f_gh%30) + f_ss) % 30;
|
||||||
|
} else {
|
||||||
|
u = f_ss % 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < 6 * RE_X_RB) {
|
||||||
|
v = 0;
|
||||||
|
} else {
|
||||||
|
if (!cfg->group_hopping_en && cfg->sequence_hopping_en) {
|
||||||
|
sequence_t seq;
|
||||||
|
bzero(&seq, sizeof(sequence_t));
|
||||||
|
sequence_LTE_pr(&seq, 20, ((cell_id / 30) << 5) + f_ss);
|
||||||
|
v = seq.c[ns];
|
||||||
|
sequence_free(&seq);
|
||||||
|
} else {
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("u: %d, v: %d\n", u, v);
|
||||||
|
if (len >= 3 * RE_X_RB) {
|
||||||
|
uint32_t n_sz;
|
||||||
|
uint32_t q;
|
||||||
|
float q_hat;
|
||||||
|
/* get largest prime n_zc<len */
|
||||||
|
for (i = NOF_PRIME_NUMBERS - 1; i > 0; i--) {
|
||||||
|
if (prime_numbers[i] < len) {
|
||||||
|
n_sz = prime_numbers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("n_sz: %d\n", n_sz);
|
||||||
|
q_hat = (float) n_sz *(u + 1) / 31;
|
||||||
|
if ((((uint32_t) (2 * q_hat)) % 2) == 0) {
|
||||||
|
q = (uint32_t) (q_hat + 0.5) + v;
|
||||||
|
} else {
|
||||||
|
q = (uint32_t) (q_hat + 0.5) - v;
|
||||||
|
}
|
||||||
|
cf_t *x_q = malloc(sizeof(cf_t) * n_sz);
|
||||||
|
if (!x_q) {
|
||||||
|
perror("malloc");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
for (i = 0; i < n_sz; i++) {
|
||||||
|
x_q[i] =
|
||||||
|
cexpf(-I * M_PI * (float) q * (float) i * ((float) i + 1) / n_sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
refs[i].simbol = cfg->beta * cexpf(I * alpha * i) * x_q[i % n_sz];
|
||||||
|
}
|
||||||
|
free(x_q);
|
||||||
|
} else {
|
||||||
|
if (len == RE_X_RB) {
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
refs[i].simbol = cfg->beta * cexpf(I * (phi_M_sc_12[u][i] * M_PI / 4 + alpha * i));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
refs[i].simbol = cfg->beta * cexpf(I * (phi_M_sc_24[u][i] * M_PI / 4 + alpha * i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initializes refsignal_t object according to 3GPP 36.211 5.5.2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int refsignal_init_LTEUL_drms_pusch(refsignal_t * q, uint32_t nof_prb, uint32_t prb_start,
|
||||||
|
uint32_t nslot, lte_cell_t cell, refsignal_ul_cfg_t * cfg)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t i;
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
uint32_t n_prs;
|
||||||
|
uint32_t M_sc;
|
||||||
|
float alpha;
|
||||||
|
|
||||||
|
if (q != NULL && nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
|
||||||
|
|
||||||
|
bzero(q, sizeof(refsignal_t));
|
||||||
|
|
||||||
|
M_sc = nof_prb * RE_X_RB;
|
||||||
|
|
||||||
|
q->nof_refs = M_sc;
|
||||||
|
q->nsymbols = 1;
|
||||||
|
q->voffset = cell.id % 6;
|
||||||
|
q->nof_prb = cell.nof_prb;
|
||||||
|
|
||||||
|
q->symbols_ref = malloc(sizeof(uint32_t) * 1);
|
||||||
|
if (!q->symbols_ref) {
|
||||||
|
perror("malloc");
|
||||||
|
goto free_and_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CP_ISNORM(cell.cp)) {
|
||||||
|
q->symbols_ref[0] = 3;
|
||||||
|
} else {
|
||||||
|
q->symbols_ref[0] = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
|
||||||
|
if (!q->refs) {
|
||||||
|
goto free_and_exit;
|
||||||
|
}
|
||||||
|
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
|
||||||
|
if (!q->ch_est) {
|
||||||
|
goto free_and_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate n_prs */
|
||||||
|
uint32_t c_init;
|
||||||
|
sequence_t seq;
|
||||||
|
bzero(&seq, sizeof(sequence_t));
|
||||||
|
c_init = ((cell.id / 30) << 5) + (((cell.id % 30) + cfg->delta_ss) % 30);
|
||||||
|
ret = sequence_LTE_pr(&seq, 8 * CP_NSYMB(cell.cp) * 20, c_init);
|
||||||
|
if (ret != LIBLTE_SUCCESS) {
|
||||||
|
goto free_and_exit;
|
||||||
|
}
|
||||||
|
n_prs = 0;
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
n_prs += (seq.c[8 * CP_NSYMB(cell.cp) * nslot + i] << i);
|
||||||
|
}
|
||||||
|
sequence_free(&seq);
|
||||||
|
|
||||||
|
// Calculate cyclic shift alpha
|
||||||
|
uint32_t n_cs =
|
||||||
|
(n_drms_1[cfg->cyclic_shift] +
|
||||||
|
n_drms_2[cfg->cyclic_shift_for_drms] + n_prs) % 12;
|
||||||
|
alpha = 2 * M_PI * (n_cs) / 12;
|
||||||
|
|
||||||
|
printf("alpha: %g\n", alpha);
|
||||||
|
|
||||||
|
if (rs_sequence(q->refs, M_sc, alpha, nslot, cell.id, cfg)) {
|
||||||
|
fprintf(stderr, "Error generating RS sequence\n");
|
||||||
|
goto free_and_exit;
|
||||||
|
}
|
||||||
|
/* mapping to resource elements */
|
||||||
|
for (i=0;i<M_sc;i++) {
|
||||||
|
q->refs[i].freq_idx = prb_start*RE_X_RB + i;
|
||||||
|
q->refs[i].time_idx = q->symbols_ref[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
free_and_exit:
|
||||||
|
if (ret == LIBLTE_ERROR) {
|
||||||
|
refsignal_free(q);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,252 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
#ifdef UNDEF_BOOL
|
||||||
|
#undef bool
|
||||||
|
#endif
|
||||||
|
#include "mex.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** MEX function to be called from MATLAB to test the channel estimator
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CELLID prhs[0]
|
||||||
|
#define PORTS prhs[1]
|
||||||
|
#define INPUT prhs[2]
|
||||||
|
#define FREQ_FILTER prhs[3]
|
||||||
|
#define TIME_FILTER prhs[4]
|
||||||
|
#define NOF_INPUTS 5
|
||||||
|
#define SFIDX prhs[5]
|
||||||
|
|
||||||
|
void help()
|
||||||
|
{
|
||||||
|
mexErrMsgTxt
|
||||||
|
("[estChannel, avg_refs, output] = liblte_chest(cell_id, nof_ports, inputSignal,[sf_idx|freq_filter],"
|
||||||
|
"[time_filter])\n\n"
|
||||||
|
" Returns a matrix of size equal to the inputSignal matrix with the channel estimates\n "
|
||||||
|
"for each resource element in inputSignal. The inputSignal matrix is the received Grid\n"
|
||||||
|
"of size nof_resource_elements x nof_ofdm_symbols.\n"
|
||||||
|
"The sf_idx is the subframe index only used if inputSignal is 1 subframe length.\n"
|
||||||
|
"Returns the averaged references and output signal after ZF/MMSE equalization\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the gateway function */
|
||||||
|
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
lte_cell_t cell;
|
||||||
|
chest_dl_t chest;
|
||||||
|
precoding_t cheq;
|
||||||
|
cf_t *input_signal = NULL, *output_signal[MAX_LAYERS];
|
||||||
|
cf_t *output_signal2 = NULL;
|
||||||
|
cf_t *ce[MAX_PORTS];
|
||||||
|
double *outr0=NULL, *outi0=NULL;
|
||||||
|
double *outr1=NULL, *outi1=NULL;
|
||||||
|
double *outr2=NULL, *outi2=NULL;
|
||||||
|
|
||||||
|
if (nrhs < NOF_INPUTS) {
|
||||||
|
help();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mxIsDouble(CELLID) && mxGetN(CELLID) != 1 &&
|
||||||
|
!mxIsDouble(PORTS) && mxGetN(PORTS) != 1 &&
|
||||||
|
mxGetM(CELLID) != 1) {
|
||||||
|
help();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.id = (uint32_t) *((double*) mxGetPr(CELLID));
|
||||||
|
cell.nof_prb = mxGetM(INPUT)/RE_X_RB;
|
||||||
|
cell.nof_ports = (uint32_t) *((double*) mxGetPr(PORTS));
|
||||||
|
if ((mxGetN(INPUT)%14) == 0) {
|
||||||
|
cell.cp = CPNORM;
|
||||||
|
} else if ((mxGetN(INPUT)%12)!=0) {
|
||||||
|
cell.cp = CPEXT;
|
||||||
|
} else {
|
||||||
|
mexErrMsgTxt("Invalid number of symbols\n");
|
||||||
|
help();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chest_dl_init(&chest, cell)) {
|
||||||
|
mexErrMsgTxt("Error initiating channel estimator\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nsubframes;
|
||||||
|
if (cell.cp == CPNORM) {
|
||||||
|
nsubframes = mxGetN(INPUT)/14;
|
||||||
|
} else {
|
||||||
|
nsubframes = mxGetN(INPUT)/12;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t sf_idx=0;
|
||||||
|
if (nsubframes == 1) {
|
||||||
|
if (nrhs != NOF_INPUTS+1) {
|
||||||
|
mexErrMsgTxt("Received 1 subframe. Need to provide subframe index.\n");
|
||||||
|
help();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sf_idx = (uint32_t) *((double*) mxGetPr(SFIDX));
|
||||||
|
} else {
|
||||||
|
if (nrhs != NOF_INPUTS) {
|
||||||
|
help();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t filter_len = 0;
|
||||||
|
float *filter;
|
||||||
|
double *f;
|
||||||
|
|
||||||
|
filter_len = mxGetNumberOfElements(FREQ_FILTER);
|
||||||
|
filter = malloc(sizeof(float) * filter_len);
|
||||||
|
f = (double*) mxGetPr(FREQ_FILTER);
|
||||||
|
for (i=0;i<filter_len;i++) {
|
||||||
|
filter[i] = (float) f[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
chest_dl_set_filter_freq(&chest, filter, filter_len);
|
||||||
|
|
||||||
|
filter_len = mxGetNumberOfElements(TIME_FILTER);
|
||||||
|
filter = malloc(sizeof(float) * filter_len);
|
||||||
|
f = (double*) mxGetPr(TIME_FILTER);
|
||||||
|
for (i=0;i<filter_len;i++) {
|
||||||
|
filter[i] = (float) f[i];
|
||||||
|
}
|
||||||
|
chest_dl_set_filter_time(&chest, filter, filter_len);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
double *inr=(double *)mxGetPr(INPUT);
|
||||||
|
double *ini=(double *)mxGetPi(INPUT);
|
||||||
|
|
||||||
|
/** Allocate input buffers */
|
||||||
|
int nof_re = 2*CP_NSYMB(cell.cp)*cell.nof_prb*RE_X_RB;
|
||||||
|
for (i=0;i<MAX_PORTS;i++) {
|
||||||
|
ce[i] = vec_malloc(nof_re * sizeof(cf_t));
|
||||||
|
}
|
||||||
|
input_signal = vec_malloc(nof_re * sizeof(cf_t));
|
||||||
|
for (i=0;i<MAX_PORTS;i++) {
|
||||||
|
output_signal[i] = vec_malloc(nof_re * sizeof(cf_t));
|
||||||
|
}
|
||||||
|
output_signal2 = vec_malloc(nof_re * sizeof(cf_t));
|
||||||
|
|
||||||
|
precoding_init(&cheq, nof_re);
|
||||||
|
|
||||||
|
/* Create output values */
|
||||||
|
if (nlhs >= 1) {
|
||||||
|
plhs[0] = mxCreateDoubleMatrix(nof_re * nsubframes, cell.nof_ports, mxCOMPLEX);
|
||||||
|
outr0 = mxGetPr(plhs[0]);
|
||||||
|
outi0 = mxGetPi(plhs[0]);
|
||||||
|
}
|
||||||
|
if (nlhs >= 2) {
|
||||||
|
plhs[1] = mxCreateDoubleMatrix(REFSIGNAL_MAX_NUM_SF(cell.nof_prb)*nsubframes, cell.nof_ports, mxCOMPLEX);
|
||||||
|
outr1 = mxGetPr(plhs[1]);
|
||||||
|
outi1 = mxGetPi(plhs[1]);
|
||||||
|
}
|
||||||
|
if (nlhs >= 3) {
|
||||||
|
plhs[2] = mxCreateDoubleMatrix(nof_re * nsubframes, 1, mxCOMPLEX);
|
||||||
|
outr2 = mxGetPr(plhs[2]);
|
||||||
|
outi2 = mxGetPi(plhs[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int sf=0;sf<nsubframes;sf++) {
|
||||||
|
/* Convert input to C complex type */
|
||||||
|
for (i=0;i<nof_re;i++) {
|
||||||
|
__real__ input_signal[i] = (float) *inr;
|
||||||
|
if (ini) {
|
||||||
|
__imag__ input_signal[i] = (float) *ini;
|
||||||
|
}
|
||||||
|
inr++;
|
||||||
|
ini++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nsubframes != 1) {
|
||||||
|
sf_idx = sf%10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop through the 10 subframes */
|
||||||
|
if (chest_dl_estimate(&chest, input_signal, ce, sf_idx)) {
|
||||||
|
mexErrMsgTxt("Error running channel estimator\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cell.nof_ports == 1) {
|
||||||
|
predecoding_single(&cheq, input_signal, ce[0], output_signal2, nof_re, chest_dl_get_noise_estimate(&chest));
|
||||||
|
} else {
|
||||||
|
predecoding_diversity(&cheq, input_signal, ce, output_signal, cell.nof_ports, nof_re, chest_dl_get_noise_estimate(&chest));
|
||||||
|
layerdemap_diversity(output_signal, output_signal2, cell.nof_ports, nof_re/cell.nof_ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nlhs >= 1) {
|
||||||
|
for (int j=0;j<cell.nof_ports;j++) {
|
||||||
|
for (i=0;i<nof_re;i++) {
|
||||||
|
*outr0 = (double) crealf(ce[j][i]);
|
||||||
|
if (outi0) {
|
||||||
|
*outi0 = (double) cimagf(ce[j][i]);
|
||||||
|
}
|
||||||
|
outr0++;
|
||||||
|
outi0++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nlhs >= 2) {
|
||||||
|
for (int j=0;j<cell.nof_ports;j++) {
|
||||||
|
for (i=0;i<REFSIGNAL_NUM_SF(cell.nof_prb,j);i++) {
|
||||||
|
*outr1 = (double) crealf(chest.pilot_estimates_average[j][i]);
|
||||||
|
if (outi1) {
|
||||||
|
*outi1 = (double) cimagf(chest.pilot_estimates_average[j][i]);
|
||||||
|
}
|
||||||
|
outr1++;
|
||||||
|
outi1++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nlhs >= 3) {
|
||||||
|
for (i=0;i<nof_re;i++) {
|
||||||
|
*outr2 = (double) crealf(output_signal2[i]);
|
||||||
|
if (outi2) {
|
||||||
|
*outi2 = (double) cimagf(output_signal2[i]);
|
||||||
|
}
|
||||||
|
outr2++;
|
||||||
|
outi2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nlhs >= 4) {
|
||||||
|
plhs[3] = mxCreateDoubleScalar(chest_dl_get_snr(&chest));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue