osmo-trx-0.4.0/0000755000175000017500000000000013324433746012324 5ustar rubenrubenosmo-trx-0.4.0/config/0000755000175000017500000000000013272615542013567 5ustar rubenrubenosmo-trx-0.4.0/config/ax_check_compile_flag.m40000644000175000017500000000625113272615542020303 0ustar rubenruben# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS osmo-trx-0.4.0/config/ax_cxx_compile_stdcxx.m40000644000175000017500000004742413272615542020443 0ustar rubenruben# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # or '14' (for the C++14 standard). # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016 Krzesimir Nowak # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 7 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AX_REQUIRE_DEFINED([AC_MSG_WARN]) AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, ax_cv_cxx_compile_cxx$1, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [ax_cv_cxx_compile_cxx$1=yes], [ax_cv_cxx_compile_cxx$1=no])]) if test x$ax_cv_cxx_compile_cxx$1 = xyes; then ac_success=yes fi m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus <= 201402L #error "This is not a C++17 compiler" #else #if defined(__clang__) #define REALLY_CLANG #else #if defined(__GNUC__) #define REALLY_GCC #endif #endif #include #include #include namespace cxx17 { #if !defined(REALLY_CLANG) namespace test_constexpr_lambdas { // TODO: test it with clang++ from git constexpr int foo = [](){return 42;}(); } #endif // !defined(REALLY_CLANG) namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } #if !defined(REALLY_CLANG) namespace test_template_argument_deduction_for_class_templates { // TODO: test it with clang++ from git template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } #endif // !defined(REALLY_CLANG) namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } #if !defined(REALLY_CLANG) namespace test_structured_bindings { // TODO: test it with clang++ from git int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } #endif // !defined(REALLY_CLANG) #if !defined(REALLY_CLANG) namespace test_exception_spec_type_system { // TODO: test it with clang++ from git struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } #endif // !defined(REALLY_CLANG) namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus <= 201402L ]]) osmo-trx-0.4.0/config/ax_cxx_compile_stdcxx_11.m40000644000175000017500000000321513272615542020732 0ustar rubenruben# ============================================================================= # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html # ============================================================================= # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the C++11 # standard; if necessary, add switches to CXX and CXXCPP to enable # support. # # This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX # macro with the version set to C++11. The two optional arguments are # forwarded literally as the second and third argument respectively. # Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for # more information. If you want to use this macro, you also need to # download the ax_cxx_compile_stdcxx.m4 file. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 18 AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])]) osmo-trx-0.4.0/config/ax_sse.m40000644000175000017500000000427013272615542015316 0ustar rubenruben# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_ext.html # =========================================================================== # # SYNOPSIS # # AX_SSE # # DESCRIPTION # # Find SIMD extensions supported by compiler. The -m"simdextensionname" is # added to SIMD_FLAGS if compiler supports it. For example, if "sse2" is # available, then "-msse2" is added to SIMD_FLAGS. # # This macro calls: # # AC_SUBST(SIMD_FLAGS) # # And defines: # # HAVE_SSE3 / HAVE_SSE4.1 # # LICENSE # # Copyright (c) 2007 Christophe Tournayre # Copyright (c) 2013 Michael Petch # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. # # NOTE: The functionality that requests the cpuid has been stripped because # this project detects the CPU capabilities during runtime. However, we # still need to check if the compiler supports the requested SIMD flag #serial 12 AC_DEFUN([AX_SSE], [ AC_REQUIRE([AC_CANONICAL_HOST]) AM_CONDITIONAL(HAVE_SSE3, false) AM_CONDITIONAL(HAVE_SSE4_1, false) case $host_cpu in i[[3456]]86*|x86_64*|amd64*) AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, []) if test x"$ax_cv_support_sse3_ext" = x"yes"; then SIMD_FLAGS="$SIMD_FLAGS -msse3" AC_DEFINE(HAVE_SSE3,, [Support SSE3 (Streaming SIMD Extensions 3) instructions]) AM_CONDITIONAL(HAVE_SSE3, true) else AC_MSG_WARN([Your compiler does not support SSE3 instructions]) fi AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, []) if test x"$ax_cv_support_sse41_ext" = x"yes"; then SIMD_FLAGS="$SIMD_FLAGS -msse4.1" AC_DEFINE(HAVE_SSE4_1,, [Support SSE4.1 (Streaming SIMD Extensions 4.1) instructions]) AM_CONDITIONAL(HAVE_SSE4_1, true) else AC_MSG_WARN([Your compiler does not support SSE4.1]) fi ;; esac AC_SUBST(SIMD_FLAGS) ]) osmo-trx-0.4.0/Makefile.common0000644000175000017500000000234513272615542015255 0ustar rubenruben# # Copyright 2008 Free Software Foundation, Inc. # # This software is distributed under the terms of the GNU Public License. # See the COPING file in the main directory for details. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs GSM_INCLUDEDIR = $(top_srcdir)/GSM STD_DEFINES_AND_INCLUDES = \ $(SVNDEV) \ -I$(COMMON_INCLUDEDIR) \ -I$(GSM_INCLUDEDIR) COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la GSM_LA = $(top_builddir)/GSM/libGSM.la if ARCH_ARM ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la else ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la endif MOSTLYCLEANFILES = *~ osmo-trx-0.4.0/git-version-gen0000755000175000017500000001245513272615542015274 0ustar rubenruben#!/bin/sh # Print a version string. scriptversion=2010-01-28.01 # Copyright (C) 2007-2010 Free Software Foundation, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. # It may be run two ways: # - from a git repository in which the "git describe" command below # produces useful output (thus requiring at least one signed tag) # - from a non-git-repo directory containing a .tarball-version file, which # presumes this script is invoked like "./git-version-gen .tarball-version". # In order to use intra-version strings in your project, you will need two # separate generated version string files: # # .tarball-version - present only in a distribution tarball, and not in # a checked-out repository. Created with contents that were learned at # the last time autoconf was run, and used by git-version-gen. Must not # be present in either $(srcdir) or $(builddir) for git-version-gen to # give accurate answers during normal development with a checked out tree, # but must be present in a tarball when there is no version control system. # Therefore, it cannot be used in any dependencies. GNUmakefile has # hooks to force a reconfigure at distribution time to get the value # correct, without penalizing normal development with extra reconfigures. # # .version - present in a checked-out repository and in a distribution # tarball. Usable in dependencies, particularly for files that don't # want to depend on config.h but do want to track version changes. # Delete this file prior to any autoconf run where you want to rebuild # files to pick up a version string change; and leave it stale to # minimize rebuild time after unrelated changes to configure sources. # # It is probably wise to add these two files to .gitignore, so that you # don't accidentally commit either generated file. # # Use the following line in your configure.ac, so that $(VERSION) will # automatically be up-to-date each time configure is run (and note that # since configure.ac no longer includes a version string, Makefile rules # should not depend on configure.ac for version updates). # # AC_INIT([GNU project], # m4_esyscmd([build-aux/git-version-gen .tarball-version]), # [bug-project@example]) # # Then use the following lines in your Makefile.am, so that .version # will be present for dependencies, and so that .tarball-version will # exist in distribution tarballs. # # BUILT_SOURCES = $(top_srcdir)/.version # $(top_srcdir)/.version: # echo $(VERSION) > $@-t && mv $@-t $@ # dist-hook: # echo $(VERSION) > $(distdir)/.tarball-version case $# in 1) ;; *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; esac tarball_version_file=$1 nl=' ' # First see if there is a tarball-only version file. # then try "git describe", then default. if test -f $tarball_version_file then v=`cat $tarball_version_file` || exit 1 case $v in *$nl*) v= ;; # reject multi-line output [0-9]*) ;; *) v= ;; esac test -z "$v" \ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 fi if test -n "$v" then : # use $v elif v=`git tag -l --sort=v:refname | grep "^[0-9]*.[0-9]*.[0-9]*$" | tail -n 1 2>/dev/null` \ && case $v in [0-9]*) ;; v[0-9]*) ;; *) (exit 1) ;; esac then # Is this a new git that lists number of commits since the last # tag or the previous older version that did not? # Newer: v6.10-77-g0f8faeb # Older: v6.10-g0f8faeb case $v in *-*-*) : git describe is okay three part flavor ;; *-*) : git describe is older two part flavor # Recreate the number of commits and rewrite such that the # result is the same as if we were using the newer version # of git describe. vtag=`echo "$v" | sed 's/-.*//'` numcommits=`git rev-list "$vtag"..HEAD | wc -l` v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; ;; esac # Change the first '-' to a '.', so version-comparing tools work properly. # Remove the "g" in git describe's output string, to save a byte. v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; else v=UNKNOWN fi v=`echo "$v" |sed 's/^v//'` # Don't declare a version "dirty" merely because a time stamp has changed. git status > /dev/null 2>&1 dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= case "$dirty" in '') ;; *) # Append the suffix only if there isn't one already. case $v in *-dirty) ;; *) v="$v-dirty" ;; esac ;; esac # Omit the trailing newline, so that m4_esyscmd can use the result directly. echo "$v" | tr -d '\012' # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: osmo-trx-0.4.0/ChangeLog0000644000175000017500000000000013272615542014062 0ustar rubenrubenosmo-trx-0.4.0/.gitreview0000644000175000017500000000006213272615542014326 0ustar rubenruben[gerrit] host=gerrit.osmocom.org project=osmo-trx osmo-trx-0.4.0/utils/0000755000175000017500000000000013272615542013462 5ustar rubenrubenosmo-trx-0.4.0/utils/clockdump.sh0000755000175000017500000000006013272615542015776 0ustar rubenruben#!/bin/sh sudo tcpdump -i lo0 -A udp port 5700 osmo-trx-0.4.0/GSM/0000755000175000017500000000000013272615542012750 5ustar rubenrubenosmo-trx-0.4.0/GSM/Makefile.am0000644000175000017500000000200613272615542015002 0ustar rubenruben# # Copyright 2008 Free Software Foundation, Inc. # # This software is distributed under the terms of the GNU Public License. # See the COPYING file in the main directory for details. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # include $(top_srcdir)/Makefile.common AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) #AM_CXXFLAGS = -O2 -g noinst_LTLIBRARIES = libGSM.la libGSM_la_SOURCES = \ GSMCommon.cpp noinst_HEADERS = \ GSMCommon.h osmo-trx-0.4.0/GSM/GSMCommon.h0000644000175000017500000001230213272615542014716 0ustar rubenruben/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */ /* * Copyright 2008-2011 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef GSMCOMMON_H #define GSMCOMMON_H #include #include #include #include #include #include #include namespace GSM { /**@namespace GSM This namespace covers L1 FEC, L2 and L3 message translation. */ /** GSM Training sequences from GSM 05.02 5.2.3. */ extern const BitVector gTrainingSequence[]; extern const BitVector gEdgeTrainingSequence[]; /** C0T0 filler burst, GSM 05.02, 5.2.6 */ extern const BitVector gDummyBurst; /** Random access burst synch. sequence */ extern const BitVector gRACHSynchSequence; /** Random access burst synch. sequence, GSM 05.02 5.2.7 */ extern const BitVector gRACHBurst; /**@name Modulus operations for frame numbers. */ //@{ /** The GSM hyperframe is largest time period in the GSM system, GSM 05.02 4.3.3. */ const uint32_t gHyperframe = 2048UL * 26UL * 51UL; /** Get a clock difference, within the modulus, v1-v2. */ int32_t FNDelta(int32_t v1, int32_t v2); /** Compare two frame clock values. @return 1 if v1>v2, -1 if v17) { mTN-=8; mFN = (mFN+1) % gHyperframe; } return *this; } Time& operator+=(int step) { // Remember the step might be negative. mFN += step; if (mFN<0) mFN+=gHyperframe; mFN = mFN % gHyperframe; return *this; } Time operator-(int step) const { return operator+(-step); } Time operator+(int step) const { Time newVal = *this; newVal += step; return newVal; } Time operator+(const Time& other) const { unsigned newTN = (mTN + other.mTN) % 8; uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe; return Time(newFN,newTN); } int operator-(const Time& other) const { return FNDelta(mFN,other.mFN); } //@} /**@name Comparisons. */ //@{ bool operator<(const Time& other) const { if (mFN==other.mFN) return (mTN(const Time& other) const { if (mFN==other.mFN) return (mTN>other.mTN); return FNCompare(mFN,other.mFN)>0; } bool operator<=(const Time& other) const { if (mFN==other.mFN) return (mTN<=other.mTN); return FNCompare(mFN,other.mFN)<=0; } bool operator>=(const Time& other) const { if (mFN==other.mFN) return (mTN>=other.mTN); return FNCompare(mFN,other.mFN)>=0; } bool operator==(const Time& other) const { return (mFN == other.mFN) && (mTN==other.mTN); } //@} /**@name Standard derivations. */ //@{ /** GSM 05.02 3.3.2.2.1 */ unsigned SFN() const { return mFN / (26*51); } /** GSM 05.02 3.3.2.2.1 */ unsigned T1() const { return SFN() % 2048; } /** GSM 05.02 3.3.2.2.1 */ unsigned T2() const { return mFN % 26; } /** GSM 05.02 3.3.2.2.1 */ unsigned T3() const { return mFN % 51; } /** GSM 05.02 3.3.2.2.1. */ unsigned T3p() const { return (T3()-1)/10; } /** GSM 05.02 6.3.1.3. */ unsigned TC() const { return (FN()/51) % 8; } /** GSM 04.08 10.5.2.30. */ unsigned T1p() const { return SFN() % 32; } /** GSM 05.02 6.2.3 */ unsigned T1R() const { return T1() % 64; } //@} }; std::ostream& operator<<(std::ostream& os, const Time& ts); }; // namespace GSM #endif // vim: ts=4 sw=4 osmo-trx-0.4.0/GSM/GSMCommon.cpp0000644000175000017500000000643013272615542015256 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * Copyright 2011 Range Networks, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "GSMCommon.h" using namespace GSM; using namespace std; const BitVector GSM::gTrainingSequence[] = { BitVector("00100101110000100010010111"), BitVector("00101101110111100010110111"), BitVector("01000011101110100100001110"), BitVector("01000111101101000100011110"), BitVector("00011010111001000001101011"), BitVector("01001110101100000100111010"), BitVector("10100111110110001010011111"), BitVector("11101111000100101110111100"), }; const BitVector GSM::gEdgeTrainingSequence[] = { BitVector("111111001111111001111001001001111111111111001111111111001111111001111001001001"), BitVector("111111001111001001111001001001111001001001001111111111001111001001111001001001"), BitVector("111001111111111111001001001111001001001111001111111001111111111111001001001111"), BitVector("111001111111111001001001001111001001111001111111111001111111111001001001001111"), BitVector("111111111001001111001111001001001111111001111111111111111001001111001111001001"), BitVector("111001111111001001001111001111001001111111111111111001111111001001001111001111"), BitVector("001111001111111001001001001001111001001111111111001111001111111001001001001001"), BitVector("001001001111001001001001111111111001111111001111001001001111001001001001111111"), }; const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000"); const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000"); // |-head-||---------midamble----------------------||--------------data----------------||t| const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000"); int32_t GSM::FNDelta(int32_t v1, int32_t v2) { static const int32_t halfModulus = gHyperframe/2; int32_t delta = v1-v2; if (delta>=halfModulus) delta -= gHyperframe; else if (delta<-halfModulus) delta += gHyperframe; return (int32_t) delta; } int GSM::FNCompare(int32_t v1, int32_t v2) { int32_t delta = FNDelta(v1,v2); if (delta>0) return 1; if (delta<0) return -1; return 0; } ostream& GSM::operator<<(ostream& os, const Time& t) { os << t.TN() << ":" << t.FN(); return os; } // vim: ts=4 sw=4 osmo-trx-0.4.0/configure.ac0000644000175000017500000001407513272615542014617 0ustar rubenrubendnl dnl Copyright 2008, 2009, 2010 Free Software Foundation, Inc. dnl dnl This software is distributed under the terms of the GNU Public License. dnl See the COPYING file in the main directory for details. dnl dnl This program is free software: you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation, either version 3 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program. If not, see . dnl AC_INIT([osmo-trx], m4_esyscmd([./git-version-gen .tarball-version]), [openbsc@lists.osmocom.org]) AC_PREREQ(2.57) AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_MACRO_DIR([config]) AM_CONFIG_HEADER(config.h) AC_CONFIG_TESTDIR(tests) AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([subdir-objects]) dnl Linux kernel KBuild style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl include release helper RELMAKE='-include osmo-release.mk' AC_SUBST([RELMAKE]) AM_PROG_AS AC_PROG_CC AC_PROG_CXX AX_CXX_COMPILE_STDCXX_11 AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_INSTALL AC_PATH_PROG([RM_PROG], [rm]) dnl check for pkg-config (explained in detail in libosmocore/configure.ac) AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) if test "x$PKG_CONFIG_INSTALLED" = "xno"; then AC_MSG_WARN([You need to install pkg-config]) fi PKG_PROG_PKG_CONFIG([0.20]) AC_LIBTOOL_WIN32_DLL AC_ENABLE_SHARED dnl do build shared libraries AC_DISABLE_STATIC dnl don't build static libraries AC_PROG_LIBTOOL dnl Checks for header files. AC_HEADER_STDC dnl This is required for GnuRadio includes to understand endianess correctly: AC_CHECK_HEADERS([byteswap.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T AC_HEADER_TIME AC_C_BIGENDIAN PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0) AC_ARG_ENABLE(sanitize, [AS_HELP_STRING( [--enable-sanitize], [Compile with address sanitizer enabled], )], [sanitize=$enableval], [sanitize="no"]) if test x"$sanitize" = x"yes" then CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined" fi AC_ARG_WITH(uhd, [ AS_HELP_STRING([--with-uhd], [enable UHD based transceiver]) ]) AC_ARG_WITH(usrp1, [ AS_HELP_STRING([--with-usrp1], [enable USRP1 gnuradio based transceiver]) ]) AC_ARG_WITH(singledb, [ AS_HELP_STRING([--with-singledb], [enable single daughterboard use on USRP1]) ]) AC_ARG_WITH(neon, [ AS_HELP_STRING([--with-neon], [enable ARM NEON support]) ]) AC_ARG_WITH(neon-vfpv4, [ AS_HELP_STRING([--with-neon-vfpv4], [enable ARM NEON FMA support]) ]) AC_ARG_WITH(sse, [ AS_HELP_STRING([--with-sse], [enable x86 SSE support (default)]) ]) AS_IF([test "x$with_neon" = "xyes"], [ AC_DEFINE(HAVE_NEON, 1, Support ARM NEON) ]) AS_IF([test "x$with_neon_vfpv4" = "xyes"], [ AC_DEFINE(HAVE_NEON, 1, Support ARM NEON) AC_DEFINE(HAVE_NEON_FMA, 1, Support ARM NEON with FMA) ]) AS_IF([test "x$with_usrp1" = "xyes"], [ PKG_CHECK_MODULES(USRP, usrp >= 3.3) ]) AS_IF([test "x$with_uhd" != "xno"],[ PKG_CHECK_MODULES(UHD, uhd >= 003.011, [AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)], [PKG_CHECK_MODULES(UHD, uhd >= 003.009, [AC_DEFINE(USE_UHD_3_9, 1, UHD version 3.9.0 or higher)], [PKG_CHECK_MODULES(UHD, uhd >= 003.005)] )] ) ]) AS_IF([test "x$with_singledb" = "xyes"], [ AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard) ]) # Find and define supported SIMD extensions AS_IF([test "x$with_sse" != "xno"], [ AX_SSE ], [ AM_CONDITIONAL(HAVE_SSE3, false) AM_CONDITIONAL(HAVE_SSE4_1, false) ]) dnl Check if the compiler supports specified GCC's built-in function AC_DEFUN([CHECK_BUILTIN_SUPPORT], [ AC_CACHE_CHECK( [whether ${CC} has $1 built-in], [osmo_cv_cc_has_builtin], [ AC_LINK_IFELSE([ AC_LANG_PROGRAM([], [ __builtin_cpu_supports("sse"); ]) ], [AS_VAR_SET([osmo_cv_cc_has_builtin], [yes])], [AS_VAR_SET([osmo_cv_cc_has_builtin], [no])]) ] ) AS_IF([test yes = AS_VAR_GET([osmo_cv_cc_has_builtin])], [ AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1, [Define to 1 if compiler has the '$1' built-in function]) ], [ AC_MSG_WARN($2) ]) ]) dnl Check if the compiler supports runtime SIMD detection CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports], [Runtime SIMD detection will be disabled]) AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" != "xno"]) AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"]) AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"]) AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"]) PKG_CHECK_MODULES(LIBUSB, libusb-1.0) PKG_CHECK_MODULES(FFTWF, fftw3f) AC_CHECK_HEADER([boost/config.hpp],[], [AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])]) AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) AC_MSG_RESULT([CFLAGS="$CFLAGS"]) AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"]) AC_MSG_RESULT([LDFLAGS="$LDFLAGS"]) dnl Output files AC_CONFIG_FILES([\ Makefile \ CommonLibs/Makefile \ GSM/Makefile \ Transceiver52M/Makefile \ Transceiver52M/arch/Makefile \ Transceiver52M/arch/common/Makefile \ Transceiver52M/arch/arm/Makefile \ Transceiver52M/arch/x86/Makefile \ Transceiver52M/device/Makefile \ Transceiver52M/device/uhd/Makefile \ Transceiver52M/device/usrp1/Makefile \ tests/Makefile \ tests/CommonLibs/Makefile \ tests/Transceiver52M/Makefile \ ]) AC_OUTPUT osmo-trx-0.4.0/Transceiver52M/0000755000175000017500000000000013324433743015072 5ustar rubenrubenosmo-trx-0.4.0/Transceiver52M/sigProcLib.cpp0000644000175000017500000013214113272615542017636 0ustar rubenruben/* * Copyright 2008, 2011 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sigProcLib.h" #include "GSMCommon.h" #include "Logger.h" #include "Resampler.h" extern "C" { #include "convolve.h" #include "scale.h" #include "mult.h" } using namespace GSM; #define TABLESIZE 1024 #define DELAYFILTS 64 /* Clipping detection threshold */ #define CLIP_THRESH 30000.0f /** Lookup tables for trigonometric approximation */ static float sincTable[TABLESIZE+1]; // add 1 element for wrap around /** Constants */ static const float M_PI_F = (float)M_PI; /* Precomputed rotation vectors */ static signalVector *GMSKRotation4 = NULL; static signalVector *GMSKReverseRotation4 = NULL; static signalVector *GMSKRotation1 = NULL; static signalVector *GMSKReverseRotation1 = NULL; /* Precomputed fractional delay filters */ static signalVector *delayFilters[DELAYFILTS]; static const Complex psk8_table[8] = { Complex(-0.70710678, 0.70710678), Complex( 0.0, -1.0), Complex( 0.0, 1.0), Complex( 0.70710678, -0.70710678), Complex(-1.0, 0.0), Complex(-0.70710678, -0.70710678), Complex( 0.70710678, 0.70710678), Complex( 1.0, 0.0), }; /* Downsampling filterbank - 4 SPS to 1 SPS */ #define DOWNSAMPLE_IN_LEN 624 #define DOWNSAMPLE_OUT_LEN 156 static Resampler *dnsampler = NULL; /* * RACH and midamble correlation waveforms. Store the buffer separately * because we need to allocate it explicitly outside of the signal vector * constructor. This is because C++ (prior to C++11) is unable to natively * perform 16-byte memory alignment required by many SSE instructions. */ struct CorrelationSequence { CorrelationSequence() : sequence(NULL), buffer(NULL) { } ~CorrelationSequence() { delete sequence; free(buffer); } signalVector *sequence; void *buffer; float toa; complex gain; }; /* * Gaussian and empty modulation pulses. Like the correlation sequences, * store the runtime (Gaussian) buffer separately because of needed alignment * for SSE instructions. */ struct PulseSequence { PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL), c0_buffer(NULL), c1_buffer(NULL), c0_inv_buffer(NULL) { } ~PulseSequence() { delete c0; delete c1; delete c0_inv; delete empty; free(c0_buffer); free(c1_buffer); } signalVector *c0; signalVector *c1; signalVector *c0_inv; signalVector *empty; void *c0_buffer; void *c1_buffer; void *c0_inv_buffer; }; static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; static CorrelationSequence *gRACHSequence = NULL; static PulseSequence *GSMPulse1 = NULL; static PulseSequence *GSMPulse4 = NULL; void sigProcLibDestroy() { for (int i = 0; i < 8; i++) { delete gMidambles[i]; delete gEdgeMidambles[i]; gMidambles[i] = NULL; gEdgeMidambles[i] = NULL; } for (int i = 0; i < DELAYFILTS; i++) { delete delayFilters[i]; delayFilters[i] = NULL; } delete GMSKRotation1; delete GMSKReverseRotation1; delete GMSKRotation4; delete GMSKReverseRotation4; delete gRACHSequence; delete GSMPulse1; delete GSMPulse4; delete dnsampler; GMSKRotation1 = NULL; GMSKRotation4 = NULL; GMSKReverseRotation4 = NULL; GMSKReverseRotation1 = NULL; gRACHSequence = NULL; GSMPulse1 = NULL; GSMPulse4 = NULL; } static float vectorNorm2(const signalVector &x) { signalVector::const_iterator xPtr = x.begin(); float Energy = 0.0; for (;xPtr != x.end();xPtr++) { Energy += xPtr->norm2(); } return Energy; } /* * Initialize 4 sps and 1 sps rotation tables */ static void initGMSKRotationTables() { size_t len1 = 157, len4 = 625; GMSKRotation4 = new signalVector(len4); GMSKReverseRotation4 = new signalVector(len4); signalVector::iterator rotPtr = GMSKRotation4->begin(); signalVector::iterator revPtr = GMSKReverseRotation4->begin(); auto phase = 0.0; while (rotPtr != GMSKRotation4->end()) { *rotPtr++ = complex(cos(phase), sin(phase)); *revPtr++ = complex(cos(-phase), sin(-phase)); phase += M_PI / 2.0 / 4.0; } GMSKRotation1 = new signalVector(len1); GMSKReverseRotation1 = new signalVector(len1); rotPtr = GMSKRotation1->begin(); revPtr = GMSKReverseRotation1->begin(); phase = 0.0; while (rotPtr != GMSKRotation1->end()) { *rotPtr++ = complex(cos(phase), sin(phase)); *revPtr++ = complex(cos(-phase), sin(-phase)); phase += M_PI / 2.0; } } static void GMSKRotate(signalVector &x, int sps) { #if HAVE_NEON size_t len; signalVector *a, *b, *out; a = &x; out = &x; len = out->size(); if (len == 157) len--; if (sps == 1) b = GMSKRotation1; else b = GMSKRotation4; mul_complex((float *) out->begin(), (float *) a->begin(), (float *) b->begin(), len); #else signalVector::iterator rotPtr, xPtr = x.begin(); if (sps == 1) rotPtr = GMSKRotation1->begin(); else rotPtr = GMSKRotation4->begin(); if (x.isReal()) { while (xPtr < x.end()) { *xPtr = *rotPtr++ * (xPtr->real()); xPtr++; } } else { while (xPtr < x.end()) { *xPtr = *rotPtr++ * (*xPtr); xPtr++; } } #endif } static bool GMSKReverseRotate(signalVector &x, int sps) { signalVector::iterator rotPtr, xPtr= x.begin(); if (sps == 1) rotPtr = GMSKReverseRotation1->begin(); else if (sps == 4) rotPtr = GMSKReverseRotation4->begin(); else return false; if (x.isReal()) { while (xPtr < x.end()) { *xPtr = *rotPtr++ * (xPtr->real()); xPtr++; } } else { while (xPtr < x.end()) { *xPtr = *rotPtr++ * (*xPtr); xPtr++; } } return true; } /** Convolution type indicator */ enum ConvType { START_ONLY, NO_DELAY, CUSTOM, UNDEFINED, }; static signalVector *convolve(const signalVector *x, const signalVector *h, signalVector *y, ConvType spanType, size_t start = 0, size_t len = 0, size_t step = 1, int offset = 0) { int rc; size_t head = 0, tail = 0; bool alloc = false, append = false; const signalVector *_x = NULL; if (!x || !h) return NULL; switch (spanType) { case START_ONLY: start = 0; head = h->size() - 1; len = x->size(); if (x->getStart() < head) append = true; break; case NO_DELAY: start = h->size() / 2; head = start; tail = start; len = x->size(); append = true; break; case CUSTOM: if (start < h->size() - 1) { head = h->size() - start; append = true; } if (start + len > x->size()) { tail = start + len - x->size(); append = true; } break; default: return NULL; } /* * Error if the output vector is too small. Create the output vector * if the pointer is NULL. */ if (y && (len > y->size())) return NULL; if (!y) { y = new signalVector(len); alloc = true; } /* Prepend or post-pend the input vector if the parameters require it */ if (append) _x = new signalVector(*x, head, tail); else _x = x; /* * Four convovle types: * 1. Complex-Real (aligned) * 2. Complex-Complex (aligned) * 3. Complex-Real (!aligned) * 4. Complex-Complex (!aligned) */ if (h->isReal() && h->isAligned()) { rc = convolve_real((float *) _x->begin(), _x->size(), (float *) h->begin(), h->size(), (float *) y->begin(), y->size(), start, len, step, offset); } else if (!h->isReal() && h->isAligned()) { rc = convolve_complex((float *) _x->begin(), _x->size(), (float *) h->begin(), h->size(), (float *) y->begin(), y->size(), start, len, step, offset); } else if (h->isReal() && !h->isAligned()) { rc = base_convolve_real((float *) _x->begin(), _x->size(), (float *) h->begin(), h->size(), (float *) y->begin(), y->size(), start, len, step, offset); } else if (!h->isReal() && !h->isAligned()) { rc = base_convolve_complex((float *) _x->begin(), _x->size(), (float *) h->begin(), h->size(), (float *) y->begin(), y->size(), start, len, step, offset); } else { rc = -1; } if (append) delete _x; if (rc < 0) { if (alloc) delete y; return NULL; } return y; } /* * Generate static EDGE linear equalizer. This equalizer is not adaptive. * Filter taps are generated from the inverted 1 SPS impulse response of * the EDGE pulse shape captured after the downsampling filter. */ static bool generateInvertC0Pulse(PulseSequence *pulse) { if (!pulse) return false; pulse->c0_inv_buffer = convolve_h_alloc(5); pulse->c0_inv = new signalVector((complex *) pulse->c0_inv_buffer, 0, 5); pulse->c0_inv->isReal(true); pulse->c0_inv->setAligned(false); signalVector::iterator xP = pulse->c0_inv->begin(); *xP++ = 0.15884; *xP++ = -0.43176; *xP++ = 1.00000; *xP++ = -0.42608; *xP++ = 0.14882; return true; } static bool generateC1Pulse(int sps, PulseSequence *pulse) { int len; if (!pulse) return false; switch (sps) { case 4: len = 8; break; default: return false; } pulse->c1_buffer = convolve_h_alloc(len); pulse->c1 = new signalVector((complex *) pulse->c1_buffer, 0, len); pulse->c1->isReal(true); /* Enable alignment for SSE usage */ pulse->c1->setAligned(true); signalVector::iterator xP = pulse->c1->begin(); switch (sps) { case 4: /* BT = 0.30 */ *xP++ = 0.0; *xP++ = 8.16373112e-03; *xP++ = 2.84385729e-02; *xP++ = 5.64158904e-02; *xP++ = 7.05463553e-02; *xP++ = 5.64158904e-02; *xP++ = 2.84385729e-02; *xP++ = 8.16373112e-03; } return true; } static PulseSequence *generateGSMPulse(int sps) { int len; float arg, avg, center; PulseSequence *pulse; if ((sps != 1) && (sps != 4)) return NULL; /* Store a single tap filter used for correlation sequence generation */ pulse = new PulseSequence(); pulse->empty = new signalVector(1); pulse->empty->isReal(true); *(pulse->empty->begin()) = 1.0f; /* * For 4 samples-per-symbol use a precomputed single pulse Laurent * approximation. This should yields below 2 degrees of phase error at * the modulator output. Use the existing pulse approximation for all * other oversampling factors. */ switch (sps) { case 4: len = 16; break; case 1: default: len = 4; } pulse->c0_buffer = convolve_h_alloc(len); pulse->c0 = new signalVector((complex *) pulse->c0_buffer, 0, len); pulse->c0->isReal(true); /* Enable alingnment for SSE usage */ pulse->c0->setAligned(true); signalVector::iterator xP = pulse->c0->begin(); if (sps == 4) { *xP++ = 0.0; *xP++ = 4.46348606e-03; *xP++ = 2.84385729e-02; *xP++ = 1.03184855e-01; *xP++ = 2.56065552e-01; *xP++ = 4.76375085e-01; *xP++ = 7.05961177e-01; *xP++ = 8.71291644e-01; *xP++ = 9.29453645e-01; *xP++ = 8.71291644e-01; *xP++ = 7.05961177e-01; *xP++ = 4.76375085e-01; *xP++ = 2.56065552e-01; *xP++ = 1.03184855e-01; *xP++ = 2.84385729e-02; *xP++ = 4.46348606e-03; generateC1Pulse(sps, pulse); } else { center = (float) (len - 1.0) / 2.0; /* GSM pulse approximation */ for (int i = 0; i < len; i++) { arg = ((float) i - center) / (float) sps; *xP++ = 0.96 * exp(-1.1380 * arg * arg - 0.527 * arg * arg * arg * arg); } avg = sqrtf(vectorNorm2(*pulse->c0) / sps); xP = pulse->c0->begin(); for (int i = 0; i < len; i++) *xP++ /= avg; } /* * Current form of the EDGE equalization filter non-realizable at 4 SPS. * Load the onto both 1 SPS and 4 SPS objects for convenience. Note that * the EDGE demodulator downsamples to 1 SPS prior to equalization. */ generateInvertC0Pulse(pulse); return pulse; } bool vectorSlicer(SoftVector *x) { SoftVector::iterator xP = x->begin(); SoftVector::iterator xPEnd = x->end(); while (xP < xPEnd) { *xP = 0.5 * (*xP + 1.0f); if (*xP > 1.0) *xP = 1.0; if (*xP < 0.0) *xP = 0.0; xP++; } return true; } static signalVector *rotateBurst(const BitVector &wBurst, int guardPeriodLength, int sps) { int burst_len; signalVector *pulse, rotated; signalVector::iterator itr; pulse = GSMPulse1->empty; burst_len = sps * (wBurst.size() + guardPeriodLength); rotated = signalVector(burst_len); itr = rotated.begin(); for (unsigned i = 0; i < wBurst.size(); i++) { *itr = 2.0 * (wBurst[i] & 0x01) - 1.0; itr += sps; } GMSKRotate(rotated, sps); rotated.isReal(false); /* Dummy filter operation */ return convolve(&rotated, pulse, NULL, START_ONLY); } static void rotateBurst2(signalVector &burst, double phase) { Complex rot = Complex(cos(phase), sin(phase)); for (size_t i = 0; i < burst.size(); i++) burst[i] = burst[i] * rot; } /* * Ignore the guard length argument in the GMSK modulator interface * because it results in 624/628 sized bursts instead of the preferred * burst length of 625. Only 4 SPS is supported. */ static signalVector *modulateBurstLaurent(const BitVector &bits) { int burst_len, sps = 4; float phase; signalVector *c0_pulse, *c1_pulse, *c0_shaped, *c1_shaped; signalVector::iterator c0_itr, c1_itr; c0_pulse = GSMPulse4->c0; c1_pulse = GSMPulse4->c1; if (bits.size() > 156) return NULL; burst_len = 625; signalVector c0_burst(burst_len, c0_pulse->size()); c0_burst.isReal(true); c0_itr = c0_burst.begin(); signalVector c1_burst(burst_len, c1_pulse->size()); c1_itr = c1_burst.begin(); /* Padded differential tail bits */ *c0_itr = 2.0 * (0x00 & 0x01) - 1.0; c0_itr += sps; /* Main burst bits */ for (unsigned i = 0; i < bits.size(); i++) { *c0_itr = 2.0 * (bits[i] & 0x01) - 1.0; c0_itr += sps; } /* Padded differential tail bits */ *c0_itr = 2.0 * (0x00 & 0x01) - 1.0; /* Generate C0 phase coefficients */ GMSKRotate(c0_burst, sps); c0_burst.isReal(false); c0_itr = c0_burst.begin(); c0_itr += sps * 2; c1_itr += sps * 2; /* Start magic */ phase = 2.0 * ((0x01 & 0x01) ^ (0x01 & 0x01)) - 1.0; *c1_itr = *c0_itr * Complex(0, phase); c0_itr += sps; c1_itr += sps; /* Generate C1 phase coefficients */ for (unsigned i = 2; i < bits.size(); i++) { phase = 2.0 * ((bits[i - 1] & 0x01) ^ (bits[i - 2] & 0x01)) - 1.0; *c1_itr = *c0_itr * Complex(0, phase); c0_itr += sps; c1_itr += sps; } /* End magic */ int i = bits.size(); phase = 2.0 * ((bits[i-1] & 0x01) ^ (bits[i-2] & 0x01)) - 1.0; *c1_itr = *c0_itr * Complex(0, phase); /* Primary (C0) and secondary (C1) pulse shaping */ c0_shaped = convolve(&c0_burst, c0_pulse, NULL, START_ONLY); c1_shaped = convolve(&c1_burst, c1_pulse, NULL, START_ONLY); /* Sum shaped outputs into C0 */ c0_itr = c0_shaped->begin(); c1_itr = c1_shaped->begin(); for (unsigned i = 0; i < c0_shaped->size(); i++ ) *c0_itr++ += *c1_itr++; delete c1_shaped; return c0_shaped; } static signalVector *rotateEdgeBurst(const signalVector &symbols, int sps) { signalVector *burst; signalVector::iterator burst_itr; burst = new signalVector(symbols.size() * sps); burst_itr = burst->begin(); for (size_t i = 0; i < symbols.size(); i++) { float phase = i * 3.0f * M_PI / 8.0f; Complex rot = Complex(cos(phase), sin(phase)); *burst_itr = symbols[i] * rot; burst_itr += sps; } return burst; } static signalVector *derotateEdgeBurst(const signalVector &symbols, int sps) { signalVector *burst; signalVector::iterator burst_itr; if (symbols.size() % sps) return NULL; burst = new signalVector(symbols.size() / sps); burst_itr = burst->begin(); for (size_t i = 0; i < burst->size(); i++) { float phase = (float) (i % 16) * 3.0f * M_PI / 8.0f; Complex rot = Complex(cosf(phase), -sinf(phase)); *burst_itr = symbols[sps * i] * rot; burst_itr++; } return burst; } static signalVector *mapEdgeSymbols(const BitVector &bits) { if (bits.size() % 3) return NULL; signalVector *symbols = new signalVector(bits.size() / 3); for (size_t i = 0; i < symbols->size(); i++) { unsigned index = (((unsigned) bits[3 * i + 0] & 0x01) << 0) | (((unsigned) bits[3 * i + 1] & 0x01) << 1) | (((unsigned) bits[3 * i + 2] & 0x01) << 2); (*symbols)[i] = psk8_table[index]; } return symbols; } /* * EDGE 8-PSK rotate and pulse shape * * Delay the EDGE downlink bursts by one symbol in order to match GMSK pulse * shaping group delay. The difference in group delay arises from the dual * pulse filter combination of the GMSK Laurent represenation whereas 8-PSK * uses a single pulse linear filter. */ static signalVector *shapeEdgeBurst(const signalVector &symbols) { size_t nsyms, nsamps = 625, sps = 4; signalVector::iterator burst_itr; nsyms = symbols.size(); if (nsyms * sps > nsamps) nsyms = 156; signalVector burst(nsamps, GSMPulse4->c0->size()); /* Delay burst by 1 symbol */ burst_itr = burst.begin() + sps; for (size_t i = 0; i < nsyms; i++) { float phase = i * 3.0f * M_PI / 8.0f; Complex rot = Complex(cos(phase), sin(phase)); *burst_itr = symbols[i] * rot; burst_itr += sps; } /* Single Gaussian pulse approximation shaping */ return convolve(&burst, GSMPulse4->c0, NULL, START_ONLY); } /* * Generate a random GSM normal burst. */ signalVector *genRandNormalBurst(int tsc, int sps, int tn) { if ((tsc < 0) || (tsc > 7) || (tn < 0) || (tn > 7)) return NULL; if ((sps != 1) && (sps != 4)) return NULL; int i = 0; BitVector bits(148); /* Tail bits */ for (; i < 3; i++) bits[i] = 0; /* Random bits */ for (; i < 60; i++) bits[i] = rand() % 2; /* Stealing bit */ bits[i++] = 0; /* Training sequence */ for (int n = 0; i < 87; i++, n++) bits[i] = gTrainingSequence[tsc][n]; /* Stealing bit */ bits[i++] = 0; /* Random bits */ for (; i < 145; i++) bits[i] = rand() % 2; /* Tail bits */ for (; i < 148; i++) bits[i] = 0; int guard = 8 + !(tn % 4); return modulateBurst(bits, guard, sps); } /* * Generate a random GSM access burst. */ signalVector *genRandAccessBurst(int delay, int sps, int tn) { if ((tn < 0) || (tn > 7)) return NULL; if ((sps != 1) && (sps != 4)) return NULL; if (delay > 68) return NULL; int i = 0; BitVector bits(88 + delay); /* delay */ for (; i < delay; i++) bits[i] = 0; /* head and synch bits */ for (int n = 0; i < 49+delay; i++, n++) bits[i] = gRACHBurst[n]; /* Random bits */ for (; i < 85+delay; i++) bits[i] = rand() % 2; /* Tail bits */ for (; i < 88+delay; i++) bits[i] = 0; int guard = 68-delay + !(tn % 4); return modulateBurst(bits, guard, sps); } signalVector *generateEmptyBurst(int sps, int tn) { if ((tn < 0) || (tn > 7)) return NULL; if (sps == 4) return new signalVector(625); else if (sps == 1) return new signalVector(148 + 8 + !(tn % 4)); else return NULL; } signalVector *generateDummyBurst(int sps, int tn) { if (((sps != 1) && (sps != 4)) || (tn < 0) || (tn > 7)) return NULL; return modulateBurst(gDummyBurst, 8 + !(tn % 4), sps); } /* * Generate a random 8-PSK EDGE burst. Only 4 SPS is supported with * the returned burst being 625 samples in length. */ signalVector *generateEdgeBurst(int tsc) { int tail = 9 / 3; int data = 174 / 3; int train = 78 / 3; if ((tsc < 0) || (tsc > 7)) return NULL; signalVector burst(148); const BitVector *midamble = &gEdgeTrainingSequence[tsc]; /* Tail */ int n, i = 0; for (; i < tail; i++) burst[i] = psk8_table[7]; /* Body */ for (; i < tail + data; i++) burst[i] = psk8_table[rand() % 8]; /* TSC */ for (n = 0; i < tail + data + train; i++, n++) { unsigned index = (((unsigned) (*midamble)[3 * n + 0] & 0x01) << 0) | (((unsigned) (*midamble)[3 * n + 1] & 0x01) << 1) | (((unsigned) (*midamble)[3 * n + 2] & 0x01) << 2); burst[i] = psk8_table[index]; } /* Body */ for (; i < tail + data + train + data; i++) burst[i] = psk8_table[rand() % 8]; /* Tail */ for (; i < tail + data + train + data + tail; i++) burst[i] = psk8_table[7]; return shapeEdgeBurst(burst); } /* * Modulate 8-PSK burst. When empty pulse shaping (rotation only) * is enabled, the output vector length will be bit sequence length * times the SPS value. When pulse shaping is enabled, the output * vector length is fixed at 625 samples (156.25 symbols at 4 SPS). * Pulse shaped bit sequences that go beyond one burst are truncated. * Pulse shaping at anything but 4 SPS is not supported. */ signalVector *modulateEdgeBurst(const BitVector &bits, int sps, bool empty) { signalVector *shape, *burst; if ((sps != 4) && !empty) return NULL; burst = mapEdgeSymbols(bits); if (!burst) return NULL; if (empty) shape = rotateEdgeBurst(*burst, sps); else shape = shapeEdgeBurst(*burst); delete burst; return shape; } static signalVector *modulateBurstBasic(const BitVector &bits, int guard_len, int sps) { int burst_len; signalVector *pulse; signalVector::iterator burst_itr; if (sps == 1) pulse = GSMPulse1->c0; else pulse = GSMPulse4->c0; burst_len = sps * (bits.size() + guard_len); signalVector burst(burst_len, pulse->size()); burst.isReal(true); burst_itr = burst.begin(); /* Raw bits are not differentially encoded */ for (unsigned i = 0; i < bits.size(); i++) { *burst_itr = 2.0 * (bits[i] & 0x01) - 1.0; burst_itr += sps; } GMSKRotate(burst, sps); burst.isReal(false); /* Single Gaussian pulse approximation shaping */ return convolve(&burst, pulse, NULL, START_ONLY); } /* Assume input bits are not differentially encoded */ signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength, int sps, bool emptyPulse) { if (emptyPulse) return rotateBurst(wBurst, guardPeriodLength, sps); else if (sps == 4) return modulateBurstLaurent(wBurst); else return modulateBurstBasic(wBurst, guardPeriodLength, sps); } static void generateSincTable() { for (int i = 0; i < TABLESIZE; i++) { auto x = (double) i / TABLESIZE * 8 * M_PI; auto y = sin(x) / x; sincTable[i] = std::isnan(y) ? 1.0 : y; } } static float sinc(float x) { if (fabs(x) >= 8 * M_PI) return 0.0; int index = (int) floorf(fabs(x) / (8 * M_PI) * TABLESIZE); return sincTable[index]; } /* * Create fractional delay filterbank with Blackman-harris windowed * sinc function generator. The number of filters generated is specified * by the DELAYFILTS value. */ static void generateDelayFilters() { int h_len = 20; complex *data; signalVector *h; signalVector::iterator itr; float k, sum; float a0 = 0.35875; float a1 = 0.48829; float a2 = 0.14128; float a3 = 0.01168; for (int i = 0; i < DELAYFILTS; i++) { data = (complex *) convolve_h_alloc(h_len); h = new signalVector(data, 0, h_len); h->setAligned(true); h->isReal(true); sum = 0.0; itr = h->end(); for (int n = 0; n < h_len; n++) { k = (float) n; *--itr = (complex) sinc(M_PI_F * (k - (float) h_len / 2.0 - (float) i / DELAYFILTS)); *itr *= a0 - a1 * cos(2 * M_PI * n / (h_len - 1)) + a2 * cos(4 * M_PI * n / (h_len - 1)) - a3 * cos(6 * M_PI * n / (h_len - 1)); sum += itr->real(); } itr = h->begin(); for (int n = 0; n < h_len; n++) *itr++ /= sum; delayFilters[i] = h; } } signalVector *delayVector(const signalVector *in, signalVector *out, float delay) { int whole, index; float frac; signalVector *h, *shift, *fshift = NULL; whole = floor(delay); frac = delay - whole; /* Sinc interpolated fractional shift (if allowable) */ if (fabs(frac) > 1e-2) { index = floorf(frac * (float) DELAYFILTS); h = delayFilters[index]; fshift = convolve(in, h, NULL, NO_DELAY); if (!fshift) return NULL; } if (!fshift) shift = new signalVector(*in); else shift = fshift; /* Integer sample shift */ if (whole < 0) { whole = -whole; signalVector::iterator wBurstItr = shift->begin(); signalVector::iterator shiftedItr = shift->begin() + whole; while (shiftedItr < shift->end()) *wBurstItr++ = *shiftedItr++; while (wBurstItr < shift->end()) *wBurstItr++ = 0.0; } else if (whole >= 0) { signalVector::iterator wBurstItr = shift->end() - 1; signalVector::iterator shiftedItr = shift->end() - 1 - whole; while (shiftedItr >= shift->begin()) *wBurstItr-- = *shiftedItr--; while (wBurstItr >= shift->begin()) *wBurstItr-- = 0.0; } if (!out) return shift; out->clone(*shift); delete shift; return out; } static complex interpolatePoint(const signalVector &inSig, float ix) { int start = (int) (floor(ix) - 10); if (start < 0) start = 0; int end = (int) (floor(ix) + 11); if ((unsigned) end > inSig.size()-1) end = inSig.size()-1; complex pVal = 0.0; if (!inSig.isReal()) { for (int i = start; i < end; i++) pVal += inSig[i] * sinc(M_PI_F*(i-ix)); } else { for (int i = start; i < end; i++) pVal += inSig[i].real() * sinc(M_PI_F*(i-ix)); } return pVal; } static complex fastPeakDetect(const signalVector &rxBurst, float *index) { float val, max = 0.0f; complex amp; int _index = -1; for (size_t i = 0; i < rxBurst.size(); i++) { val = rxBurst[i].norm2(); if (val > max) { max = val; _index = i; amp = rxBurst[i]; } } if (index) *index = (float) _index; return amp; } static complex peakDetect(const signalVector &rxBurst, float *peakIndex, float *avgPwr) { complex maxVal = 0.0; float maxIndex = -1; float sumPower = 0.0; for (unsigned int i = 0; i < rxBurst.size(); i++) { float samplePower = rxBurst[i].norm2(); if (samplePower > maxVal.real()) { maxVal = samplePower; maxIndex = i; } sumPower += samplePower; } // interpolate around the peak // to save computation, we'll use early-late balancing float earlyIndex = maxIndex-1; float lateIndex = maxIndex+1; float incr = 0.5; while (incr > 1.0/1024.0) { complex earlyP = interpolatePoint(rxBurst,earlyIndex); complex lateP = interpolatePoint(rxBurst,lateIndex); if (earlyP < lateP) earlyIndex += incr; else if (earlyP > lateP) earlyIndex -= incr; else break; incr /= 2.0; lateIndex = earlyIndex + 2.0; } maxIndex = earlyIndex + 1.0; maxVal = interpolatePoint(rxBurst,maxIndex); if (peakIndex!=NULL) *peakIndex = maxIndex; if (avgPwr!=NULL) *avgPwr = (sumPower-maxVal.norm2()) / (rxBurst.size()-1); return maxVal; } void scaleVector(signalVector &x, complex scale) { #ifdef HAVE_NEON int len = x.size(); scale_complex((float *) x.begin(), (float *) x.begin(), (float *) &scale, len); #else signalVector::iterator xP = x.begin(); signalVector::iterator xPEnd = x.end(); if (!x.isReal()) { while (xP < xPEnd) { *xP = *xP * scale; xP++; } } else { while (xP < xPEnd) { *xP = xP->real() * scale; xP++; } } #endif } /** in-place conjugation */ static void conjugateVector(signalVector &x) { if (x.isReal()) return; signalVector::iterator xP = x.begin(); signalVector::iterator xPEnd = x.end(); while (xP < xPEnd) { *xP = xP->conj(); xP++; } } static bool generateMidamble(int sps, int tsc) { bool status = true; float toa; complex *data = NULL; signalVector *autocorr = NULL, *midamble = NULL; signalVector *midMidamble = NULL, *_midMidamble = NULL; if ((tsc < 0) || (tsc > 7)) return false; delete gMidambles[tsc]; /* Use middle 16 bits of each TSC. Correlation sequence is not pulse shaped */ midMidamble = modulateBurst(gTrainingSequence[tsc].segment(5,16), 0, sps, true); if (!midMidamble) return false; /* Simulated receive sequence is pulse shaped */ midamble = modulateBurst(gTrainingSequence[tsc], 0, sps, false); if (!midamble) { status = false; goto release; } // NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst, // the ideal TSC has an + 180 degree phase shift, // due to the pi/2 frequency shift, that // needs to be accounted for. // 26-midamble is 61 symbols into burst, has +90 degree phase shift. scaleVector(*midMidamble, complex(-1.0, 0.0)); scaleVector(*midamble, complex(0.0, 1.0)); conjugateVector(*midMidamble); /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */ data = (complex *) convolve_h_alloc(midMidamble->size()); _midMidamble = new signalVector(data, 0, midMidamble->size()); _midMidamble->setAligned(true); memcpy(_midMidamble->begin(), midMidamble->begin(), midMidamble->size() * sizeof(complex)); autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY); if (!autocorr) { status = false; goto release; } gMidambles[tsc] = new CorrelationSequence; gMidambles[tsc]->buffer = data; gMidambles[tsc]->sequence = _midMidamble; gMidambles[tsc]->gain = peakDetect(*autocorr, &toa, NULL); /* For 1 sps only * (Half of correlation length - 1) + midpoint of pulse shape + remainder * 13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2 */ if (sps == 1) gMidambles[tsc]->toa = toa - 13.5; else gMidambles[tsc]->toa = 0; release: delete autocorr; delete midamble; delete midMidamble; if (!status) { delete _midMidamble; free(data); gMidambles[tsc] = NULL; } return status; } static CorrelationSequence *generateEdgeMidamble(int tsc) { complex *data = NULL; signalVector *midamble = NULL, *_midamble = NULL; CorrelationSequence *seq; if ((tsc < 0) || (tsc > 7)) return NULL; /* Use middle 48 bits of each TSC. Correlation sequence is not pulse shaped */ const BitVector *bits = &gEdgeTrainingSequence[tsc]; midamble = modulateEdgeBurst(bits->segment(15, 48), 1, true); if (!midamble) return NULL; conjugateVector(*midamble); data = (complex *) convolve_h_alloc(midamble->size()); _midamble = new signalVector(data, 0, midamble->size()); _midamble->setAligned(true); memcpy(_midamble->begin(), midamble->begin(), midamble->size() * sizeof(complex)); /* Channel gain is an empirically measured value */ seq = new CorrelationSequence; seq->buffer = data; seq->sequence = _midamble; seq->gain = Complex(-19.6432, 19.5006) / 1.18; seq->toa = 0; delete midamble; return seq; } static bool generateRACHSequence(int sps) { bool status = true; float toa; complex *data = NULL; signalVector *autocorr = NULL; signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL; delete gRACHSequence; seq0 = modulateBurst(gRACHSynchSequence, 0, sps, false); if (!seq0) return false; seq1 = modulateBurst(gRACHSynchSequence.segment(0, 40), 0, sps, true); if (!seq1) { status = false; goto release; } conjugateVector(*seq1); /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */ data = (complex *) convolve_h_alloc(seq1->size()); _seq1 = new signalVector(data, 0, seq1->size()); _seq1->setAligned(true); memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex)); autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY); if (!autocorr) { status = false; goto release; } gRACHSequence = new CorrelationSequence; gRACHSequence->sequence = _seq1; gRACHSequence->buffer = data; gRACHSequence->gain = peakDetect(*autocorr, &toa, NULL); /* For 1 sps only * (Half of correlation length - 1) + midpoint of pulse shaping filer * 20.5 = (40 / 2 - 1) + 1.5 */ if (sps == 1) gRACHSequence->toa = toa - 20.5; else gRACHSequence->toa = 0.0; release: delete autocorr; delete seq0; delete seq1; if (!status) { delete _seq1; free(data); gRACHSequence = NULL; } return status; } /* * Peak-to-average computation +/- range from peak in symbols */ #define COMPUTE_PEAK_MIN 2 #define COMPUTE_PEAK_MAX 5 /* * Minimum number of values needed to compute peak-to-average */ #define COMPUTE_PEAK_CNT 5 static float computePeakRatio(signalVector *corr, int sps, float toa, complex amp) { int num = 0; complex *peak; float rms, avg = 0.0; /* Check for bogus results */ if ((toa < 0.0) || (toa > corr->size())) return 0.0; peak = corr->begin() + (int) rint(toa); for (int i = COMPUTE_PEAK_MIN * sps; i <= COMPUTE_PEAK_MAX * sps; i++) { if (peak - i >= corr->begin()) { avg += (peak - i)->norm2(); num++; } if (peak + i < corr->end()) { avg += (peak + i)->norm2(); num++; } } if (num < COMPUTE_PEAK_CNT) return 0.0; rms = sqrtf(avg / (float) num) + 0.00001; return (amp.abs()) / rms; } float energyDetect(const signalVector &rxBurst, unsigned windowLength) { signalVector::const_iterator windowItr = rxBurst.begin(); //+rxBurst.size()/2 - 5*windowLength/2; float energy = 0.0; if (windowLength == 0) return 0.0; if (windowLength > rxBurst.size()) windowLength = rxBurst.size(); for (unsigned i = 0; i < windowLength; i++) { energy += windowItr->norm2(); windowItr+=4; } return energy/windowLength; } static signalVector *downsampleBurst(const signalVector &burst) { signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len()); signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN); memcpy(in.begin(), burst.begin(), DOWNSAMPLE_IN_LEN * 2 * sizeof(float)); if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN, (float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) { delete out; out = NULL; } return out; }; /* * Detect a burst based on correlation and peak-to-average ratio * * For one sampler-per-symbol, perform fast peak detection (no interpolation) * for initial gating. We do this because energy detection should be disabled. * For higher oversampling values, we assume the energy detector is in place * and we run full interpolating peak detection. */ static int detectBurst(const signalVector &burst, signalVector &corr, CorrelationSequence *sync, float thresh, int sps, complex *amp, float *toa, int start, int len) { const signalVector *corr_in; signalVector *dec = NULL; if (sps == 4) { dec = downsampleBurst(burst); corr_in = dec; sps = 1; } else { corr_in = &burst; } /* Correlate */ if (!convolve(corr_in, sync->sequence, &corr, CUSTOM, start, len, 1, 0)) { delete dec; return -1; } delete dec; /* Running at the downsampled rate at this point */ sps = 1; /* Peak detection - place restrictions at correlation edges */ *amp = fastPeakDetect(corr, toa); if ((*toa < 3 * sps) || (*toa > len - 3 * sps)) return 0; /* Peak -to-average ratio */ if (computePeakRatio(&corr, sps, *toa, *amp) < thresh) return 0; /* Compute peak-to-average ratio. Reject if we don't have enough values */ *amp = peakDetect(corr, toa, NULL); /* Normalize our channel gain */ *amp = *amp / sync->gain; /* Compensate for residuate time lag */ *toa = *toa - sync->toa; return 1; } static float maxAmplitude(const signalVector &burst) { float max = 0.0; for (size_t i = 0; i < burst.size(); i++) { if (fabs(burst[i].real()) > max) max = fabs(burst[i].real()); if (fabs(burst[i].imag()) > max) max = fabs(burst[i].imag()); } return max; } /* * RACH/Normal burst detection with clipping detection * * Correlation window parameters: * target: Tail bits + burst length * head: Search symbols before target * tail: Search symbols after target */ static int detectGeneralBurst(const signalVector &rxBurst, float thresh, int sps, complex &, float &toa, int target, int head, int tail, CorrelationSequence *sync) { int rc, start, len; bool clipping = false; if ((sps != 1) && (sps != 4)) return -SIGERR_UNSUPPORTED; // Detect potential clipping // We still may be able to demod the burst, so we'll give it a try // and only report clipping if we can't demod. float maxAmpl = maxAmplitude(rxBurst); if (maxAmpl > CLIP_THRESH) { LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl; clipping = true; } start = target - head - 1; len = head + tail; signalVector corr(len); rc = detectBurst(rxBurst, corr, sync, thresh, sps, &, &toa, start, len); if (rc < 0) { return -SIGERR_INTERNAL; } else if (!rc) { amp = 0.0f; toa = 0.0f; return clipping?-SIGERR_CLIP:SIGERR_NONE; } /* Subtract forward search bits from delay */ toa -= head; return 1; } /* * RACH burst detection * * Correlation window parameters: * target: Tail bits + RACH length (reduced from 41 to a multiple of 4) * head: Search 8 symbols before target * tail: Search 8 symbols + maximum expected delay */ static int detectRACHBurst(const signalVector &burst, float threshold, int sps, complex &litude, float &toa, unsigned max_toa) { int rc, target, head, tail; CorrelationSequence *sync; target = 8 + 40; head = 8; tail = 8 + max_toa; sync = gRACHSequence; rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa, target, head, tail, sync); return rc; } /* * Normal burst detection * * Correlation window parameters: * target: Tail + data + mid-midamble + 1/2 remaining midamblebits * head: Search 6 symbols before target * tail: Search 6 symbols + maximum expected delay */ static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold, int sps, complex &litude, float &toa, unsigned max_toa) { int rc, target, head, tail; CorrelationSequence *sync; if (tsc > 7) return -SIGERR_UNSUPPORTED; target = 3 + 58 + 16 + 5; head = 6; tail = 6 + max_toa; sync = gMidambles[tsc]; rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa, target, head, tail, sync); return rc; } static int detectEdgeBurst(const signalVector &burst, unsigned tsc, float threshold, int sps, complex &litude, float &toa, unsigned max_toa) { int rc, target, head, tail; CorrelationSequence *sync; if (tsc > 7) return -SIGERR_UNSUPPORTED; target = 3 + 58 + 16 + 5; head = 6; tail = 6 + max_toa; sync = gEdgeMidambles[tsc]; rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa, target, head, tail, sync); return rc; } int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold, int sps, CorrType type, complex &, float &toa, unsigned max_toa) { int rc = 0; switch (type) { case EDGE: rc = detectEdgeBurst(burst, tsc, threshold, sps, amp, toa, max_toa); if (rc > 0) break; else type = TSC; case TSC: rc = analyzeTrafficBurst(burst, tsc, threshold, sps, amp, toa, max_toa); break; case RACH: rc = detectRACHBurst(burst, threshold, sps, amp, toa, max_toa); break; default: LOG(ERR) << "Invalid correlation type"; } if (rc > 0) return type; return rc; } /* * Soft 8-PSK decoding using Manhattan distance metric */ static SoftVector *softSliceEdgeBurst(signalVector &burst) { size_t nsyms = 148; if (burst.size() < nsyms) return NULL; signalVector::iterator itr; SoftVector *bits = new SoftVector(nsyms * 3); /* * Bits 0 and 1 - First and second bits of the symbol respectively */ rotateBurst2(burst, -M_PI / 8.0); itr = burst.begin(); for (size_t i = 0; i < nsyms; i++) { (*bits)[3 * i + 0] = -itr->imag(); (*bits)[3 * i + 1] = itr->real(); itr++; } /* * Bit 2 - Collapse symbols into quadrant 0 (positive X and Y). * Decision area is then simplified to X=Y axis. Rotate again to * place decision boundary on X-axis. */ itr = burst.begin(); for (size_t i = 0; i < burst.size(); i++) { burst[i] = Complex(fabs(itr->real()), fabs(itr->imag())); itr++; } rotateBurst2(burst, -M_PI / 4.0); itr = burst.begin(); for (size_t i = 0; i < nsyms; i++) { (*bits)[3 * i + 2] = -itr->imag(); itr++; } signalVector soft(bits->size()); for (size_t i = 0; i < bits->size(); i++) soft[i] = (*bits)[i]; return bits; } /* * Convert signalVector to SoftVector by taking real part of the signal. */ static SoftVector *signalToSoftVector(signalVector *dec) { SoftVector *bits = new SoftVector(dec->size()); SoftVector::iterator bit_itr = bits->begin(); signalVector::iterator burst_itr = dec->begin(); for (; burst_itr < dec->end(); burst_itr++) *bit_itr++ = burst_itr->real(); return bits; } /* * Shared portion of GMSK and EDGE demodulators consisting of timing * recovery and single tap channel correction. For 4 SPS (if activated), * the output is downsampled prior to the 1 SPS modulation specific * stages. */ static signalVector *demodCommon(const signalVector &burst, int sps, complex chan, float toa) { signalVector *delay, *dec; if ((sps != 1) && (sps != 4)) return NULL; delay = delayVector(&burst, NULL, -toa * (float) sps); scaleVector(*delay, (complex) 1.0 / chan); if (sps == 1) return delay; dec = downsampleBurst(*delay); delete delay; return dec; } /* * Demodulate GSMK burst. Prior to symbol rotation, operate at * 4 SPS (if activated) to minimize distortion through the fractional * delay filters. Symbol rotation and after always operates at 1 SPS. */ static SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps, complex channel, float TOA) { SoftVector *bits; signalVector *dec; dec = demodCommon(rxBurst, sps, channel, TOA); if (!dec) return NULL; /* Shift up by a quarter of a frequency */ GMSKReverseRotate(*dec, 1); /* Take real part of the signal */ bits = signalToSoftVector(dec); delete dec; return bits; } /* * Demodulate an 8-PSK burst. Prior to symbol rotation, operate at * 4 SPS (if activated) to minimize distortion through the fractional * delay filters. Symbol rotation and after always operates at 1 SPS. * * Allow 1 SPS demodulation here, but note that other parts of the * transceiver restrict EDGE operatoin to 4 SPS - 8-PSK distortion * through the fractional delay filters at 1 SPS renders signal * nearly unrecoverable. */ static SoftVector *demodEdgeBurst(const signalVector &burst, int sps, complex chan, float toa) { SoftVector *bits; signalVector *dec, *rot, *eq; dec = demodCommon(burst, sps, chan, toa); if (!dec) return NULL; /* Equalize and derotate */ eq = convolve(dec, GSMPulse4->c0_inv, NULL, NO_DELAY); rot = derotateEdgeBurst(*eq, 1); /* Soft slice and normalize */ bits = softSliceEdgeBurst(*rot); delete dec; delete eq; delete rot; return bits; } SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp, float toa, CorrType type) { if (type == EDGE) return demodEdgeBurst(burst, sps, amp, toa); else return demodGmskBurst(burst, sps, amp, toa); } bool sigProcLibSetup() { generateSincTable(); initGMSKRotationTables(); GSMPulse1 = generateGSMPulse(1); GSMPulse4 = generateGSMPulse(4); generateRACHSequence(1); for (int tsc = 0; tsc < 8; tsc++) { generateMidamble(1, tsc); gEdgeMidambles[tsc] = generateEdgeMidamble(tsc); } generateDelayFilters(); dnsampler = new Resampler(1, 4); if (!dnsampler->init()) { LOG(ALERT) << "Rx resampler failed to initialize"; goto fail; } return true; fail: sigProcLibDestroy(); return false; } osmo-trx-0.4.0/Transceiver52M/Transceiver.h0000644000175000017500000002337213272615542017540 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "radioInterface.h" #include "Interthread.h" #include "GSMCommon.h" #include "Sockets.h" #include #include extern "C" { #include "config_defs.h" } class Transceiver; /** Channel descriptor for transceiver object and channel number pair */ struct TransceiverChannel { TransceiverChannel(Transceiver *trx, int num) { this->trx = trx; this->num = num; } ~TransceiverChannel() { } Transceiver *trx; size_t num; }; /** Internal transceiver state variables */ struct TransceiverState { TransceiverState(); ~TransceiverState(); /* Initialize a multiframe slot in the filler table */ bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay); int chanType[8]; /* Last timestamp of each timeslot's channel estimate */ GSM::Time chanEstimateTime[8]; /* The filler table */ signalVector *fillerTable[102][8]; int fillerModulus[8]; bool mRetrans; /* Most recent channel estimate of all timeslots */ signalVector *chanResponse[8]; /* Most recent DFE feedback filter of all timeslots */ signalVector *DFEForward[8]; signalVector *DFEFeedback[8]; /* Most recent SNR, timing, and channel amplitude estimates */ float SNRestimate[8]; float chanRespOffset[8]; complex chanRespAmplitude[8]; /* Received noise energy levels */ float mNoiseLev; noiseVector mNoises; /* Shadowed downlink attenuation */ int mPower; }; /** The Transceiver class, responsible for physical layer of basestation */ class Transceiver { public: /** Transceiver constructor @param wBasePort base port number of UDP sockets @param TRXAddress IP address of the TRX, as a string @param GSMcoreAddress IP address of the GSM core, as a string @param wSPS number of samples per GSM symbol @param wTransmitLatency initial setting of transmit latency @param radioInterface associated radioInterface object */ Transceiver(int wBasePort, const char *TRXAddress, const char *GSMcoreAddress, size_t tx_sps, size_t rx_sps, size_t chans, GSM::Time wTransmitLatency, RadioInterface *wRadioInterface, double wRssiOffset); /** Destructor */ ~Transceiver(); /** Start the control loop */ bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge); /** attach the radioInterface receive FIFO */ bool receiveFIFO(VectorFIFO *wFIFO, size_t chan) { if (chan >= mReceiveFIFO.size()) return false; mReceiveFIFO[chan] = wFIFO; return true; } /** accessor for number of channels */ size_t numChans() const { return mChans; }; /** Codes for channel combinations */ typedef enum { FILL, ///< Channel is transmitted, but unused I, ///< TCH/FS II, ///< TCH/HS, idle every other slot III, ///< TCH/HS IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4 VI, ///< CCCH+BCCH, uplink RACH VII, ///< SDCCH/8 + SACCH/8 VIII, ///< TCH/F + FACCH/F + SACCH/M IX, ///< TCH/F + SACCH/M X, ///< TCH/FD + SACCH/MD XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH XII, ///< PCCCH+PDTCH+PACCH+PTCCH XIII, ///< PDTCH+PACCH+PTCCH NONE, ///< Channel is inactive, default LOOPBACK ///< similar go VII, used in loopback testing } ChannelCombination; private: int mBasePort; std::string mLocalAddr; std::string mRemoteAddr; std::vector mDataSockets; ///< socket for writing to/reading from GSM core std::vector mCtrlSockets; ///< socket for writing/reading control commands from GSM core UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core std::vector mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core std::vector mReceiveFIFO; ///< radioInterface FIFO of receive bursts std::vector mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO std::vector mControlServiceLoopThreads; ///< thread to process control messages from GSM core std::vector mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock GSM::Time mLatencyUpdateTime; ///< last time latency was updated GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core RadioInterface *mRadioInterface; ///< associated radioInterface object double txFullScale; ///< full scale input to radio double rxFullScale; ///< full scale output to radio double rssiOffset; ///< RSSI to dBm conversion offset /** modulate and add a burst to the transmit queue */ void addRadioVector(size_t chan, BitVector &bits, int RSSI, GSM::Time &wTime); /** Update filler table */ void updateFillerTable(size_t chan, radioVector *burst); /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */ void pushRadioVector(GSM::Time &nowTime); /** Pull and demodulate a burst from the receive FIFO */ SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid, double &timingOffset, double &noise, size_t chan = 0); /** Set modulus for specific timeslot */ void setModulus(size_t timeslot, size_t chan); /** return the expected burst type for the specified timestamp */ CorrType expectedCorrType(GSM::Time currTime, size_t chan); /** send messages over the clock socket */ void writeClockInterface(void); int mSPSTx; ///< number of samples per Tx symbol int mSPSRx; ///< number of samples per Rx symbol size_t mChans; bool mEdge; bool mOn; ///< flag to indicate that transceiver is powered on bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started bool mHandover[8][8]; ///< expect handover to the timeslot/subslot double mTxFreq; ///< the transmit frequency double mRxFreq; ///< the receive frequency unsigned mTSC; ///< the midamble sequence code unsigned mMaxExpectedDelayAB; ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH) unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk std::vector mStates; /** Start and stop I/O threads through the control socket API */ bool start(); void stop(); /** Protect destructor accessable stop call */ Mutex mLock; protected: /** drive lower receive I/O and burst generation */ void driveReceiveRadio(); /** drive demodulation of GSM bursts */ void driveReceiveFIFO(size_t chan); /** drive transmission of GSM bursts */ void driveTxFIFO(); /** drive handling of control messages from GSM core */ void driveControl(size_t chan); /** drive modulation and sorting of GSM bursts from GSM core @return true if a burst was transferred successfully */ bool driveTxPriorityQueue(size_t chan); friend void *RxUpperLoopAdapter(TransceiverChannel *); friend void *TxUpperLoopAdapter(TransceiverChannel *); friend void *RxLowerLoopAdapter(Transceiver *); friend void *TxLowerLoopAdapter(Transceiver *); friend void *ControlServiceLoopAdapter(TransceiverChannel *); void reset(); /** set priority on current thread */ void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); } void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm, double rssi, double noise, double toa); }; void *RxUpperLoopAdapter(TransceiverChannel *); /** Main drive threads */ void *RxLowerLoopAdapter(Transceiver *); void *TxLowerLoopAdapter(Transceiver *); /** control message handler thread loop */ void *ControlServiceLoopAdapter(TransceiverChannel *); /** transmit queueing thread loop */ void *TxUpperLoopAdapter(TransceiverChannel *); osmo-trx-0.4.0/Transceiver52M/radioClock.h0000644000175000017500000000221113272615542017312 0ustar rubenruben/* * Written by Thomas Tsou * Based on code by Harvind S Samra * * Copyright 2011 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #ifndef RADIOCLOCK_H #define RADIOCLOCK_H #include "GSMCommon.h" class RadioClock { public: void set(const GSM::Time& wTime); void incTN(); GSM::Time get(); void wait(); private: GSM::Time mClock; Mutex mLock; Signal updateSignal; }; #endif /* RADIOCLOCK_H */ osmo-trx-0.4.0/Transceiver52M/Synthesis.cpp0000644000175000017500000000516113272615542017573 0ustar rubenruben/* * Polyphase synthesis filter * * Copyright (C) 2012-2014 Tom Tsou * Copyright (C) 2015 Ettus Research LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include #include #include #include #include #include #include "Synthesis.h" extern "C" { #include "fft.h" #include "convolve.h" } static void interleave(float **in, size_t ilen, float *out, size_t m) { size_t i, n; for (i = 0; i < ilen; i++) { for (n = 0; n < m; n++) { out[2 * (i * m + n) + 0] = in[n][2 * i + 0]; out[2 * (i * m + n) + 1] = in[n][2 * i + 1]; } } } size_t Synthesis::inputLen() const { return blockLen; } size_t Synthesis::outputLen() const { return blockLen * m; } float *Synthesis::inputBuffer(size_t chan) const { if (chan >= m) return NULL; return hOutputs[chan]; } bool Synthesis::resetBuffer(size_t chan) { if (chan >= m) return false; memset(hOutputs[chan], 0, blockLen * 2 * sizeof(float)); return true; } /* * Implementation based on material found in: * * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ, * Prentice Hall, 2006." */ bool Synthesis::rotate(float *out, size_t len) { size_t hSize = 2 * hLen * sizeof(float); if (!checkLen(blockLen, len)) { std::cout << "Length fail" << std::endl; exit(1); return false; } cxvec_fft(fftHandle); /* * Convolve through filterbank while applying and saving sample history */ for (size_t i = 0; i < m; i++) { memcpy(&hInputs[i][2 * -hLen], hist[i], hSize); memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize); convolve_real(hInputs[i], blockLen, subFilters[i], hLen, hOutputs[i], blockLen, 0, blockLen, 1, 0); } /* Interleave into output vector */ interleave(hOutputs, blockLen, out, m); return true; } Synthesis::Synthesis(size_t m, size_t blockLen, size_t hLen) : ChannelizerBase(m, blockLen, hLen) { } Synthesis::~Synthesis() { } osmo-trx-0.4.0/Transceiver52M/Synthesis.h0000644000175000017500000000203713272615542017237 0ustar rubenruben#ifndef _SYNTHESIS_H_ #define _SYNTHESIS_H_ #include "ChannelizerBase.h" class Synthesis : public ChannelizerBase { public: /** Constructor for synthesis filterbank @param m number of physical channels @param blockLen number of samples per output of each iteration @param hLen number of taps in each constituent filter path */ Synthesis(size_t m, size_t blockLen, size_t hLen = 16); ~Synthesis(); /* Return required input and output buffer lengths */ size_t inputLen() const; size_t outputLen() const; /** Rotate "output commutator" and drive samples through filterbank @param out complex output vector @param oLen number of samples in buffer (must match block length * m) @return false on error and true otherwise */ bool rotate(float *out, size_t oLen); /** Get buffer for an input path @param chan channel number of filterbank @return NULL on error and pointer to buffer otherwise */ float *inputBuffer(size_t chan) const; bool resetBuffer(size_t chan); }; #endif /* _SYNTHESIS_H_ */ osmo-trx-0.4.0/Transceiver52M/Resampler.cpp0000644000175000017500000001100113272615542017522 0ustar rubenruben/* * Rational Sample Rate Conversion * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "Resampler.h" extern "C" { #include "convolve.h" } #ifndef M_PI #define M_PI 3.14159265358979323846264338327f #endif #define MAX_OUTPUT_LEN 4096 using namespace std; static float sinc(float x) { if (x == 0.0) return 0.9999999999; return sin(M_PI * x) / (M_PI * x); } void Resampler::initFilters(float bw) { float cutoff; float sum = 0.0f, scale = 0.0f; /* * Allocate partition filters and the temporary prototype filter * according to numerator of the rational rate. Coefficients are * real only and must be 16-byte memory aligned for SSE usage. */ auto proto = vector(p * filt_len); for (auto &part : partitions) part = (complex *) memalign(16, filt_len * sizeof(complex)); /* * Generate the prototype filter with a Blackman-harris window. * Scale coefficients with DC filter gain set to unity divided * by the number of filter partitions. */ float a0 = 0.35875; float a1 = 0.48829; float a2 = 0.14128; float a3 = 0.01168; if (p > q) cutoff = (float) p; else cutoff = (float) q; float midpt = (proto.size() - 1) / 2.0; for (size_t i = 0; i < proto.size(); i++) { proto[i] = sinc(((float) i - midpt) / cutoff * bw); proto[i] *= a0 - a1 * cos(2 * M_PI * i / (proto.size() - 1)) + a2 * cos(4 * M_PI * i / (proto.size() - 1)) - a3 * cos(6 * M_PI * i / (proto.size() - 1)); sum += proto[i]; } scale = p / sum; /* Populate filter partitions from the prototype filter */ for (size_t i = 0; i < filt_len; i++) { for (size_t n = 0; n < p; n++) partitions[n][i] = complex(proto[i * p + n] * scale); } /* Store filter taps in reverse */ for (auto &part : partitions) reverse(&part[0], &part[filt_len]); } static bool check_vec_len(int in_len, int out_len, int p, int q) { if (in_len % q) { std::cerr << "Invalid input length " << in_len << " is not multiple of " << q << std::endl; return false; } if (out_len % p) { std::cerr << "Invalid output length " << out_len << " is not multiple of " << p << std::endl; return false; } if ((in_len / q) != (out_len / p)) { std::cerr << "Input/output block length mismatch" << std::endl; std::cerr << "P = " << p << ", Q = " << q << std::endl; std::cerr << "Input len: " << in_len << std::endl; std::cerr << "Output len: " << out_len << std::endl; return false; } if (out_len > MAX_OUTPUT_LEN) { std::cerr << "Block length of " << out_len << " exceeds max of " << MAX_OUTPUT_LEN << std::endl; return false; } return true; } int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len) { int n, path; if (!check_vec_len(in_len, out_len, p, q)) return -1; /* Generate output from precomputed input/output paths */ for (size_t i = 0; i < out_len; i++) { n = in_index[i]; path = out_path[i]; convolve_real(in, in_len, reinterpret_cast(partitions[path]), filt_len, &out[2 * i], out_len - i, n, 1, 1, 0); } return out_len; } bool Resampler::init(float bw) { if (p == 0 || q == 0 || filt_len == 0) return false; /* Filterbank filter internals */ initFilters(bw); /* Precompute filterbank paths */ int i = 0; for (auto &index : in_index) index = (q * i++) / p; i = 0; for (auto &path : out_path) path = (q * i++) % p; return true; } size_t Resampler::len() { return filt_len; } Resampler::Resampler(size_t p, size_t q, size_t filt_len) : in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p) { this->p = p; this->q = q; this->filt_len = filt_len; } Resampler::~Resampler() { for (auto &part : partitions) free(part); } osmo-trx-0.4.0/Transceiver52M/radioInterface.cpp0000644000175000017500000002104413272615542020517 0ustar rubenruben/* * Radio device interface * * Copyright (C) 2008-2014 Free Software Foundation, Inc. * Copyright (C) 2015 Ettus Research LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include "radioInterface.h" #include "Resampler.h" #include extern "C" { #include "convert.h" } #define CHUNK 625 #define NUMCHUNKS 4 RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps, size_t rx_sps, size_t chans, int wReceiveOffset, GSM::Time wStartTime) : mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), underrun(false), overrun(false), receiveOffset(wReceiveOffset), mOn(false) { mClock.set(wStartTime); } RadioInterface::~RadioInterface(void) { close(); } bool RadioInterface::init(int type) { if ((type != RadioDevice::NORMAL) || !mChans) { LOG(ALERT) << "Invalid configuration"; return false; } close(); sendBuffer.resize(mChans); recvBuffer.resize(mChans); convertSendBuffer.resize(mChans); convertRecvBuffer.resize(mChans); mReceiveFIFO.resize(mChans); powerScaling.resize(mChans); for (size_t i = 0; i < mChans; i++) { sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true); recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false); convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2]; convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2]; powerScaling[i] = 1.0; } return true; } void RadioInterface::close() { sendBuffer.resize(0); recvBuffer.resize(0); convertSendBuffer.resize(0); convertRecvBuffer.resize(0); } double RadioInterface::fullScaleInputValue(void) { return mRadio->fullScaleInputValue(); } double RadioInterface::fullScaleOutputValue(void) { return mRadio->fullScaleOutputValue(); } int RadioInterface::setPowerAttenuation(int atten, size_t chan) { double rfGain, digAtten; if (chan >= mChans) { LOG(ALERT) << "Invalid channel requested"; return -1; } if (atten < 0.0) atten = 0.0; rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan); digAtten = (double) atten - mRadio->maxTxGain() + rfGain; if (digAtten < 1.0) powerScaling[chan] = 1.0; else powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0)); return atten; } int RadioInterface::radioifyVector(signalVector &wVector, size_t chan, bool zero) { if (zero) sendBuffer[chan]->zero(wVector.size()); else sendBuffer[chan]->write((float *) wVector.begin(), wVector.size()); return wVector.size(); } int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan) { if (newVector->size() > recvBuffer[chan]->getAvailSamples()) { LOG(ALERT) << "Insufficient number of samples in receive buffer"; return -1; } recvBuffer[chan]->read((float *) newVector->begin(), newVector->size()); return newVector->size(); } bool RadioInterface::tuneTx(double freq, size_t chan) { return mRadio->setTxFreq(freq, chan); } bool RadioInterface::tuneRx(double freq, size_t chan) { return mRadio->setRxFreq(freq, chan); } /** synchronization thread loop */ void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface) { while (1) { sleep(60); radioInterface->alignRadio(); pthread_testcancel(); } return NULL; } void RadioInterface::alignRadio() { mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000); } bool RadioInterface::start() { if (mOn) return true; LOG(INFO) << "Starting radio device"; if (mRadio->requiresRadioAlign()) mAlignRadioServiceLoopThread.start( (void * (*)(void*))AlignRadioServiceLoopAdapter, (void*)this); if (!mRadio->start()) return false; for (size_t i = 0; i < mChans; i++) { sendBuffer[i]->reset(); recvBuffer[i]->reset(); } writeTimestamp = mRadio->initialWriteTimestamp(); readTimestamp = mRadio->initialReadTimestamp(); mRadio->updateAlignment(writeTimestamp-10000); mRadio->updateAlignment(writeTimestamp-10000); mOn = true; LOG(INFO) << "Radio started"; return true; } /* * Stop the radio device * * This is a pass-through call to the device interface. Because the underlying * stop command issuance generally doesn't return confirmation on device status, * this call will only return false if the device is already stopped. */ bool RadioInterface::stop() { if (!mOn || !mRadio->stop()) return false; mOn = false; return true; } void RadioInterface::driveTransmitRadio(std::vector &bursts, std::vector &zeros) { if (!mOn) return; for (size_t i = 0; i < mChans; i++) radioifyVector(*bursts[i], i, zeros[i]); while (pushBuffer()); } bool RadioInterface::driveReceiveRadio() { radioVector *burst = NULL; if (!mOn) return false; pullBuffer(); GSM::Time rcvClock = mClock.get(); rcvClock.decTN(receiveOffset); unsigned tN = rcvClock.TN(); int recvSz = recvBuffer[0]->getAvailSamples(); const int symbolsPerSlot = gSlotLen + 8; int burstSize; if (mSPSRx == 4) burstSize = 625; else burstSize = symbolsPerSlot + (tN % 4 == 0); /* * Pre-allocate head room for the largest correlation size * so we can later avoid a re-allocation and copy * */ size_t head = GSM::gRACHSynchSequence.size(); /* * Form receive bursts and pass up to transceiver. Use repeating * pattern of 157-156-156-156 symbols per timeslot */ while (recvSz > burstSize) { for (size_t i = 0; i < mChans; i++) { burst = new radioVector(rcvClock, burstSize, head); unRadioifyVector(burst->getVector(), i); if (mReceiveFIFO[i].size() < 32) mReceiveFIFO[i].write(burst); else delete burst; } mClock.incTN(); rcvClock.incTN(); recvSz -= burstSize; tN = rcvClock.TN(); if (mSPSRx != 4) burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx; } return true; } bool RadioInterface::isUnderrun() { bool retVal = underrun; underrun = false; return retVal; } VectorFIFO* RadioInterface::receiveFIFO(size_t chan) { if (chan >= mReceiveFIFO.size()) return NULL; return &mReceiveFIFO[chan]; } double RadioInterface::setRxGain(double dB, size_t chan) { return mRadio->setRxGain(dB, chan); } double RadioInterface::getRxGain(size_t chan) { return mRadio->getRxGain(chan); } /* Receive a timestamped chunk from the device */ void RadioInterface::pullBuffer() { bool local_underrun; size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen(); if (recvBuffer[0]->getFreeSegments() <= 0) return; /* Outer buffer access size is fixed */ numRecv = mRadio->readSamples(convertRecvBuffer, segmentLen, &overrun, readTimestamp, &local_underrun); if (numRecv != segmentLen) { LOG(ALERT) << "Receive error " << numRecv; return; } for (size_t i = 0; i < mChans; i++) { convert_short_float(recvBuffer[i]->getWriteSegment(), convertRecvBuffer[i], segmentLen * 2); } underrun |= local_underrun; readTimestamp += numRecv; } /* Send timestamped chunk to the device with arbitrary size */ bool RadioInterface::pushBuffer() { size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen(); if (sendBuffer[0]->getAvailSegments() < 1) return false; for (size_t i = 0; i < mChans; i++) { convert_float_short(convertSendBuffer[i], (float *) sendBuffer[i]->getReadSegment(), powerScaling[i], segmentLen * 2); } /* Send the all samples in the send buffer */ numSent = mRadio->writeSamples(convertSendBuffer, segmentLen, &underrun, writeTimestamp); writeTimestamp += numSent; return true; } osmo-trx-0.4.0/Transceiver52M/radioInterface.h0000644000175000017500000001226613272615542020172 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include "sigProcLib.h" #include "GSMCommon.h" #include "LinkedLists.h" #include "radioDevice.h" #include "radioVector.h" #include "radioClock.h" #include "radioBuffer.h" #include "Resampler.h" #include "Channelizer.h" #include "Synthesis.h" static const unsigned gSlotLen = 148; ///< number of symbols per slot, not counting guard periods /** class to interface the transceiver with the USRP */ class RadioInterface { protected: Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections std::vector mReceiveFIFO; ///< FIFO that holds receive bursts RadioDevice *mRadio; ///< the USRP object size_t mSPSTx; size_t mSPSRx; size_t mChans; std::vector sendBuffer; std::vector recvBuffer; std::vector convertRecvBuffer; std::vector convertSendBuffer; std::vector powerScaling; bool underrun; ///< indicates writes to USRP are too slow bool overrun; ///< indicates reads from USRP are too slow TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP TIMESTAMP readTimestamp; ///< sample timestamp of next packet read from USRP RadioClock mClock; ///< the basestation clock! int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots bool mOn; ///< indicates radio is on private: /** format samples to USRP */ int radioifyVector(signalVector &wVector, size_t chan, bool zero); /** format samples from USRP */ int unRadioifyVector(signalVector *wVector, size_t chan); /** push GSM bursts into the transmit buffer */ virtual bool pushBuffer(void); /** pull GSM bursts from the receive buffer */ virtual void pullBuffer(void); public: /** start the interface */ bool start(); bool stop(); /** intialization */ virtual bool init(int type); virtual void close(); /** constructor */ RadioInterface(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps, size_t chans = 1, int receiveOffset = 3, GSM::Time wStartTime = GSM::Time(0)); /** destructor */ virtual ~RadioInterface(); /** check for underrun, resets underrun value */ bool isUnderrun(); /** return the receive FIFO */ VectorFIFO* receiveFIFO(size_t chan = 0); /** return the basestation clock */ RadioClock* getClock(void) { return &mClock;}; /** set transmit frequency */ virtual bool tuneTx(double freq, size_t chan = 0); /** set receive frequency */ virtual bool tuneRx(double freq, size_t chan = 0); /** set receive gain */ double setRxGain(double dB, size_t chan = 0); /** get receive gain */ double getRxGain(size_t chan = 0); /** drive transmission of GSM bursts */ void driveTransmitRadio(std::vector &bursts, std::vector &zeros); /** drive reception of GSM bursts */ bool driveReceiveRadio(); int setPowerAttenuation(int atten, size_t chan = 0); /** returns the full-scale transmit amplitude **/ double fullScaleInputValue(); /** returns the full-scale receive amplitude **/ double fullScaleOutputValue(); /** set thread priority on current thread */ void setPriority(float prio = 0.5) { mRadio->setPriority(prio); } /** get transport window type of attached device */ enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); } /** Minimum latency that the device can achieve */ GSM::Time minLatency() { return mRadio->minLatency(); } protected: /** drive synchronization of Tx/Rx of USRP */ void alignRadio(); friend void *AlignRadioServiceLoopAdapter(RadioInterface*); }; class RadioInterfaceResamp : public RadioInterface { private: signalVector *outerSendBuffer; signalVector *outerRecvBuffer; bool pushBuffer(); void pullBuffer(); public: RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps); ~RadioInterfaceResamp(); bool init(int type); void close(); }; class RadioInterfaceMulti : public RadioInterface { private: bool pushBuffer(); void pullBuffer(); signalVector *outerSendBuffer; signalVector *outerRecvBuffer; std::vector history; std::vector active; Resampler *dnsampler; Resampler *upsampler; Channelizer *channelizer; Synthesis *synthesis; public: RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps, size_t rx_sps, size_t chans = 1); ~RadioInterfaceMulti(); bool init(int type); void close(); bool tuneTx(double freq, size_t chan); bool tuneRx(double freq, size_t chan); double setRxGain(double dB, size_t chan); }; osmo-trx-0.4.0/Transceiver52M/pulseApproximate.m0000644000175000017500000000041213272615542020610 0ustar rubenrubenpp = [0 0 0.015 0.18 0.7 0.96 0.7 0.18 0.015 0 0]; t = -2.5:0.5:2.5; v = -0.000:-0.001:-1.999; for ix1 = 1:length(v), disp(ix1); for ix2 = 1:length(v), p = exp(v(ix1)*t.^2+v(ix2)*t.^4); r(ix1,ix2) = norm(p./max(abs(p)) - pp./max(abs(pp))); end; end; osmo-trx-0.4.0/Transceiver52M/radioBuffer.h0000644000175000017500000000176413272615542017504 0ustar rubenruben#include #include #include class RadioBuffer { public: RadioBuffer(size_t numSegments, size_t segmentLen, size_t hLen, bool outDirection); ~RadioBuffer(); const size_t getSegmentLen() { return segmentLen; } const size_t getNumSegments() { return numSegments; } const size_t getAvailSamples() { return availSamples; } const size_t getAvailSegments() { return availSamples / segmentLen; } const size_t getFreeSamples() { return bufferLen - availSamples; } const size_t getFreeSegments() { return getFreeSamples() / segmentLen; } void reset(); /* Output direction */ const float *getReadSegment(); bool write(const float *wr, size_t len); bool zero(size_t len); /* Input direction */ float *getWriteSegment(); bool zeroWriteSegment(); bool read(float *rd, size_t len); private: size_t writeIndex, readIndex, availSamples; size_t bufferLen, numSegments, segmentLen, hLen; float *buffer; std::vector segments; bool outDirection; }; osmo-trx-0.4.0/Transceiver52M/Resampler.h0000644000175000017500000000506613272615542017205 0ustar rubenruben/* * Rational Sample Rate Conversion * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _RESAMPLER_H_ #define _RESAMPLER_H_ #include #include class Resampler { public: /* Constructor for rational sample rate conversion * @param p numerator of resampling ratio * @param q denominator of resampling ratio * @param filt_len length of each polyphase subfilter */ Resampler(size_t p, size_t q, size_t filt_len = 16); ~Resampler(); /* Initilize resampler filterbank. * @param bw bandwidth factor on filter generation (pre-window) * @return false on error, zero otherwise * * Automatic setting is to compute the filter to prevent aliasing with * a Blackman-Harris window. Adjustment is made through a bandwith * factor to shift the cutoff and/or the constituent filter lengths. * Calculation of specific rolloff factors or 3-dB cutoff points is * left as an excersize for the reader. */ bool init(float bw = 1.0f); /* Rotate "commutator" and drive samples through filterbank * @param in continuous buffer of input complex float values * @param in_len input buffer length * @param out continuous buffer of output complex float values * @param out_len output buffer length * @return number of samples outputted, negative on error * * Input and output vector lengths must of be equal multiples of the * rational conversion rate denominator and numerator respectively. */ int rotate(const float *in, size_t in_len, float *out, size_t out_len); /* Get filter length * @return number of taps in each filter partition */ size_t len(); private: size_t p; size_t q; size_t filt_len; std::vector in_index; std::vector out_path; std::vector *> partitions; void initFilters(float bw); }; #endif /* _RESAMPLER_H_ */ osmo-trx-0.4.0/Transceiver52M/Channelizer.cpp0000644000175000017500000000467713272615542020057 0ustar rubenruben/* * Polyphase channelizer * * Copyright (C) 2012-2014 Tom Tsou * Copyright (C) 2015 Ettus Research LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include #include #include #include #include #include "Channelizer.h" extern "C" { #include "fft.h" #include "convolve.h" } static void deinterleave(const float *in, size_t ilen, float **out, size_t olen, size_t m) { size_t i, n; for (i = 0; i < olen; i++) { for (n = 0; n < m; n++) { out[m - 1 - n][2 * i + 0] = in[2 * (i * m + n) + 0]; out[m - 1 - n][2 * i + 1] = in[2 * (i * m + n) + 1]; } } } size_t Channelizer::inputLen() const { return blockLen * m; } size_t Channelizer::outputLen() const { return blockLen; } float *Channelizer::outputBuffer(size_t chan) const { if (chan >= m) return NULL; return hInputs[chan]; } /* * Implementation based on material found in: * * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ, * Prentice Hall, 2006." */ bool Channelizer::rotate(const float *in, size_t len) { size_t hSize = 2 * hLen * sizeof(float); if (!checkLen(blockLen, len)) return false; deinterleave(in, len, hInputs, blockLen, m); /* * Convolve through filterbank while applying and saving sample history */ for (size_t i = 0; i < m; i++) { memcpy(&hInputs[i][2 * -hLen], hist[i], hSize); memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize); convolve_real(hInputs[i], blockLen, subFilters[i], hLen, hOutputs[i], blockLen, 0, blockLen, 1, 0); } cxvec_fft(fftHandle); return true; } /* Setup channelizer paramaters */ Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen) : ChannelizerBase(m, blockLen, hLen) { } Channelizer::~Channelizer() { } osmo-trx-0.4.0/Transceiver52M/radioInterfaceMulti.cpp0000644000175000017500000002200013272615542021523 0ustar rubenruben/* * Multi-carrier radio interface * * Copyright (C) 2016 Ettus Research LLC * * Author: Tom Tsou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include #include #include "Resampler.h" extern "C" { #include "convert.h" } /* Resampling parameters for 64 MHz clocking */ #define RESAMP_INRATE 65 #define RESAMP_OUTRATE (96 / 2) /* Universal resampling parameters */ #define NUMCHUNKS 24 #define MCHANS 4 RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps, size_t rx_sps, size_t chans) : RadioInterface(radio, tx_sps, rx_sps, chans), outerSendBuffer(NULL), outerRecvBuffer(NULL), dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL) { } RadioInterfaceMulti::~RadioInterfaceMulti() { close(); } void RadioInterfaceMulti::close() { delete outerSendBuffer; delete outerRecvBuffer; delete dnsampler; delete upsampler; delete channelizer; delete synthesis; outerSendBuffer = NULL; outerRecvBuffer = NULL; dnsampler = NULL; upsampler = NULL; channelizer = NULL; synthesis = NULL; mReceiveFIFO.resize(0); powerScaling.resize(0); history.resize(0); active.resize(0); RadioInterface::close(); } static int getLogicalChan(size_t pchan, size_t chans) { switch (chans) { case 1: if (pchan == 0) return 0; else return -1; break; case 2: if (pchan == 0) return 0; if (pchan == 3) return 1; else return -1; break; case 3: if (pchan == 1) return 0; if (pchan == 0) return 1; if (pchan == 3) return 2; else return -1; break; default: break; }; return -1; } static int getFreqShift(size_t chans) { switch (chans) { case 1: return 0; case 2: return 0; case 3: return 1; default: break; }; return -1; } /* Initialize I/O specific objects */ bool RadioInterfaceMulti::init(int type) { float cutoff = 1.0f; size_t inchunk = 0, outchunk = 0; if (mChans > MCHANS - 1) { LOG(ALERT) << "Invalid channel configuration " << mChans; return false; } close(); sendBuffer.resize(mChans); recvBuffer.resize(mChans); convertSendBuffer.resize(1); convertRecvBuffer.resize(1); mReceiveFIFO.resize(mChans); powerScaling.resize(mChans); history.resize(mChans); active.resize(MCHANS, false); inchunk = RESAMP_INRATE * 4; outchunk = RESAMP_OUTRATE * 4; if (inchunk * NUMCHUNKS < 625 * 2) { LOG(ALERT) << "Invalid inner chunk size " << inchunk; return false; } dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE); if (!dnsampler->init(1.0)) { LOG(ALERT) << "Rx resampler failed to initialize"; return false; } upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE); if (!upsampler->init(cutoff)) { LOG(ALERT) << "Tx resampler failed to initialize"; return false; } channelizer = new Channelizer(MCHANS, outchunk); if (!channelizer->init()) { LOG(ALERT) << "Rx channelizer failed to initialize"; return false; } synthesis = new Synthesis(MCHANS, outchunk); if (!synthesis->init()) { LOG(ALERT) << "Tx synthesis filter failed to initialize"; return false; } /* * Allocate high and low rate buffers. The high rate receive * buffer and low rate transmit vectors feed into the resampler * and requires headroom equivalent to the filter length. Low * rate buffers are allocated in the main radio interface code. */ for (size_t i = 0; i < mChans; i++) { sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk, upsampler->len(), true); recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk, 0, false); history[i] = new signalVector(dnsampler->len()); synthesis->resetBuffer(i); } outerSendBuffer = new signalVector(synthesis->outputLen()); outerRecvBuffer = new signalVector(channelizer->inputLen()); convertSendBuffer[0] = new short[2 * synthesis->outputLen()]; convertRecvBuffer[0] = new short[2 * channelizer->inputLen()]; /* Configure channels */ switch (mChans) { case 1: active[0] = true; break; case 2: active[0] = true; active[3] = true; break; case 3: active[0] = true; active[1] = true; active[3] = true; break; default: LOG(ALERT) << "Unsupported channel combination"; return false; } return true; } /* Receive a timestamped chunk from the device */ void RadioInterfaceMulti::pullBuffer() { bool local_underrun; size_t num; float *buf; if (recvBuffer[0]->getFreeSegments() <= 0) return; /* Outer buffer access size is fixed */ num = mRadio->readSamples(convertRecvBuffer, outerRecvBuffer->size(), &overrun, readTimestamp, &local_underrun); if (num != channelizer->inputLen()) { LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen(); return; } convert_short_float((float *) outerRecvBuffer->begin(), convertRecvBuffer[0], 2 * outerRecvBuffer->size()); underrun |= local_underrun; readTimestamp += num; channelizer->rotate((float *) outerRecvBuffer->begin(), outerRecvBuffer->size()); for (size_t pchan = 0; pchan < MCHANS; pchan++) { if (!active[pchan]) continue; int lchan = getLogicalChan(pchan, mChans); if (lchan < 0) { LOG(ALERT) << "Invalid logical channel " << pchan; continue; } /* * Update history by writing into the head portion of the * channelizer output buffer. For this to work, filter length of * the polyphase channelizer partition filter should be equal to * or larger than the resampling filter. */ buf = channelizer->outputBuffer(pchan); size_t cLen = channelizer->outputLen(); size_t hLen = dnsampler->len(); size_t hSize = 2 * hLen * sizeof(float); memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize); memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize); float *wr_segment = recvBuffer[lchan]->getWriteSegment(); /* Write to the end of the inner receive buffer */ if (!dnsampler->rotate(channelizer->outputBuffer(pchan), channelizer->outputLen(), wr_segment, recvBuffer[lchan]->getSegmentLen())) { LOG(ALERT) << "Sample rate upsampling error"; } } } /* Send a timestamped chunk to the device */ bool RadioInterfaceMulti::pushBuffer() { if (sendBuffer[0]->getAvailSegments() <= 0) return false; for (size_t pchan = 0; pchan < MCHANS; pchan++) { if (!active[pchan]) { synthesis->resetBuffer(pchan); continue; } int lchan = getLogicalChan(pchan, mChans); if (lchan < 0) { LOG(ALERT) << "Invalid logical channel " << pchan; continue; } if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(), sendBuffer[lchan]->getSegmentLen(), synthesis->inputBuffer(pchan), synthesis->inputLen())) { LOG(ALERT) << "Sample rate downsampling error"; } } synthesis->rotate((float *) outerSendBuffer->begin(), outerSendBuffer->size()); convert_float_short(convertSendBuffer[0], (float *) outerSendBuffer->begin(), 1.0 / (float) mChans, 2 * outerSendBuffer->size()); size_t num = mRadio->writeSamples(convertSendBuffer, outerSendBuffer->size(), &underrun, writeTimestamp); if (num != outerSendBuffer->size()) { LOG(ALERT) << "Transmit error " << num; } writeTimestamp += num; return true; } /* Frequency comparison limit */ #define FREQ_DELTA_LIMIT 10.0 static bool fltcmp(double a, double b) { return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false; } bool RadioInterfaceMulti::tuneTx(double freq, size_t chan) { if (chan >= mChans) return false; double shift = (double) getFreqShift(mChans); if (!chan) return mRadio->setTxFreq(freq + shift * MCBTS_SPACING); double center = mRadio->getTxFreq(); if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) { LOG(NOTICE) << "Channel " << chan << " RF frequency offset is " << freq / 1e6 << " MHz"; } return true; } bool RadioInterfaceMulti::tuneRx(double freq, size_t chan) { if (chan >= mChans) return false; double shift = (double) getFreqShift(mChans); if (!chan) return mRadio->setRxFreq(freq + shift * MCBTS_SPACING); double center = mRadio->getRxFreq(); if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) { LOG(NOTICE) << "Channel " << chan << " RF frequency offset is " << freq / 1e6 << " MHz"; } return true; } double RadioInterfaceMulti::setRxGain(double db, size_t chan) { if (!chan) return mRadio->setRxGain(db); else return mRadio->getRxGain(); } osmo-trx-0.4.0/Transceiver52M/ChannelizerBase.cpp0000644000175000017500000001303513272615542020636 0ustar rubenruben/* * Polyphase channelizer * * Copyright (C) 2012-2014 Tom Tsou * Copyright (C) 2015 Ettus Research LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include #include #include #include #include #include "Logger.h" #include "ChannelizerBase.h" extern "C" { #include "fft.h" } static float sinc(float x) { if (x == 0.0f) return 0.999999999999f; return sin(M_PI * x) / (M_PI * x); } /* * There are more efficient reversal algorithms, but we only reverse at * initialization so we don't care. */ static void reverse(float *buf, size_t len) { float tmp[2 * len]; memcpy(tmp, buf, 2 * len * sizeof(float)); for (size_t i = 0; i < len; i++) { buf[2 * i + 0] = tmp[2 * (len - 1 - i) + 0]; buf[2 * i + 1] = tmp[2 * (len - 1 - i) + 1]; } } /* * Create polyphase filterbank * * Implementation based material found in, * * "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ, * Prentice Hall, 2006." */ bool ChannelizerBase::initFilters() { size_t protoLen = m * hLen; float *proto; float sum = 0.0f, scale = 0.0f; float midpt = (float) (protoLen - 1.0) / 2.0; /* * Allocate 'M' partition filters and the temporary prototype * filter. Coefficients are real only and must be 16-byte memory * aligned for SSE usage. */ proto = new float[protoLen]; if (!proto) return false; subFilters = (float **) malloc(sizeof(float *) * m); if (!subFilters) { delete[] proto; return false; } for (size_t i = 0; i < m; i++) { subFilters[i] = (float *) memalign(16, hLen * 2 * sizeof(float)); } /* * Generate the prototype filter with a Blackman-harris window. * Scale coefficients with DC filter gain set to unity divided * by the number of channels. */ float a0 = 0.35875; float a1 = 0.48829; float a2 = 0.14128; float a3 = 0.01168; for (size_t i = 0; i < protoLen; i++) { proto[i] = sinc(((float) i - midpt) / (float) m); proto[i] *= a0 - a1 * cos(2 * M_PI * i / (protoLen - 1)) + a2 * cos(4 * M_PI * i / (protoLen - 1)) - a3 * cos(6 * M_PI * i / (protoLen - 1)); sum += proto[i]; } scale = (float) m / sum; /* * Populate partition filters and reverse the coefficients per * convolution requirements. */ for (size_t i = 0; i < hLen; i++) { for (size_t n = 0; n < m; n++) { subFilters[n][2 * i + 0] = proto[i * m + n] * scale; subFilters[n][2 * i + 1] = 0.0f; } } for (size_t i = 0; i < m; i++) reverse(subFilters[i], hLen); delete[] proto; return true; } bool ChannelizerBase::initFFT() { size_t size; if (fftInput || fftOutput || fftHandle) return false; size = blockLen * m * 2 * sizeof(float); fftInput = (float *) fft_malloc(size); memset(fftInput, 0, size); size = (blockLen + hLen) * m * 2 * sizeof(float); fftOutput = (float *) fft_malloc(size); memset(fftOutput, 0, size); if (!fftInput | !fftOutput) { LOG(ALERT) << "Memory allocation error"; return false; } fftHandle = init_fft(0, m, blockLen, blockLen + hLen, fftInput, fftOutput, hLen); return true; } bool ChannelizerBase::mapBuffers() { if (!fftHandle) { LOG(ALERT) << "FFT buffers not initialized"; return false; } hInputs = (float **) malloc(sizeof(float *) * m); hOutputs = (float **) malloc(sizeof(float *) * m); if (!hInputs | !hOutputs) return false; for (size_t i = 0; i < m; i++) { hInputs[i] = &fftOutput[2 * (i * (blockLen + hLen) + hLen)]; hOutputs[i] = &fftInput[2 * (i * blockLen)]; } return true; } /* * Setup filterbank internals */ bool ChannelizerBase::init() { /* * Filterbank coefficients, fft plan, history, and output sample * rate conversion blocks */ if (!initFilters()) { LOG(ALERT) << "Failed to initialize channelizing filter"; return false; } hist = (float **) malloc(sizeof(float *) * m); for (size_t i = 0; i < m; i++) { hist[i] = new float[2 * hLen]; memset(hist[i], 0, 2 * hLen * sizeof(float)); } if (!initFFT()) { LOG(ALERT) << "Failed to initialize FFT"; return false; } mapBuffers(); return true; } /* Check vector length validity */ bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen) { if (outerLen != innerLen * m) { LOG(ALERT) << "Invalid outer length " << innerLen << " is not multiple of " << blockLen; return false; } if (innerLen != blockLen) { LOG(ALERT) << "Invalid inner length " << outerLen << " does not equal " << blockLen; return false; } return true; } /* * Setup channelizer paramaters */ ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen) : fftInput(NULL), fftOutput(NULL), fftHandle(NULL) { this->m = m; this->hLen = hLen; this->blockLen = blockLen; } ChannelizerBase::~ChannelizerBase() { free_fft(fftHandle); for (size_t i = 0; i < m; i++) { free(subFilters[i]); delete hist[i]; } fft_free(fftInput); fft_free(fftOutput); free(hInputs); free(hOutputs); free(hist); } osmo-trx-0.4.0/Transceiver52M/README.DFEsymbolspaced0000644000175000017500000000054213272615542020757 0ustar rubenrubensignalVectors G0, G1. i.e. G0(D) = 1 +2D + 3D^2 = [1 2 3] G0(D) = 1/sqrt(SNR). G1(D) = [h0 h1D .. h_(N-1)D^(N-1)] for i = 0,1,...,N_f-1, d = |G0(0)|^2+|G1(0)|^2 l_i(D) = D^i ( G0(D)*G0'(0) + G1(D)*G1'(0) )/d k = G1(0)/G0(0) G0n(D) = G0(D)+k'*G1(D) G1n(D) = (-G0(D)*k+G1(D))/D G0(D) = G0n(D)/sqrt(1+k*k') G1(D) = G1n(D)/sqrt(1+k*k') end osmo-trx-0.4.0/Transceiver52M/Complex.h0000644000175000017500000001547213272615542016664 0ustar rubenruben/**@file templates for Complex classes unlike the built-in complex<> templates, these inline most operations for speed */ /* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef COMPLEXCPP_H #define COMPLEXCPP_H #include #include template class Complex { public: Real r, i; /**@name constructors */ //@{ /**@name from real */ //@{ Complex(Real real, Real imag) {r=real; i=imag;} // x=complex(a,b) Complex(Real real) {r=real; i=0;} // x=complex(a) //@} /**@name from nothing */ //@{ Complex() {r=(Real)0; i=(Real)0;} // x=complex() //@} /**@name from other complex */ //@{ Complex(const Complex& z) {r=z.r; i=z.i;} // x=complex(z) Complex(const Complex& z) {r=z.r; i=z.i;} // x=complex(z) Complex(const Complex& z) {r=z.r; i=z.i;} // x=complex(z) //@} //@} /**@name casting up from basic numeric types */ //@{ Complex& operator=(char a) { r=(Real)a; i=(Real)0; return *this; } Complex& operator=(int a) { r=(Real)a; i=(Real)0; return *this; } Complex& operator=(long int a) { r=(Real)a; i=(Real)0; return *this; } Complex& operator=(short a) { r=(Real)a; i=(Real)0; return *this; } Complex& operator=(float a) { r=(Real)a; i=(Real)0; return *this; } Complex& operator=(double a) { r=(Real)a; i=(Real)0; return *this; } Complex& operator=(long double a) { r=(Real)a; i=(Real)0; return *this; } //@} /**@name arithmetic */ //@{ /**@ binary operators */ //@{ Complex operator+(const Complex& a) const { return Complex(r+a.r, i+a.i); } Complex operator+(Real a) const { return Complex(r+a,i); } Complex operator-(const Complex& a) const { return Complex(r-a.r, i-a.i); } Complex operator-(Real a) const { return Complex(r-a,i); } Complex operator*(const Complex& a) const { return Complex(r*a.r-i*a.i, r*a.i+i*a.r); } Complex operator*(Real a) const { return Complex(r*a, i*a); } Complex operator/(const Complex& a) const { return operator*(a.inv()); } Complex operator/(Real a) const { return Complex(r/a, i/a); } //@} /*@name component-wise product */ //@{ Complex operator&(const Complex& a) const { return Complex(r*a.r, i*a.i); } //@} /*@name inplace operations */ //@{ Complex& operator+=(const Complex&); Complex& operator-=(const Complex&); Complex& operator*=(const Complex&); Complex& operator/=(const Complex&); Complex& operator+=(Real); Complex& operator-=(Real); Complex& operator*=(Real); Complex& operator/=(Real); //@} //@} /**@name comparisons */ //@{ bool operator==(const Complex& a) const { return ((i==a.i)&&(r==a.r)); } bool operator!=(const Complex& a) const { return ((i!=a.i)||(r!=a.r)); } bool operator<(const Complex& a) const { return norm2()(const Complex& a) const { return norm2()>a.norm2(); } //@} /// reciprocation Complex inv() const; // unary functions -- inlined /**@name unary functions */ //@{ /**@name inlined */ //@{ Complex conj() const { return Complex(r,-i); } Real norm2() const { return i*i+r*r; } Complex flip() const { return Complex(i,r); } Real real() const { return r;} Real imag() const { return i;} Complex neg() const { return Complex(-r, -i); } bool isZero() const { return ((r==(Real)0) && (i==(Real)0)); } //@} /**@name not inlined due to outside calls */ //@{ Real abs() const { return ::sqrt(norm2()); } Real arg() const { return ::atan2(i,r); } float dB() const { return 10.0*log10(norm2()); } Complex exp() const { return expj(i)*(::exp(r)); } Complex unit() const; ///< unit phasor with same angle Complex log() const { return Complex(::log(abs()),arg()); } Complex pow(double n) const { return expj(arg()*n)*(::pow(abs(),n)); } Complex sqrt() const { return pow(0.5); } //@} //@} }; /**@name standard Complex manifestations */ //@{ typedef Complex complex; typedef Complex dcomplex; typedef Complex complex16; typedef Complex complex32; //@} template inline Complex Complex::inv() const { Real nVal; nVal = norm2(); return Complex(r/nVal, -i/nVal); } template Complex& Complex::operator+=(const Complex& a) { r += a.r; i += a.i; return *this; } template Complex& Complex::operator*=(const Complex& a) { operator*(a); return *this; } template Complex& Complex::operator-=(const Complex& a) { r -= a.r; i -= a.i; return *this; } template Complex& Complex::operator/=(const Complex& a) { operator/(a); return *this; } /* op= style operations with reals */ template Complex& Complex::operator+=(Real a) { r += a; return *this; } template Complex& Complex::operator*=(Real a) { r *=a; i *=a; return *this; } template Complex& Complex::operator-=(Real a) { r -= a; return *this; } template Complex& Complex::operator/=(Real a) { r /= a; i /= a; return *this; } template Complex Complex::unit() const { Real absVal = abs(); return (Complex(r/absVal, i/absVal)); } /**@name complex functions outside of the Complex<> class. */ //@{ /** this allows type-commutative multiplication */ template Complex operator*(Real a, const Complex& z) { return Complex(z.r*a, z.i*a); } /** this allows type-commutative addition */ template Complex operator+(Real a, const Complex& z) { return Complex(z.r+a, z.i); } /** this allows type-commutative subtraction */ template Complex operator-(Real a, const Complex& z) { return Complex(z.r-a, z.i); } /// e^jphi template Complex expj(Real phi) { return Complex(cos(phi),sin(phi)); } /// phasor expression of a complex number template Complex phasor(Real C, Real phi) { return (expj(phi)*C); } /// formatted stream output template std::ostream& operator<<(std::ostream& os, const Complex& z) { os << z.r << ' '; //os << z.r << ", "; //if (z.i>=0) { os << "+"; } os << z.i << "j"; return os; } //@} #endif osmo-trx-0.4.0/Transceiver52M/Transceiver.cpp0000644000175000017500000007430713272615542020077 0ustar rubenruben/* * Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include // std::setprecision #include #include "Transceiver.h" #include #ifdef HAVE_CONFIG_H #include "config.h" #endif using namespace GSM; #define USB_LATENCY_INTRVL 10,0 /* Number of running values use in noise average */ #define NOISE_CNT 20 TransceiverState::TransceiverState() : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0) { for (int i = 0; i < 8; i++) { chanType[i] = Transceiver::NONE; fillerModulus[i] = 26; chanResponse[i] = NULL; DFEForward[i] = NULL; DFEFeedback[i] = NULL; for (int n = 0; n < 102; n++) fillerTable[n][i] = NULL; } } TransceiverState::~TransceiverState() { for (int i = 0; i < 8; i++) { delete chanResponse[i]; delete DFEForward[i]; delete DFEFeedback[i]; for (int n = 0; n < 102; n++) delete fillerTable[n][i]; } } bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay) { signalVector *burst; if ((sps != 1) && (sps != 4)) return false; for (size_t n = 0; n < 8; n++) { for (size_t i = 0; i < 102; i++) { switch (filler) { case FILLER_DUMMY: burst = generateDummyBurst(sps, n); break; case FILLER_NORM_RAND: burst = genRandNormalBurst(rtsc, sps, n); break; case FILLER_EDGE_RAND: burst = generateEdgeBurst(rtsc); break; case FILLER_ACCESS_RAND: burst = genRandAccessBurst(rach_delay, sps, n); break; case FILLER_ZERO: default: burst = generateEmptyBurst(sps, n); } scaleVector(*burst, scale); fillerTable[i][n] = burst; } if ((filler == FILLER_NORM_RAND) || (filler == FILLER_EDGE_RAND)) { chanType[n] = TSC; } } return false; } Transceiver::Transceiver(int wBasePort, const char *TRXAddress, const char *GSMcoreAddress, size_t tx_sps, size_t rx_sps, size_t chans, GSM::Time wTransmitLatency, RadioInterface *wRadioInterface, double wRssiOffset) : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress), mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface), rssiOffset(wRssiOffset), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false), mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0) { txFullScale = mRadioInterface->fullScaleInputValue(); rxFullScale = mRadioInterface->fullScaleOutputValue(); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) mHandover[i][j] = false; } } Transceiver::~Transceiver() { stop(); sigProcLibDestroy(); for (size_t i = 0; i < mChans; i++) { mControlServiceLoopThreads[i]->cancel(); mControlServiceLoopThreads[i]->join(); delete mControlServiceLoopThreads[i]; mTxPriorityQueues[i].clear(); delete mCtrlSockets[i]; delete mDataSockets[i]; } } /* * Initialize transceiver * * Start or restart the control loop. Any further control is handled through the * socket API. Randomize the central radio clock set the downlink burst * counters. Note that the clock will not update until the radio starts, but we * are still expected to report clock indications through control channel * activity. */ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge) { int d_srcport, d_dstport, c_srcport, c_dstport; if (!mChans) { LOG(ALERT) << "No channels assigned"; return false; } if (!sigProcLibSetup()) { LOG(ALERT) << "Failed to initialize signal processing library"; return false; } mEdge = edge; mDataSockets.resize(mChans); mCtrlSockets.resize(mChans); mControlServiceLoopThreads.resize(mChans); mTxPriorityQueueServiceLoopThreads.resize(mChans); mRxServiceLoopThreads.resize(mChans); mTxPriorityQueues.resize(mChans); mReceiveFIFO.resize(mChans); mStates.resize(mChans); /* Filler table retransmissions - support only on channel 0 */ if (filler == FILLER_DUMMY) mStates[0].mRetrans = true; /* Setup sockets */ for (size_t i = 0; i < mChans; i++) { c_srcport = mBasePort + 2 * i + 1; c_dstport = mBasePort + 2 * i + 101; d_srcport = mBasePort + 2 * i + 2; d_dstport = mBasePort + 2 * i + 102; mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport); mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport); } /* Randomize the central clock */ GSM::Time startTime(random() % gHyperframe, 0); mRadioInterface->getClock()->set(startTime); mTransmitDeadlineClock = startTime; mLastClockUpdateTime = startTime; mLatencyUpdateTime = startTime; /* Start control threads */ for (size_t i = 0; i < mChans; i++) { TransceiverChannel *chan = new TransceiverChannel(this, i); mControlServiceLoopThreads[i] = new Thread(32768); mControlServiceLoopThreads[i]->start((void * (*)(void*)) ControlServiceLoopAdapter, (void*) chan); if (i && filler == FILLER_DUMMY) filler = FILLER_ZERO; mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay); } return true; } /* * Start the transceiver * * Submit command(s) to the radio device to commence streaming samples and * launch threads to handle sample I/O. Re-synchronize the transmit burst * counters to the central radio clock here as well. */ bool Transceiver::start() { ScopedLock lock(mLock); if (mOn) { LOG(ERR) << "Transceiver already running"; return true; } LOG(NOTICE) << "Starting the transceiver"; GSM::Time time = mRadioInterface->getClock()->get(); mTransmitDeadlineClock = time; mLastClockUpdateTime = time; mLatencyUpdateTime = time; if (!mRadioInterface->start()) { LOG(ALERT) << "Device failed to start"; return false; } /* Device is running - launch I/O threads */ mRxLowerLoopThread = new Thread(32768); mTxLowerLoopThread = new Thread(32768); mTxLowerLoopThread->start((void * (*)(void*)) TxLowerLoopAdapter,(void*) this); mRxLowerLoopThread->start((void * (*)(void*)) RxLowerLoopAdapter,(void*) this); /* Launch uplink and downlink burst processing threads */ for (size_t i = 0; i < mChans; i++) { TransceiverChannel *chan = new TransceiverChannel(this, i); mRxServiceLoopThreads[i] = new Thread(32768); mRxServiceLoopThreads[i]->start((void * (*)(void*)) RxUpperLoopAdapter, (void*) chan); chan = new TransceiverChannel(this, i); mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768); mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*)) TxUpperLoopAdapter, (void*) chan); } mForceClockInterface = true; mOn = true; return true; } /* * Stop the transceiver * * Perform stopping by disabling receive streaming and issuing cancellation * requests to running threads. Most threads will timeout and terminate once * device is disabled, but the transmit loop may block waiting on the central * UMTS clock. Explicitly signal the clock to make sure that the transmit loop * makes it to the thread cancellation point. */ void Transceiver::stop() { ScopedLock lock(mLock); if (!mOn) return; LOG(NOTICE) << "Stopping the transceiver"; mTxLowerLoopThread->cancel(); mRxLowerLoopThread->cancel(); mTxLowerLoopThread->join(); mRxLowerLoopThread->join(); delete mTxLowerLoopThread; delete mRxLowerLoopThread; for (size_t i = 0; i < mChans; i++) { mRxServiceLoopThreads[i]->cancel(); mTxPriorityQueueServiceLoopThreads[i]->cancel(); } LOG(INFO) << "Stopping the device"; mRadioInterface->stop(); for (size_t i = 0; i < mChans; i++) { mRxServiceLoopThreads[i]->join(); mTxPriorityQueueServiceLoopThreads[i]->join(); delete mRxServiceLoopThreads[i]; delete mTxPriorityQueueServiceLoopThreads[i]; mTxPriorityQueues[i].clear(); } mOn = false; LOG(NOTICE) << "Transceiver stopped"; } void Transceiver::addRadioVector(size_t chan, BitVector &bits, int RSSI, GSM::Time &wTime) { signalVector *burst; radioVector *radio_burst; if (chan >= mTxPriorityQueues.size()) { LOG(ALERT) << "Invalid channel " << chan; return; } if (wTime.TN() > 7) { LOG(ALERT) << "Received burst with invalid slot " << wTime.TN(); return; } /* Use the number of bits as the EDGE burst indicator */ if (bits.size() == EDGE_BURST_NBITS) burst = modulateEdgeBurst(bits, mSPSTx); else burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx); scaleVector(*burst, txFullScale * pow(10, -RSSI / 10)); radio_burst = new radioVector(wTime, burst); mTxPriorityQueues[chan].write(radio_burst); } void Transceiver::updateFillerTable(size_t chan, radioVector *burst) { int TN, modFN; TransceiverState *state = &mStates[chan]; TN = burst->getTime().TN(); modFN = burst->getTime().FN() % state->fillerModulus[TN]; delete state->fillerTable[modFN][TN]; state->fillerTable[modFN][TN] = burst->getVector(); burst->setVector(NULL); } void Transceiver::pushRadioVector(GSM::Time &nowTime) { int TN, modFN; radioVector *burst; TransceiverState *state; std::vector bursts(mChans); std::vector zeros(mChans); std::vector filler(mChans, true); for (size_t i = 0; i < mChans; i ++) { state = &mStates[i]; while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) { LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->USRP interface (" << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans; if (state->mRetrans) updateFillerTable(i, burst); delete burst; } TN = nowTime.TN(); modFN = nowTime.FN() % state->fillerModulus[TN]; bursts[i] = state->fillerTable[modFN][TN]; zeros[i] = state->chanType[TN] == NONE; if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) { bursts[i] = burst->getVector(); if (state->mRetrans) { updateFillerTable(i, burst); } else { burst->setVector(NULL); filler[i] = false; } delete burst; } } mRadioInterface->driveTransmitRadio(bursts, zeros); for (size_t i = 0; i < mChans; i++) { if (!filler[i]) delete bursts[i]; } } void Transceiver::setModulus(size_t timeslot, size_t chan) { TransceiverState *state = &mStates[chan]; switch (state->chanType[timeslot]) { case NONE: case I: case II: case III: case FILL: state->fillerModulus[timeslot] = 26; break; case IV: case VI: case V: state->fillerModulus[timeslot] = 51; break; //case V: case VII: state->fillerModulus[timeslot] = 102; break; case XIII: state->fillerModulus[timeslot] = 52; break; default: break; } } CorrType Transceiver::expectedCorrType(GSM::Time currTime, size_t chan) { static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 }; static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2, 3,3,3,3,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2 }; static int sdcch8_subslot[102] = { 5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0, 1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,4,4,4,4 }; TransceiverState *state = &mStates[chan]; unsigned burstTN = currTime.TN(); unsigned burstFN = currTime.FN(); int subch; switch (state->chanType[burstTN]) { case NONE: return OFF; break; case FILL: return IDLE; break; case I: // TODO: Are we expecting RACH on an IDLE frame? /* if (burstFN % 26 == 25) return IDLE;*/ if (mHandover[burstTN][0]) return RACH; return TSC; break; case II: subch = tchh_subslot[burstFN % 26]; if (subch == 1) return IDLE; if (mHandover[burstTN][0]) return RACH; return TSC; break; case III: subch = tchh_subslot[burstFN % 26]; if (mHandover[burstTN][subch]) return RACH; return TSC; break; case IV: case VI: return RACH; break; case V: { int mod51 = burstFN % 51; if ((mod51 <= 36) && (mod51 >= 14)) return RACH; else if ((mod51 == 4) || (mod51 == 5)) return RACH; else if ((mod51 == 45) || (mod51 == 46)) return RACH; else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]]) return RACH; else return TSC; break; } case VII: if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12)) return IDLE; else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]]) return RACH; else return TSC; break; case XIII: { int mod52 = burstFN % 52; if ((mod52 == 12) || (mod52 == 38)) return RACH; else if ((mod52 == 25) || (mod52 == 51)) return IDLE; else return TSC; break; } case LOOPBACK: if ((burstFN % 51 <= 50) && (burstFN % 51 >=48)) return IDLE; else return TSC; break; default: return OFF; break; } } void writeToFile(radioVector *radio_burst, size_t chan) { GSM::Time time = radio_burst->getTime(); std::ostringstream fname; fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc"; std::ofstream outfile (fname.str().c_str(), std::ofstream::binary); outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float)); outfile.close(); } /* * Pull bursts from the FIFO and handle according to the slot * and burst correlation type. Equalzation is currently disabled. */ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid, double &timingOffset, double &noise, size_t chan) { int rc; complex amp; float toa, max = -1.0, avg = 0.0; int max_i = -1; signalVector *burst; SoftVector *bits = NULL; TransceiverState *state = &mStates[chan]; isRssiValid = false; /* Blocking FIFO read */ radioVector *radio_burst = mReceiveFIFO[chan]->read(); if (!radio_burst) return NULL; /* Set time and determine correlation type */ GSM::Time time = radio_burst->getTime(); CorrType type = expectedCorrType(time, chan); /* Enable 8-PSK burst detection if EDGE is enabled */ if (mEdge && (type == TSC)) type = EDGE; /* Debug: dump bursts to disk */ /* bits 0-7 - chan 0 timeslots * bits 8-15 - chan 1 timeslots */ if (mWriteBurstToDiskMask & ((1<chans(); i++) { float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx); if (pow > max) { max = pow; max_i = i; } avg += pow; } if (max_i < 0) { LOG(ALERT) << "Received empty burst"; delete radio_burst; return NULL; } /* Average noise on diversity paths and update global levels */ burst = radio_burst->getVector(max_i); avg = sqrt(avg / radio_burst->chans()); wTime = time; RSSI = 20.0 * log10(rxFullScale / avg); /* RSSI estimation are valid */ isRssiValid = true; if (type == IDLE) { /* Update noise levels */ state->mNoises.insert(avg); state->mNoiseLev = state->mNoises.avg(); noise = 20.0 * log10(rxFullScale / state->mNoiseLev); delete radio_burst; return NULL; } else { /* Do not update noise levels */ noise = 20.0 * log10(rxFullScale / state->mNoiseLev); } /* Detect normal or RACH bursts */ rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, (type==RACH)?mMaxExpectedDelayAB:mMaxExpectedDelayNB); if (rc > 0) { type = (CorrType) rc; } else if (rc <= 0) { if (rc == -SIGERR_CLIP) { LOG(WARNING) << "Clipping detected on received RACH or Normal Burst"; } else if (rc != SIGERR_NONE) { LOG(WARNING) << "Unhandled RACH or Normal Burst detection error"; } delete radio_burst; return NULL; } timingOffset = toa; bits = demodAnyBurst(*burst, mSPSRx, amp, toa, type); delete radio_burst; return bits; } void Transceiver::reset() { for (size_t i = 0; i < mTxPriorityQueues.size(); i++) mTxPriorityQueues[i].clear(); } #define MAX_PACKET_LENGTH 100 /** * Matches a buffer with a command. * @param buf a buffer to look command in * @param cmd a command to look in buffer * @param params pointer to arguments, or NULL * @return true if command matches, otherwise false */ static bool match_cmd(char *buf, const char *cmd, char **params) { size_t cmd_len = strlen(cmd); /* Check a command itself */ if (strncmp(buf, cmd, cmd_len)) return false; /* A command has arguments */ if (params != NULL) { /* Make sure there is a space */ if (buf[cmd_len] != ' ') return false; /* Update external pointer */ *params = buf + cmd_len + 1; } return true; } void Transceiver::driveControl(size_t chan) { char buffer[MAX_PACKET_LENGTH + 1]; char response[MAX_PACKET_LENGTH + 1]; char *command, *params; int msgLen; /* Attempt to read from control socket */ msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH); if (msgLen < 1) return; /* Zero-terminate received string */ buffer[msgLen] = '\0'; /* Verify a command signature */ if (strncmp(buffer, "CMD ", 4)) { LOG(WARNING) << "bogus message on control interface"; return; } /* Set command pointer */ command = buffer + 4; LOG(INFO) << "command is " << command; if (match_cmd(command, "POWEROFF", NULL)) { stop(); sprintf(response,"RSP POWEROFF 0"); } else if (match_cmd(command, "POWERON", NULL)) { if (!start()) { sprintf(response,"RSP POWERON 1"); } else { sprintf(response,"RSP POWERON 0"); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) mHandover[i][j] = false; } } } else if (match_cmd(command, "HANDOVER", ¶ms)) { unsigned ts = 0, ss = 0; sscanf(params, "%u %u", &ts, &ss); if (ts > 7 || ss > 7) { sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss); } else { mHandover[ts][ss] = true; sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss); } } else if (match_cmd(command, "NOHANDOVER", ¶ms)) { unsigned ts = 0, ss = 0; sscanf(params, "%u %u", &ts, &ss); if (ts > 7 || ss > 7) { sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss); } else { mHandover[ts][ss] = false; sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss); } } else if (match_cmd(command, "SETMAXDLY", ¶ms)) { //set expected maximum time-of-arrival int maxDelay; sscanf(params, "%d", &maxDelay); mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay); } else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) { //set expected maximum time-of-arrival int maxDelay; sscanf(params, "%d", &maxDelay); mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay); } else if (match_cmd(command, "SETRXGAIN", ¶ms)) { //set expected maximum time-of-arrival int newGain; sscanf(params, "%d", &newGain); newGain = mRadioInterface->setRxGain(newGain, chan); sprintf(response,"RSP SETRXGAIN 0 %d",newGain); } else if (match_cmd(command, "NOISELEV", NULL)) { if (mOn) { float lev = mStates[chan].mNoiseLev; sprintf(response,"RSP NOISELEV 0 %d", (int) round(20.0 * log10(rxFullScale / lev))); } else { sprintf(response,"RSP NOISELEV 1 0"); } } else if (match_cmd(command, "SETPOWER", ¶ms)) { int power; sscanf(params, "%d", &power); power = mRadioInterface->setPowerAttenuation(power, chan); mStates[chan].mPower = power; sprintf(response, "RSP SETPOWER 0 %d", power); } else if (match_cmd(command, "ADJPOWER", ¶ms)) { int power, step; sscanf(params, "%d", &step); power = mStates[chan].mPower + step; power = mRadioInterface->setPowerAttenuation(power, chan); mStates[chan].mPower = power; sprintf(response, "RSP ADJPOWER 0 %d", power); } else if (match_cmd(command, "RXTUNE", ¶ms)) { // tune receiver int freqKhz; sscanf(params, "%d", &freqKhz); mRxFreq = freqKhz * 1e3; if (!mRadioInterface->tuneRx(mRxFreq, chan)) { LOG(ALERT) << "RX failed to tune"; sprintf(response,"RSP RXTUNE 1 %d",freqKhz); } else sprintf(response,"RSP RXTUNE 0 %d",freqKhz); } else if (match_cmd(command, "TXTUNE", ¶ms)) { // tune txmtr int freqKhz; sscanf(params, "%d", &freqKhz); mTxFreq = freqKhz * 1e3; if (!mRadioInterface->tuneTx(mTxFreq, chan)) { LOG(ALERT) << "TX failed to tune"; sprintf(response,"RSP TXTUNE 1 %d",freqKhz); } else sprintf(response,"RSP TXTUNE 0 %d",freqKhz); } else if (match_cmd(command, "SETTSC", ¶ms)) { // set TSC unsigned TSC; sscanf(params, "%u", &TSC); if (TSC > 7) { sprintf(response, "RSP SETTSC 1 %d", TSC); } else { LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC; mTSC = TSC; sprintf(response,"RSP SETTSC 0 %d", TSC); } } else if (match_cmd(command, "SETSLOT", ¶ms)) { // set slot type int corrCode; int timeslot; sscanf(params, "%d %d", ×lot, &corrCode); if ((timeslot < 0) || (timeslot > 7)) { LOG(WARNING) << "bogus message on control interface"; sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode); return; } mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode; setModulus(timeslot, chan); sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode); } else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) { // debug command! may change or disapear without notice // set a mask which bursts to dump to disk int mask; sscanf(params, "%d", &mask); mWriteBurstToDiskMask = mask; sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask); } else { LOG(WARNING) << "bogus command " << command << " on control interface."; sprintf(response,"RSP ERR 1"); } mCtrlSockets[chan]->write(response, strlen(response) + 1); } bool Transceiver::driveTxPriorityQueue(size_t chan) { int burstLen; char buffer[EDGE_BURST_NBITS + 50]; // check data socket size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer)); if (msgLen == gSlotLen + 1 + 4 + 1) { burstLen = gSlotLen; } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) { if (mSPSTx != 4) return false; burstLen = EDGE_BURST_NBITS; } else { LOG(ERR) << "badly formatted packet on GSM->TRX interface"; return false; } int timeSlot = (int) buffer[0]; uint64_t frameNum = 0; for (int i = 0; i < 4; i++) frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]); LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot); int RSSI = (int) buffer[5]; BitVector newBurst(burstLen); BitVector::iterator itr = newBurst.begin(); char *bufferItr = buffer+6; while (itr < newBurst.end()) *itr++ = *bufferItr++; GSM::Time currTime = GSM::Time(frameNum,timeSlot); addRadioVector(chan, newBurst, RSSI, currTime); return true; } void Transceiver::driveReceiveRadio() { if (!mRadioInterface->driveReceiveRadio()) { usleep(100000); } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) { mForceClockInterface = false; writeClockInterface(); } } void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm, double rssi, double noise, double toa) { LOG(DEBUG) << std::fixed << std::right << " chan: " << chan << " time: " << time << " RSSI: " << std::setw(5) << std::setprecision(1) << rssi << "dBFS/" << std::setw(6) << -dbm << "dBm" << " noise: " << std::setw(5) << std::setprecision(1) << noise << "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm" << " TOA: " << std::setw(5) << std::setprecision(2) << toa << " bits: " << *burst; } void Transceiver::driveReceiveFIFO(size_t chan) { SoftVector *rxBurst = NULL; double RSSI; // in dBFS double dBm; // in dBm double TOA; // in symbols int TOAint; // in 1/256 symbols double noise; // noise level in dBFS GSM::Time burstTime; bool isRssiValid; // are RSSI, noise and burstTime valid unsigned nbits = gSlotLen; rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan); if (!rxBurst) return; // Convert -1..+1 soft bits to 0..1 soft bits vectorSlicer(rxBurst); /* * EDGE demodulator returns 444 (148 * 3) bits */ if (rxBurst->size() == gSlotLen * 3) nbits = gSlotLen * 3; dBm = RSSI + rssiOffset; logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA); TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer char burstString[nbits + 10]; burstString[0] = burstTime.TN(); for (int i = 0; i < 4; i++) burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff; burstString[5] = (int)dBm; burstString[6] = (TOAint >> 8) & 0x0ff; burstString[7] = TOAint & 0x0ff; SoftVector::iterator burstItr = rxBurst->begin(); for (unsigned i = 0; i < nbits; i++) burstString[8 + i] = (char) round((*burstItr++) * 255.0); burstString[nbits + 9] = '\0'; delete rxBurst; mDataSockets[chan]->write(burstString, nbits + 10); } void Transceiver::driveTxFIFO() { /** Features a carefully controlled latency mechanism, to assure that transmit packets arrive at the radio/USRP before they need to be transmitted. Deadline clock indicates the burst that needs to be pushed into the FIFO right NOW. If transmit queue does not have a burst, stick in filler data. */ RadioClock *radioClock = (mRadioInterface->getClock()); if (mOn) { //radioClock->wait(); // wait until clock updates LOG(DEBUG) << "radio clock " << radioClock->get(); while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) { // if underrun, then we're not providing bursts to radio/USRP fast // enough. Need to increase latency by one GSM frame. if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) { if (mRadioInterface->isUnderrun()) { // only update latency at the defined frame interval if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) { mTransmitLatency = mTransmitLatency + GSM::Time(1,0); LOG(INFO) << "new latency: " << mTransmitLatency; mLatencyUpdateTime = radioClock->get(); } } else { // if underrun hasn't occurred in the last sec (216 frames) drop // transmit latency by a timeslot if (mTransmitLatency > mRadioInterface->minLatency()) { if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) { mTransmitLatency.decTN(); LOG(INFO) << "reduced latency: " << mTransmitLatency; mLatencyUpdateTime = radioClock->get(); } } } } // time to push burst to transmit FIFO pushRadioVector(mTransmitDeadlineClock); mTransmitDeadlineClock.incTN(); } } radioClock->wait(); } void Transceiver::writeClockInterface() { char command[50]; // FIXME -- This should be adaptive. sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2)); LOG(INFO) << "ClockInterface: sending " << command; mClockSocket.write(command, strlen(command) + 1); mLastClockUpdateTime = mTransmitDeadlineClock; } void *RxUpperLoopAdapter(TransceiverChannel *chan) { Transceiver *trx = chan->trx; size_t num = chan->num; delete chan; trx->setPriority(0.42); while (1) { trx->driveReceiveFIFO(num); pthread_testcancel(); } return NULL; } void *RxLowerLoopAdapter(Transceiver *transceiver) { transceiver->setPriority(0.45); while (1) { transceiver->driveReceiveRadio(); pthread_testcancel(); } return NULL; } void *TxLowerLoopAdapter(Transceiver *transceiver) { transceiver->setPriority(0.44); while (1) { transceiver->driveTxFIFO(); pthread_testcancel(); } return NULL; } void *ControlServiceLoopAdapter(TransceiverChannel *chan) { Transceiver *trx = chan->trx; size_t num = chan->num; delete chan; while (1) { trx->driveControl(num); pthread_testcancel(); } return NULL; } void *TxUpperLoopAdapter(TransceiverChannel *chan) { Transceiver *trx = chan->trx; size_t num = chan->num; delete chan; trx->setPriority(0.40); while (1) { trx->driveTxPriorityQueue(num); pthread_testcancel(); } return NULL; } osmo-trx-0.4.0/Transceiver52M/device/0000755000175000017500000000000013272615542016332 5ustar rubenrubenosmo-trx-0.4.0/Transceiver52M/device/radioDevice.h0000644000175000017500000001270313272615542020724 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef __RADIO_DEVICE_H__ #define __RADIO_DEVICE_H__ #include #include #include "GSMCommon.h" extern "C" { #include "config_defs.h" } #ifdef HAVE_CONFIG_H #include "config.h" #endif #define GSMRATE (1625e3/6) #define MCBTS_SPACING 800000.0 /** a 64-bit virtual timestamp for radio data */ typedef unsigned long long TIMESTAMP; /** A class to handle a USRP rev 4, with a two RFX900 daughterboards */ class RadioDevice { public: /* Available transport bus types */ enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED }; /* Radio interface types */ enum InterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, MULTI_ARFCN, }; static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans = 1, double offset = 0.0, const std::vector& tx_paths = std::vector(1, ""), const std::vector& rx_paths = std::vector(1, "")); /** Initialize the USRP */ virtual int open(const std::string &args, int ref, bool swap_channels)=0; virtual ~RadioDevice() { } /** Start the USRP */ virtual bool start()=0; /** Stop the USRP */ virtual bool stop()=0; /** Get the Tx window type */ virtual enum TxWindowType getWindowType()=0; /** Enable thread priority */ virtual void setPriority(float prio = 0.5) = 0; /** Read samples from the radio. @param buf preallocated buf to contain read result @param len number of samples desired @param overrun Set if read buffer has been overrun, e.g. data not being read fast enough @param timestamp The timestamp of the first samples to be read @param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough @param RSSI The received signal strength of the read result @return The number of samples actually read */ virtual int readSamples(std::vector &bufs, int len, bool *overrun, TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0, unsigned *RSSI = 0) = 0; /** Write samples to the radio. @param buf Contains the data to be written. @param len number of samples to write. @param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough @param timestamp The timestamp of the first sample of the data buffer. @param isControl Set if data is a control packet, e.g. a ping command @return The number of samples actually written */ virtual int writeSamples(std::vector &bufs, int len, bool *underrun, TIMESTAMP timestamp, bool isControl = false) = 0; /** Update the alignment between the read and write timestamps */ virtual bool updateAlignment(TIMESTAMP timestamp)=0; /** Set the transmitter frequency */ virtual bool setTxFreq(double wFreq, size_t chan = 0) = 0; /** Set the receiver frequency */ virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0; /** Returns the starting write Timestamp*/ virtual TIMESTAMP initialWriteTimestamp(void)=0; /** Returns the starting read Timestamp*/ virtual TIMESTAMP initialReadTimestamp(void)=0; /** returns the full-scale transmit amplitude **/ virtual double fullScaleInputValue()=0; /** returns the full-scale receive amplitude **/ virtual double fullScaleOutputValue()=0; /** sets the receive chan gain, returns the gain setting **/ virtual double setRxGain(double dB, size_t chan = 0) = 0; /** gets the current receive gain **/ virtual double getRxGain(size_t chan = 0) = 0; /** return maximum Rx Gain **/ virtual double maxRxGain(void) = 0; /** return minimum Rx Gain **/ virtual double minRxGain(void) = 0; /** sets the transmit chan gain, returns the gain setting **/ virtual double setTxGain(double dB, size_t chan = 0) = 0; /** return maximum Tx Gain **/ virtual double maxTxGain(void) = 0; /** return minimum Tx Gain **/ virtual double minTxGain(void) = 0; /** sets the RX path to use, returns true if successful and false otherwise */ virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0; /** return the used RX path */ virtual std::string getRxAntenna(size_t chan = 0) = 0; /** sets the RX path to use, returns true if successful and false otherwise */ virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0; /** return the used RX path */ virtual std::string getTxAntenna(size_t chan = 0) = 0; /** return whether user drives synchronization of Tx/Rx of USRP */ virtual bool requiresRadioAlign() = 0; /** Minimum latency that the device can achieve */ virtual GSM::Time minLatency() = 0; /** Return internal status values */ virtual double getTxFreq(size_t chan = 0) = 0; virtual double getRxFreq(size_t chan = 0) = 0; virtual double getSampleRate()=0; virtual double numberRead()=0; virtual double numberWritten()=0; }; #endif osmo-trx-0.4.0/Transceiver52M/device/Makefile.am0000644000175000017500000000023513272615542020366 0ustar rubenrubeninclude $(top_srcdir)/Makefile.common noinst_HEADERS = radioDevice.h SUBDIRS = if DEVICE_USRP1 SUBDIRS += usrp1 endif if DEVICE_UHD SUBDIRS += uhd endif osmo-trx-0.4.0/Transceiver52M/device/uhd/0000755000175000017500000000000013272615542017112 5ustar rubenrubenosmo-trx-0.4.0/Transceiver52M/device/uhd/UHDDevice.cpp0000644000175000017500000012451013272615542021361 0ustar rubenruben/* * Device support for Ettus Research UHD driver * * Copyright 2010,2011 Free Software Foundation, Inc. * Copyright (C) 2015 Ettus Research LLC * * Author: Tom Tsou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include #include "radioDevice.h" #include "Threads.h" #include "Logger.h" #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef USE_UHD_3_11 #include #endif #define USRP_TX_AMPL 0.3 #define UMTRX_TX_AMPL 0.7 #define LIMESDR_TX_AMPL 0.3 #define SAMPLE_BUF_SZ (1 << 20) /* * UHD timeout value on streaming (re)start * * Allow some time for streaming to commence after the start command is issued, * but consider a wait beyond one second to be a definite error condition. */ #define UHD_RESTART_TIMEOUT 1.0 /* * UmTRX specific settings */ #define UMTRX_VGA1_DEF -18 enum uhd_dev_type { USRP1, USRP2, B100, B200, B210, B2XX_MCBTS, E1XX, E3XX, X3XX, UMTRX, LIMESDR, }; /* * USRP version dependent device timings */ #if defined(USE_UHD_3_9) || defined(USE_UHD_3_11) #define B2XX_TIMING_1SPS 1.7153e-4 #define B2XX_TIMING_4SPS 1.1696e-4 #define B2XX_TIMING_4_4SPS 6.18462e-5 #define B2XX_TIMING_MCBTS 7e-5 #else #define B2XX_TIMING_1SPS 9.9692e-5 #define B2XX_TIMING_4SPS 6.9248e-5 #define B2XX_TIMING_4_4SPS 4.52308e-5 #define B2XX_TIMING_MCBTS 6.42452e-5 #endif /* * Tx / Rx sample offset values. In a perfect world, there is no group delay * though analog components, and behaviour through digital filters exactly * matches calculated values. In reality, there are unaccounted factors, * which are captured in these empirically measured (using a loopback test) * timing correction values. * * Notes: * USRP1 with timestamps is not supported by UHD. */ /* Device Type, Tx-SPS, Rx-SPS */ typedef std::tuple dev_key; /* Device parameter descriptor */ struct dev_desc { unsigned channels; double mcr; double rate; double offset; std::string str; }; static const std::map dev_param_map { { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } }, { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } }, { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } }, { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } }, { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } }, { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } }, { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } }, { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } }, { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } }, { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } }, { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } }, { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } }, { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } }, { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } }, { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } }, { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } }, { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } }, { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } }, { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } }, { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} }, { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } }, { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } }, { std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } }, }; /* Sample Buffer - Allows reading and writing of timed samples using osmo-trx or UHD style timestamps. Time conversions are handled internally or accessable through the static convert calls. */ class smpl_buf { public: /** Sample buffer constructor @param len number of 32-bit samples the buffer should hold @param rate sample clockrate @param timestamp */ smpl_buf(size_t len, double rate); ~smpl_buf(); /** Query number of samples available for reading @param timestamp time of first sample @return number of available samples or error */ ssize_t avail_smpls(TIMESTAMP timestamp) const; ssize_t avail_smpls(uhd::time_spec_t timestamp) const; /** Read and write @param buf pointer to buffer @param len number of samples desired to read or write @param timestamp time of first stample @return number of actual samples read or written or error */ ssize_t read(void *buf, size_t len, TIMESTAMP timestamp); ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp); ssize_t write(void *buf, size_t len, TIMESTAMP timestamp); ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp); /** Buffer status string @return a formatted string describing internal buffer state */ std::string str_status(size_t ts) const; /** Formatted error string @param code an error code @return a formatted error string */ static std::string str_code(ssize_t code); enum err_code { ERROR_TIMESTAMP = -1, ERROR_READ = -2, ERROR_WRITE = -3, ERROR_OVERFLOW = -4 }; private: uint32_t *data; size_t buf_len; double clk_rt; TIMESTAMP time_start; TIMESTAMP time_end; size_t data_start; size_t data_end; }; /* uhd_device - UHD implementation of the Device interface. Timestamped samples are sent to and received from the device. An intermediate buffer on the receive side collects and aligns packets of samples. Events and errors such as underruns are reported asynchronously by the device and received in a separate thread. */ class uhd_device : public RadioDevice { public: uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset, const std::vector& tx_paths, const std::vector& rx_paths); ~uhd_device(); int open(const std::string &args, int ref, bool swap_channels); bool start(); bool stop(); bool restart(); void setPriority(float prio); enum TxWindowType getWindowType() { return tx_window; } int readSamples(std::vector &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun, unsigned *RSSI); int writeSamples(std::vector &bufs, int len, bool *underrun, TIMESTAMP timestamp, bool isControl); bool updateAlignment(TIMESTAMP timestamp); bool setTxFreq(double wFreq, size_t chan); bool setRxFreq(double wFreq, size_t chan); TIMESTAMP initialWriteTimestamp(); TIMESTAMP initialReadTimestamp(); double fullScaleInputValue(); double fullScaleOutputValue(); double setRxGain(double db, size_t chan); double getRxGain(size_t chan); double maxRxGain(void) { return rx_gain_max; } double minRxGain(void) { return rx_gain_min; } double setTxGain(double db, size_t chan); double maxTxGain(void) { return tx_gain_max; } double minTxGain(void) { return tx_gain_min; } double getTxFreq(size_t chan); double getRxFreq(size_t chan); double getRxFreq(); bool setRxAntenna(const std::string &ant, size_t chan); std::string getRxAntenna(size_t chan); bool setTxAntenna(const std::string &ant, size_t chan); std::string getTxAntenna(size_t chan); bool requiresRadioAlign(); GSM::Time minLatency(); inline double getSampleRate() { return tx_rate; } inline double numberRead() { return rx_pkt_cnt; } inline double numberWritten() { return 0; } /** Receive and process asynchronous message @return true if message received or false on timeout or error */ bool recv_async_msg(); enum err_code { ERROR_TIMING = -1, ERROR_TIMEOUT = -2, ERROR_UNRECOVERABLE = -3, ERROR_UNHANDLED = -4, }; private: uhd::usrp::multi_usrp::sptr usrp_dev; uhd::tx_streamer::sptr tx_stream; uhd::rx_streamer::sptr rx_stream; enum TxWindowType tx_window; enum uhd_dev_type dev_type; size_t tx_sps, rx_sps, chans; double tx_rate, rx_rate; double tx_gain_min, tx_gain_max; double rx_gain_min, rx_gain_max; double offset; std::vector tx_gains, rx_gains; std::vector tx_freqs, rx_freqs; std::vector tx_paths, rx_paths; size_t tx_spp, rx_spp; bool started; bool aligned; size_t rx_pkt_cnt; size_t drop_cnt; uhd::time_spec_t prev_ts; TIMESTAMP ts_initial, ts_offset; std::vector rx_buffers; void init_gains(); void set_channels(bool swap); void set_rates(); bool set_antennas(); bool parse_dev_type(); bool flush_recv(size_t num_pkts); int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls); std::string str_code(uhd::rx_metadata_t metadata); std::string str_code(uhd::async_metadata_t metadata); uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx); bool set_freq(double freq, size_t chan, bool tx); Thread *async_event_thrd; InterfaceType iface; Mutex tune_lock; }; void *async_event_loop(uhd_device *dev) { dev->setPriority(0.43); while (1) { dev->recv_async_msg(); pthread_testcancel(); } return NULL; } #ifndef USE_UHD_3_11 /* Catch and drop underrun 'U' and overrun 'O' messages from stdout since we already report using the logging facility. Direct everything else appropriately. */ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg) { switch (type) { case uhd::msg::status: LOG(INFO) << msg; break; case uhd::msg::warning: LOG(WARNING) << msg; break; case uhd::msg::error: LOG(ERR) << msg; break; case uhd::msg::fastpath: break; } } #endif static void thread_enable_cancel(bool cancel) { cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) : pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); } uhd_device::uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double offset, const std::vector& tx_paths, const std::vector& rx_paths) : tx_gain_min(0.0), tx_gain_max(0.0), rx_gain_min(0.0), rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0), prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL) { this->tx_sps = tx_sps; this->rx_sps = rx_sps; this->chans = chans; this->offset = offset; this->iface = iface; this->tx_paths = tx_paths; this->rx_paths = rx_paths; } uhd_device::~uhd_device() { stop(); for (size_t i = 0; i < rx_buffers.size(); i++) delete rx_buffers[i]; } void uhd_device::init_gains() { uhd::gain_range_t range; if (dev_type == UMTRX) { std::vector gain_stages = usrp_dev->get_tx_gain_names(0); if (gain_stages[0] == "VGA") { LOG(WARNING) << "Update your UHD version for a proper Tx gain support"; } if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") { range = usrp_dev->get_tx_gain_range(); tx_gain_min = range.start(); tx_gain_max = range.stop(); } else { range = usrp_dev->get_tx_gain_range("VGA2"); tx_gain_min = UMTRX_VGA1_DEF + range.start(); tx_gain_max = UMTRX_VGA1_DEF + range.stop(); } } else { range = usrp_dev->get_tx_gain_range(); tx_gain_min = range.start(); tx_gain_max = range.stop(); } LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]"; range = usrp_dev->get_rx_gain_range(); rx_gain_min = range.start(); rx_gain_max = range.stop(); LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]"; for (size_t i = 0; i < tx_gains.size(); i++) { double gain = (tx_gain_min + tx_gain_max) / 2; LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain; usrp_dev->set_tx_gain(gain, i); tx_gains[i] = usrp_dev->get_tx_gain(i); } for (size_t i = 0; i < rx_gains.size(); i++) { double gain = (rx_gain_min + rx_gain_max) / 2; LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain; usrp_dev->set_rx_gain(gain, i); rx_gains[i] = usrp_dev->get_rx_gain(i); } return; } void uhd_device::set_rates() { dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)); if (desc.mcr != 0.0) usrp_dev->set_master_clock_rate(desc.mcr); tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate; rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate; usrp_dev->set_tx_rate(tx_rate); usrp_dev->set_rx_rate(rx_rate); tx_rate = usrp_dev->get_tx_rate(); rx_rate = usrp_dev->get_rx_rate(); ts_offset = static_cast(desc.offset * rx_rate); LOG(INFO) << "Rates configured for " << desc.str; } bool uhd_device::set_antennas() { unsigned int i; for (i = 0; i < tx_paths.size(); i++) { if (tx_paths[i] == "") continue; LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i]; if (!setTxAntenna(tx_paths[i], i)) { LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i]; return false; } } for (i = 0; i < rx_paths.size(); i++) { if (rx_paths[i] == "") continue; LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i]; if (!setRxAntenna(rx_paths[i], i)) { LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i]; return false; } } LOG(INFO) << "Antennas configured successfully"; return true; } double uhd_device::setTxGain(double db, size_t chan) { if (iface == MULTI_ARFCN) chan = 0; if (chan >= tx_gains.size()) { LOG(ALERT) << "Requested non-existent channel" << chan; return 0.0f; } if (dev_type == UMTRX) { std::vector gain_stages = usrp_dev->get_tx_gain_names(0); if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") { usrp_dev->set_tx_gain(db, chan); } else { // New UHD versions support split configuration of // Tx gain stages. We utilize this to set the gain // configuration, optimal for the Tx signal quality. // From our measurements, VGA1 must be 18dB plus-minus // one and VGA2 is the best when 23dB or lower. usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan); usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan); } } else { usrp_dev->set_tx_gain(db, chan); } tx_gains[chan] = usrp_dev->get_tx_gain(chan); LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)"; return tx_gains[chan]; } double uhd_device::setRxGain(double db, size_t chan) { if (chan >= rx_gains.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return 0.0f; } usrp_dev->set_rx_gain(db, chan); rx_gains[chan] = usrp_dev->get_rx_gain(chan); LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)"; return rx_gains[chan]; } double uhd_device::getRxGain(size_t chan) { if (iface == MULTI_ARFCN) chan = 0; if (chan >= rx_gains.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return 0.0f; } return rx_gains[chan]; } /* Parse the UHD device tree and mboard name to find out what device we're dealing with. We need the window type so that the transceiver knows how to deal with the transport latency. Reject the USRP1 because UHD doesn't support timestamped samples with it. */ bool uhd_device::parse_dev_type() { uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree(); std::string devString = prop_tree->access("/name").get(); std::string mboardString = usrp_dev->get_mboard_name(); const std::map> devStringMap { { "B100", { B100, TX_WINDOW_USRP1 } }, { "B200", { B200, TX_WINDOW_USRP1 } }, { "B200mini", { B200, TX_WINDOW_USRP1 } }, { "B205mini", { B200, TX_WINDOW_USRP1 } }, { "B210", { B210, TX_WINDOW_USRP1 } }, { "E100", { E1XX, TX_WINDOW_FIXED } }, { "E110", { E1XX, TX_WINDOW_FIXED } }, { "E310", { E3XX, TX_WINDOW_FIXED } }, { "E3XX", { E3XX, TX_WINDOW_FIXED } }, { "X300", { X3XX, TX_WINDOW_FIXED } }, { "X310", { X3XX, TX_WINDOW_FIXED } }, { "USRP2", { USRP2, TX_WINDOW_FIXED } }, { "UmTRX", { UMTRX, TX_WINDOW_FIXED } }, { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } }, }; // Compare UHD motherboard and device strings */ auto mapIter = devStringMap.begin(); while (mapIter != devStringMap.end()) { if (devString.find(mapIter->first) != std::string::npos || mboardString.find(mapIter->first) != std::string::npos) { dev_type = std::get<0>(mapIter->second); tx_window = std::get<1>(mapIter->second); return true; } mapIter++; } LOG(ALERT) << "Unsupported device " << devString; return false; } /* * Check for UHD version > 3.9.0 for E3XX support */ static bool uhd_e3xx_version_chk() { std::string ver = uhd::get_version_string(); std::string major_str(ver.begin(), ver.begin() + 3); std::string minor_str(ver.begin() + 4, ver.begin() + 7); int major_val = atoi(major_str.c_str()); int minor_val = atoi(minor_str.c_str()); if (major_val < 3) return false; if (minor_val < 9) return false; return true; } void uhd_device::set_channels(bool swap) { if (iface == MULTI_ARFCN) { if (dev_type != B200 && dev_type != B210) throw std::invalid_argument("Device does not support MCBTS"); dev_type = B2XX_MCBTS; chans = 1; } if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels) throw std::invalid_argument("Device does not support number of requested channels"); std::string subdev_string; switch (dev_type) { case B210: case E3XX: if (chans == 1) subdev_string = swap ? "A:B" : "A:A"; else if (chans == 2) subdev_string = swap ? "A:B A:A" : "A:A A:B"; break; case X3XX: case UMTRX: if (chans == 1) subdev_string = swap ? "B:0" : "A:0"; else if (chans == 2) subdev_string = swap ? "B:0 A:0" : "A:0 B:0"; break; default: break; } if (!subdev_string.empty()) { uhd::usrp::subdev_spec_t spec(subdev_string); usrp_dev->set_tx_subdev_spec(spec); usrp_dev->set_rx_subdev_spec(spec); } } int uhd_device::open(const std::string &args, int ref, bool swap_channels) { const char *refstr; // Find UHD devices uhd::device_addr_t addr(args); uhd::device_addrs_t dev_addrs = uhd::device::find(addr); if (dev_addrs.size() == 0) { LOG(ALERT) << "No UHD devices found with address '" << args << "'"; return -1; } // Use the first found device LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string(); try { usrp_dev = uhd::usrp::multi_usrp::make(addr); } catch(...) { LOG(ALERT) << "UHD make failed, device " << args; return -1; } // Check for a valid device type and set bus type if (!parse_dev_type()) return -1; if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) { LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater"; return -1; } try { set_channels(swap_channels); } catch (const std::exception &e) { LOG(ALERT) << "Channel setting failed - " << e.what(); return -1; } if (!set_antennas()) { LOG(ALERT) << "UHD antenna setting failed"; return -1; } tx_freqs.resize(chans); rx_freqs.resize(chans); tx_gains.resize(chans); rx_gains.resize(chans); rx_buffers.resize(chans); switch (ref) { case REF_INTERNAL: refstr = "internal"; break; case REF_EXTERNAL: refstr = "external"; break; case REF_GPS: refstr = "gpsdo"; break; default: LOG(ALERT) << "Invalid reference type"; return -1; } usrp_dev->set_clock_source(refstr); try { set_rates(); } catch (const std::exception &e) { LOG(ALERT) << "UHD rate setting failed - " << e.what(); return -1; } // Set RF frontend bandwidth if (dev_type == UMTRX) { // Setting LMS6002D LPF to 500kHz gives us the best signal quality for (size_t i = 0; i < chans; i++) { usrp_dev->set_tx_bandwidth(500*1000*2, i); usrp_dev->set_rx_bandwidth(500*1000*2, i); } } else if (dev_type == LIMESDR) { for (size_t i = 0; i < chans; i++) { usrp_dev->set_tx_bandwidth(5e6, i); usrp_dev->set_rx_bandwidth(5e6, i); } } /* Create TX and RX streamers */ uhd::stream_args_t stream_args("sc16"); for (size_t i = 0; i < chans; i++) stream_args.channels.push_back(i); tx_stream = usrp_dev->get_tx_stream(stream_args); rx_stream = usrp_dev->get_rx_stream(stream_args); /* Number of samples per over-the-wire packet */ tx_spp = tx_stream->get_max_num_samps(); rx_spp = rx_stream->get_max_num_samps(); // Create receive buffer size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t); for (size_t i = 0; i < rx_buffers.size(); i++) rx_buffers[i] = new smpl_buf(buf_len, rx_rate); // Initialize and shadow gain values init_gains(); // Print configuration LOG(INFO) << "\n" << usrp_dev->get_pp_string(); if (iface == MULTI_ARFCN) return MULTI_ARFCN; switch (dev_type) { case B100: return RESAMP_64M; case USRP2: case X3XX: return RESAMP_100M; case B200: case B210: case E1XX: case E3XX: case LIMESDR: default: break; } return NORMAL; } bool uhd_device::flush_recv(size_t num_pkts) { uhd::rx_metadata_t md; size_t num_smpls; float timeout = UHD_RESTART_TIMEOUT; std::vector > pkt_bufs(chans, std::vector(2 * rx_spp)); std::vector pkt_ptrs; for (size_t i = 0; i < pkt_bufs.size(); i++) pkt_ptrs.push_back(&pkt_bufs[i].front()); ts_initial = 0; while (!ts_initial || (num_pkts-- > 0)) { num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md, timeout, true); if (!num_smpls) { switch (md.error_code) { case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: LOG(ALERT) << "Device timed out"; return false; default: continue; } } ts_initial = md.time_spec.to_ticks(rx_rate); } LOG(INFO) << "Initial timestamp " << ts_initial << std::endl; return true; } bool uhd_device::restart() { /* Allow 100 ms delay to align multi-channel streams */ double delay = 0.1; aligned = false; uhd::time_spec_t current = usrp_dev->get_time_now(); uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; cmd.stream_now = false; cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay); usrp_dev->issue_stream_cmd(cmd); return flush_recv(10); } bool uhd_device::start() { LOG(INFO) << "Starting USRP..."; if (started) { LOG(ERR) << "Device already started"; return false; } #ifndef USE_UHD_3_11 // Register msg handler uhd::msg::register_handler(&uhd_msg_handler); #endif // Start asynchronous event (underrun check) loop async_event_thrd = new Thread(); async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this); // Start streaming if (!restart()) return false; // Display usrp time double time_now = usrp_dev->get_time_now().get_real_secs(); LOG(INFO) << "The current time is " << time_now << " seconds"; started = true; return true; } bool uhd_device::stop() { if (!started) return false; uhd::stream_cmd_t stream_cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS; usrp_dev->issue_stream_cmd(stream_cmd); async_event_thrd->cancel(); async_event_thrd->join(); delete async_event_thrd; started = false; return true; } void uhd_device::setPriority(float prio) { uhd::set_thread_priority_safe(prio); return; } int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls) { if (!num_smpls) { LOG(ERR) << str_code(md); switch (md.error_code) { case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: LOG(ALERT) << "UHD: Receive timed out"; return ERROR_TIMEOUT; case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND: case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN: case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET: default: return ERROR_UNHANDLED; } } // Missing timestamp if (!md.has_time_spec) { LOG(ALERT) << "UHD: Received packet missing timestamp"; return ERROR_UNRECOVERABLE; } // Monotonicity check if (md.time_spec < prev_ts) { LOG(ALERT) << "UHD: Loss of monotonic time"; LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", " << "Previous time: " << prev_ts.get_real_secs(); return ERROR_TIMING; } // Workaround for UHD tick rounding bug TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate); if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1) md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate); prev_ts = md.time_spec; return 0; } int uhd_device::readSamples(std::vector &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun, unsigned *RSSI) { ssize_t rc; uhd::time_spec_t ts; uhd::rx_metadata_t metadata; if (bufs.size() != chans) { LOG(ALERT) << "Invalid channel combination " << bufs.size(); return -1; } *overrun = false; *underrun = false; // Shift read time with respect to transmit clock timestamp += ts_offset; ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate); LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs(); // Check that timestamp is valid rc = rx_buffers[0]->avail_smpls(timestamp); if (rc < 0) { LOG(ERR) << rx_buffers[0]->str_code(rc); LOG(ERR) << rx_buffers[0]->str_status(timestamp); return 0; } // Create vector buffer std::vector > pkt_bufs(chans, std::vector(2 * rx_spp)); std::vector pkt_ptrs; for (size_t i = 0; i < pkt_bufs.size(); i++) pkt_ptrs.push_back(&pkt_bufs[i].front()); // Receive samples from the usrp until we have enough while (rx_buffers[0]->avail_smpls(timestamp) < len) { thread_enable_cancel(false); size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, metadata, 0.1, true); thread_enable_cancel(true); rx_pkt_cnt++; // Check for errors rc = check_rx_md_err(metadata, num_smpls); switch (rc) { case ERROR_UNRECOVERABLE: LOG(ALERT) << "UHD: Version " << uhd::get_version_string(); LOG(ALERT) << "UHD: Unrecoverable error, exiting..."; exit(-1); case ERROR_TIMEOUT: // Assume stopping condition return 0; case ERROR_TIMING: restart(); case ERROR_UNHANDLED: continue; } ts = metadata.time_spec; LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs(); for (size_t i = 0; i < rx_buffers.size(); i++) { rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(), num_smpls, metadata.time_spec); // Continue on local overrun, exit on other errors if ((rc < 0)) { LOG(ERR) << rx_buffers[i]->str_code(rc); LOG(ERR) << rx_buffers[i]->str_status(timestamp); if (rc != smpl_buf::ERROR_OVERFLOW) return 0; } } } // We have enough samples for (size_t i = 0; i < rx_buffers.size(); i++) { rc = rx_buffers[i]->read(bufs[i], len, timestamp); if ((rc < 0) || (rc != len)) { LOG(ERR) << rx_buffers[i]->str_code(rc); LOG(ERR) << rx_buffers[i]->str_status(timestamp); return 0; } } return len; } int uhd_device::writeSamples(std::vector &bufs, int len, bool *underrun, unsigned long long timestamp,bool isControl) { uhd::tx_metadata_t metadata; metadata.has_time_spec = true; metadata.start_of_burst = false; metadata.end_of_burst = false; metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate); *underrun = false; // No control packets if (isControl) { LOG(ERR) << "Control packets not supported"; return 0; } if (bufs.size() != chans) { LOG(ALERT) << "Invalid channel combination " << bufs.size(); return -1; } // Drop a fixed number of packets (magic value) if (!aligned) { drop_cnt++; if (drop_cnt == 1) { LOG(DEBUG) << "Aligning transmitter: stop burst"; *underrun = true; metadata.end_of_burst = true; } else if (drop_cnt < 30) { LOG(DEBUG) << "Aligning transmitter: packet advance"; return len; } else { LOG(DEBUG) << "Aligning transmitter: start burst"; metadata.start_of_burst = true; aligned = true; drop_cnt = 0; } } thread_enable_cancel(false); size_t num_smpls = tx_stream->send(bufs, len, metadata); thread_enable_cancel(true); if (num_smpls != (unsigned) len) { LOG(ALERT) << "UHD: Device send timed out"; } return num_smpls; } bool uhd_device::updateAlignment(TIMESTAMP timestamp) { return true; } uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) { double rf_spread, rf_freq; std::vector freqs; uhd::tune_request_t treq(freq); if (dev_type == UMTRX) { if (offset != 0.0) return uhd::tune_request_t(freq, offset); // Don't use DSP tuning, because LMS6002D PLL steps are small enough. // We end up with DSP tuning just for 2-3Hz, which is meaningless and // only distort the signal (because cordic is not ideal). treq.target_freq = freq; treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL; treq.rf_freq = freq; treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL; treq.dsp_freq = 0.0; return treq; } else if (chans == 1) { if (offset == 0.0) return treq; return uhd::tune_request_t(freq, offset); } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) { LOG(ALERT) << chans << " channels unsupported"; return treq; } if (tx) freqs = tx_freqs; else freqs = rx_freqs; /* Tune directly if other channel isn't tuned */ if (freqs[!chan] < 10.0) return treq; /* Find center frequency between channels */ rf_spread = fabs(freqs[!chan] - freq); if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) { LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n"; return treq; } rf_freq = (freqs[!chan] + freq) / 2.0f; treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL; treq.target_freq = freq; treq.rf_freq = rf_freq; return treq; } bool uhd_device::set_freq(double freq, size_t chan, bool tx) { std::vector freqs; uhd::tune_result_t tres; uhd::tune_request_t treq = select_freq(freq, chan, tx); if (tx) { tres = usrp_dev->set_tx_freq(treq, chan); tx_freqs[chan] = usrp_dev->get_tx_freq(chan); } else { tres = usrp_dev->set_rx_freq(treq, chan); rx_freqs[chan] = usrp_dev->get_rx_freq(chan); } LOG(INFO) << "\n" << tres.to_pp_string() << std::endl; if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) return true; /* Manual RF policy means we intentionally tuned with a baseband * offset for dual-channel purposes. Now retune the other channel * with the opposite corresponding frequency offset */ if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) { if (tx) { treq = select_freq(tx_freqs[!chan], !chan, true); tres = usrp_dev->set_tx_freq(treq, !chan); tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan); } else { treq = select_freq(rx_freqs[!chan], !chan, false); tres = usrp_dev->set_rx_freq(treq, !chan); rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan); } LOG(INFO) << "\n" << tres.to_pp_string() << std::endl; } return true; } bool uhd_device::setTxFreq(double wFreq, size_t chan) { if (chan >= tx_freqs.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return false; } ScopedLock lock(tune_lock); return set_freq(wFreq, chan, true); } bool uhd_device::setRxFreq(double wFreq, size_t chan) { if (chan >= rx_freqs.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return false; } ScopedLock lock(tune_lock); return set_freq(wFreq, chan, false); } double uhd_device::getTxFreq(size_t chan) { if (chan >= tx_freqs.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return 0.0; } return tx_freqs[chan]; } double uhd_device::getRxFreq(size_t chan) { if (chan >= rx_freqs.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return 0.0; } return rx_freqs[chan]; } bool uhd_device::setRxAntenna(const std::string &ant, size_t chan) { std::vector avail; if (chan >= rx_paths.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return false; } avail = usrp_dev->get_rx_antennas(chan); if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan; LOG(INFO) << "Available Rx antennas: "; for (std::vector::const_iterator i = avail.begin(); i != avail.end(); ++i) LOG(INFO) << "- '" << *i << "'"; return false; } usrp_dev->set_rx_antenna(ant, chan); rx_paths[chan] = usrp_dev->get_rx_antenna(chan); if (ant != rx_paths[chan]) { LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan]; return false; } return true; } std::string uhd_device::getRxAntenna(size_t chan) { if (chan >= rx_paths.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return ""; } return usrp_dev->get_rx_antenna(chan); } bool uhd_device::setTxAntenna(const std::string &ant, size_t chan) { std::vector avail; if (chan >= tx_paths.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return false; } avail = usrp_dev->get_tx_antennas(chan); if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan; LOG(INFO) << "Available Tx antennas: "; for (std::vector::const_iterator i = avail.begin(); i != avail.end(); ++i) LOG(INFO) << "- '" << *i << "'"; return false; } usrp_dev->set_tx_antenna(ant, chan); tx_paths[chan] = usrp_dev->get_tx_antenna(chan); if (ant != tx_paths[chan]) { LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan]; return false; } return true; } std::string uhd_device::getTxAntenna(size_t chan) { if (chan >= tx_paths.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return ""; } return usrp_dev->get_tx_antenna(chan); } bool uhd_device::requiresRadioAlign() { return false; } GSM::Time uhd_device::minLatency() { /* Empirical data from a handful of relatively recent machines shows that the B100 will underrun when the transmit threshold is reduced to a time of 6 and a half frames, so we set a minimum 7 frame threshold. */ return GSM::Time(6,7); } /* * Only allow sampling the Rx path lower than Tx and not vice-versa. * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed * combination. */ TIMESTAMP uhd_device::initialWriteTimestamp() { if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps)) return ts_initial; else return ts_initial * tx_sps; } TIMESTAMP uhd_device::initialReadTimestamp() { return ts_initial; } double uhd_device::fullScaleInputValue() { if (dev_type == LIMESDR) return (double) SHRT_MAX * LIMESDR_TX_AMPL; if (dev_type == UMTRX) return (double) SHRT_MAX * UMTRX_TX_AMPL; else return (double) SHRT_MAX * USRP_TX_AMPL; } double uhd_device::fullScaleOutputValue() { return (double) SHRT_MAX; } bool uhd_device::recv_async_msg() { uhd::async_metadata_t md; thread_enable_cancel(false); bool rc = usrp_dev->get_device()->recv_async_msg(md); thread_enable_cancel(true); if (!rc) return false; // Assume that any error requires resynchronization if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) { aligned = false; if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) && (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) { LOG(ERR) << str_code(md); } } return true; } std::string uhd_device::str_code(uhd::rx_metadata_t metadata) { std::ostringstream ost("UHD: "); switch (metadata.error_code) { case uhd::rx_metadata_t::ERROR_CODE_NONE: ost << "No error"; break; case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: ost << "No packet received, implementation timed-out"; break; case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND: ost << "A stream command was issued in the past"; break; case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN: ost << "Expected another stream command"; break; case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: ost << "An internal receive buffer has filled"; break; case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT: ost << "Multi-channel alignment failed"; break; case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET: ost << "The packet could not be parsed"; break; default: ost << "Unknown error " << metadata.error_code; } if (metadata.has_time_spec) ost << " at " << metadata.time_spec.get_real_secs() << " sec."; return ost.str(); } std::string uhd_device::str_code(uhd::async_metadata_t metadata) { std::ostringstream ost("UHD: "); switch (metadata.event_code) { case uhd::async_metadata_t::EVENT_CODE_BURST_ACK: ost << "A packet was successfully transmitted"; break; case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW: ost << "An internal send buffer has emptied"; break; case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR: ost << "Packet loss between host and device"; break; case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR: ost << "Packet time was too late or too early"; break; case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET: ost << "Underflow occurred inside a packet"; break; case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST: ost << "Packet loss within a burst"; break; default: ost << "Unknown error " << metadata.event_code; } if (metadata.has_time_spec) ost << " at " << metadata.time_spec.get_real_secs() << " sec."; return ost.str(); } smpl_buf::smpl_buf(size_t len, double rate) : buf_len(len), clk_rt(rate), time_start(0), time_end(0), data_start(0), data_end(0) { data = new uint32_t[len]; } smpl_buf::~smpl_buf() { delete[] data; } ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const { if (timestamp < time_start) return ERROR_TIMESTAMP; else if (timestamp >= time_end) return 0; else return time_end - timestamp; } ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const { return avail_smpls(timespec.to_ticks(clk_rt)); } ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp) { int type_sz = 2 * sizeof(short); // Check for valid read if (timestamp < time_start) return ERROR_TIMESTAMP; if (timestamp >= time_end) return 0; if (len >= buf_len) return ERROR_READ; // How many samples should be copied size_t num_smpls = time_end - timestamp; if (num_smpls > len) num_smpls = len; // Starting index size_t read_start = (data_start + (timestamp - time_start)) % buf_len; // Read it if (read_start + num_smpls < buf_len) { size_t numBytes = len * type_sz; memcpy(buf, data + read_start, numBytes); } else { size_t first_cp = (buf_len - read_start) * type_sz; size_t second_cp = len * type_sz - first_cp; memcpy(buf, data + read_start, first_cp); memcpy((char*) buf + first_cp, data, second_cp); } data_start = (read_start + len) % buf_len; time_start = timestamp + len; if (time_start > time_end) return ERROR_READ; else return num_smpls; } ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts) { return read(buf, len, ts.to_ticks(clk_rt)); } ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp) { int type_sz = 2 * sizeof(short); // Check for valid write if ((len == 0) || (len >= buf_len)) return ERROR_WRITE; if ((timestamp + len) <= time_end) return ERROR_TIMESTAMP; if (timestamp < time_end) { LOG(ERR) << "Overwriting old buffer data: timestamp="<& tx_paths, const std::vector& rx_paths) { return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths); } osmo-trx-0.4.0/Transceiver52M/device/uhd/Makefile.am0000644000175000017500000000042613272615542021150 0ustar rubenrubeninclude $(top_srcdir)/Makefile.common AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS) noinst_LTLIBRARIES = libdevice.la libdevice_la_SOURCES = UHDDevice.cpp osmo-trx-0.4.0/Transceiver52M/device/usrp1/0000755000175000017500000000000013272615542017404 5ustar rubenrubenosmo-trx-0.4.0/Transceiver52M/device/usrp1/Makefile.am0000644000175000017500000000046713272615542021447 0ustar rubenrubeninclude $(top_srcdir)/Makefile.common AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS) noinst_HEADERS = USRPDevice.h noinst_LTLIBRARIES = libdevice.la libdevice_la_SOURCES = USRPDevice.cpp osmo-trx-0.4.0/Transceiver52M/device/usrp1/USRPDevice.cpp0000644000175000017500000004150413272615542022025 0ustar rubenruben/* * Copyright 2008, 2009 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /* Compilation Flags SWLOOPBACK compile for software loopback testing */ #include #include #include #include "Logger.h" #include "Threads.h" #include "USRPDevice.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif using namespace std; enum dboardConfigType { TXA_RXB, TXB_RXA, TXA_RXA, TXB_RXB }; #ifdef SINGLEDB const dboardConfigType dboardConfig = TXA_RXA; #else const dboardConfigType dboardConfig = TXA_RXB; #endif const double USRPDevice::masterClockRate = 52.0e6; USRPDevice::USRPDevice(size_t sps) { LOG(INFO) << "creating USRP device..."; this->sps = sps; decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps)); actualSampleRate = masterClockRate/decimRate; rxGain = 0; /* * Undetermined delay b/w ping response timestamp and true * receive timestamp. Values are empirically measured. With * split sample rate Tx/Rx - 4/1 sps we need to need to * compensate for advance rather than delay. */ if (sps == 1) pingOffset = 272; else if (sps == 4) pingOffset = 269 - 7500; else pingOffset = 0; #ifdef SWLOOPBACK samplePeriod = 1.0e6/actualSampleRate; loopbackBufferSize = 0; gettimeofday(&lastReadTime,NULL); firstRead = false; #endif } int USRPDevice::open(const std::string &, int, bool) { writeLock.unlock(); LOG(INFO) << "opening USRP device.."; #ifndef SWLOOPBACK string rbf = "std_inband.rbf"; //string rbf = "inband_1rxhb_1tx.rbf"; m_uRx.reset(); if (!skipRx) { try { m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make( 0, decimRate * sps, 1, -1, usrp_standard_rx::FPGA_MODE_NORMAL, 1024, 16 * 8, rbf)); m_uRx->set_fpga_master_clock_freq(masterClockRate); } catch(...) { LOG(ALERT) << "make failed on Rx"; m_uRx.reset(); return -1; } if (m_uRx->fpga_master_clock_freq() != masterClockRate) { LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() << ", desired clock freq = " << masterClockRate; m_uRx.reset(); return -1; } } try { m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make( 0, decimRate * 2, 1, -1, 1024, 16 * 8, rbf)); m_uTx->set_fpga_master_clock_freq(masterClockRate); } catch(...) { LOG(ALERT) << "make failed on Tx"; m_uTx.reset(); return -1; } if (m_uTx->fpga_master_clock_freq() != masterClockRate) { LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() << ", desired clock freq = " << masterClockRate; m_uTx.reset(); return -1; } if (!skipRx) m_uRx->stop(); m_uTx->stop(); #endif switch (dboardConfig) { case TXA_RXB: txSubdevSpec = usrp_subdev_spec(0,0); rxSubdevSpec = usrp_subdev_spec(1,0); break; case TXB_RXA: txSubdevSpec = usrp_subdev_spec(1,0); rxSubdevSpec = usrp_subdev_spec(0,0); break; case TXA_RXA: txSubdevSpec = usrp_subdev_spec(0,0); rxSubdevSpec = usrp_subdev_spec(0,0); break; case TXB_RXB: txSubdevSpec = usrp_subdev_spec(1,0); rxSubdevSpec = usrp_subdev_spec(1,0); break; default: txSubdevSpec = usrp_subdev_spec(0,0); rxSubdevSpec = usrp_subdev_spec(1,0); } m_dbTx = m_uTx->selected_subdev(txSubdevSpec); m_dbRx = m_uRx->selected_subdev(rxSubdevSpec); samplesRead = 0; samplesWritten = 0; started = false; return NORMAL; } bool USRPDevice::start() { LOG(INFO) << "starting USRP..."; #ifndef SWLOOPBACK if (!m_uRx && !skipRx) return false; if (!m_uTx) return false; if (!skipRx) m_uRx->stop(); m_uTx->stop(); writeLock.lock(); // power up and configure daughterboards m_dbTx->set_enable(true); m_uTx->set_mux(m_uTx->determine_tx_mux_value(txSubdevSpec)); m_uRx->set_mux(m_uRx->determine_rx_mux_value(rxSubdevSpec)); if (!m_dbRx->select_rx_antenna(1)) m_dbRx->select_rx_antenna(0); writeLock.unlock(); // Set gains to midpoint setTxGain((minTxGain() + maxTxGain()) / 2); setRxGain((minRxGain() + maxRxGain()) / 2); data = new short[currDataSize]; dataStart = 0; dataEnd = 0; timeStart = 0; timeEnd = 0; timestampOffset = 0; latestWriteTimestamp = 0; lastPktTimestamp = 0; hi32Timestamp = 0; isAligned = false; if (!skipRx) started = (m_uRx->start() && m_uTx->start()); else started = m_uTx->start(); return started; #else gettimeofday(&lastReadTime,NULL); return true; #endif } bool USRPDevice::stop() { #ifndef SWLOOPBACK if (!m_uRx) return false; if (!m_uTx) return false; delete[] currData; started = !(m_uRx->stop() && m_uTx->stop()); return !started; #else return true; #endif } double USRPDevice::maxTxGain() { return m_dbTx->gain_max(); } double USRPDevice::minTxGain() { return m_dbTx->gain_min(); } double USRPDevice::maxRxGain() { return m_dbRx->gain_max(); } double USRPDevice::minRxGain() { return m_dbRx->gain_min(); } double USRPDevice::setTxGain(double dB, size_t chan) { if (chan) { LOG(ALERT) << "Invalid channel " << chan; return 0.0; } writeLock.lock(); if (dB > maxTxGain()) dB = maxTxGain(); if (dB < minTxGain()) dB = minTxGain(); LOG(NOTICE) << "Setting TX gain to " << dB << " dB."; if (!m_dbTx->set_gain(dB)) LOG(ERR) << "Error setting TX gain"; writeLock.unlock(); return dB; } double USRPDevice::setRxGain(double dB, size_t chan) { if (chan) { LOG(ALERT) << "Invalid channel " << chan; return 0.0; } dB = 47.0; writeLock.lock(); if (dB > maxRxGain()) dB = maxRxGain(); if (dB < minRxGain()) dB = minRxGain(); LOG(NOTICE) << "Setting RX gain to " << dB << " dB."; if (!m_dbRx->set_gain(dB)) LOG(ERR) << "Error setting RX gain"; writeLock.unlock(); return dB; } bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan) { if (chan >= rx_paths.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return false; } LOG(ALERT) << "Not implemented"; return true; } std::string USRPDevice::getRxAntenna(size_t chan) { if (chan >= rx_paths.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return ""; } LOG(ALERT) << "Not implemented"; return ""; } bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan) { if (chan >= tx_paths.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return false; } LOG(ALERT) << "Not implemented"; return true; } std::string USRPDevice::getTxAntenna(size_t chan) { if (chan >= tx_paths.size()) { LOG(ALERT) << "Requested non-existent channel " << chan; return ""; } LOG(ALERT) << "Not implemented"; return ""; } bool USRPDevice::requiresRadioAlign() { return true; } GSM::Time USRPDevice::minLatency() { return GSM::Time(1,1); } // NOTE: Assumes sequential reads int USRPDevice::readSamples(std::vector &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun, unsigned *RSSI) { #ifndef SWLOOPBACK if (!m_uRx) return 0; short *buf = bufs[0]; timestamp += timestampOffset; if (timestamp + len < timeStart) { memset(buf,0,len*2*sizeof(short)); return len; } if (underrun) *underrun = false; uint32_t readBuf[2000]; while (1) { //guestimate USB read size int readLen=0; { int numSamplesNeeded = timestamp + len - timeEnd; if (numSamplesNeeded <=0) break; readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0)); if (readLen > 8000) readLen= (8000/512)*512; } // read USRP packets, parse and save A/D data as needed readLen = m_uRx->read((void *)readBuf,readLen,overrun); for(int pktNum = 0; pktNum < (readLen/512); pktNum++) { // tmpBuf points to start of a USB packet uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4); TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]); uint32_t word0 = usrp_to_host_u32(tmpBuf[0]); uint32_t chan = (word0 >> 16) & 0x1f; unsigned payloadSz = word0 & 0x1ff; LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp); if (incrementHi32 && (timeStart!=0)) { LOG(DEBUG) << "high 32 increment!!!"; hi32Timestamp++; } pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp; lastPktTimestamp = pktTimestamp; if (chan == 0x01f) { // control reply, check to see if its ping reply uint32_t word2 = usrp_to_host_u32(tmpBuf[2]); if ((word2 >> 16) == ((0x01 << 8) | 0x02)) { timestamp -= timestampOffset; timestampOffset = pktTimestamp - pingTimestamp + pingOffset; LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset; timestamp += timestampOffset; isAligned = true; } continue; } if (chan != 0) { LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; continue; } if ((word0 >> 28) & 0x04) { if (underrun) *underrun = true; LOG(DEBUG) << "UNDERRUN in TRX->USRP interface"; } if (RSSI) *RSSI = (word0 >> 21) & 0x3f; if (!isAligned) continue; unsigned cursorStart = pktTimestamp - timeStart + dataStart; while (cursorStart*2 > currDataSize) { cursorStart -= currDataSize/2; } if (cursorStart*2 + payloadSz/2 > currDataSize) { // need to circle around buffer memcpy(data+cursorStart*2,tmpBuf+2,(currDataSize-cursorStart*2)*sizeof(short)); memcpy(data,tmpBuf+2+(currDataSize/2-cursorStart),payloadSz-(currDataSize-cursorStart*2)*sizeof(short)); } else { memcpy(data+cursorStart*2,tmpBuf+2,payloadSz); } if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd) timeEnd = pktTimestamp+payloadSz/2/sizeof(short); LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; } } // copy desired data to buf unsigned bufStart = dataStart+(timestamp-timeStart); if (bufStart + len < currDataSize/2) { LOG(DEBUG) << "bufStart: " << bufStart; memcpy(buf,data+bufStart*2,len*2*sizeof(short)); memset(data+bufStart*2,0,len*2*sizeof(short)); } else { LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; unsigned firstLength = (currDataSize/2-bufStart); LOG(DEBUG) << "firstLength: " << firstLength; memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short)); memset(data+bufStart*2,0,firstLength*2*sizeof(short)); memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short)); memset(data,0,(len-firstLength)*2*sizeof(short)); } dataStart = (bufStart + len) % (currDataSize/2); timeStart = timestamp + len; return len; #else if (loopbackBufferSize < 2) return 0; int numSamples = 0; struct timeval currTime; gettimeofday(&currTime,NULL); double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 + (currTime.tv_usec - lastReadTime.tv_usec); if (timeElapsed < samplePeriod) {return 0;} int numSamplesToRead = (int) floor(timeElapsed/samplePeriod); if (numSamplesToRead < len) return 0; if (numSamplesToRead > len) numSamplesToRead = len; if (numSamplesToRead > loopbackBufferSize/2) { firstRead =false; numSamplesToRead = loopbackBufferSize/2; } memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead); loopbackBufferSize -= 2*numSamplesToRead; memcpy(loopbackBuffer,loopbackBuffer+2*numSamplesToRead, sizeof(short)*loopbackBufferSize); numSamples = numSamplesToRead; if (firstRead) { int new_usec = lastReadTime.tv_usec + (int) round((double) numSamplesToRead * samplePeriod); lastReadTime.tv_sec = lastReadTime.tv_sec + new_usec/1000000; lastReadTime.tv_usec = new_usec % 1000000; } else { gettimeofday(&lastReadTime,NULL); firstRead = true; } samplesRead += numSamples; return numSamples; #endif } int USRPDevice::writeSamples(std::vector &bufs, int len, bool *underrun, unsigned long long timestamp, bool isControl) { writeLock.lock(); #ifndef SWLOOPBACK if (!m_uTx) return 0; short *buf = bufs[0]; static uint32_t outData[128*20]; for (int i = 0; i < len*2; i++) { buf[i] = host_to_usrp_short(buf[i]); } int numWritten = 0; unsigned isStart = 1; unsigned RSSI = 0; unsigned CHAN = (isControl) ? 0x01f : 0x00; len = len*2*sizeof(short); int numPkts = (int) ceil((float)len/(float)504); unsigned isEnd = (numPkts < 2); uint32_t *outPkt = outData; int pktNum = 0; while (numWritten < len) { // pkt is pointer to start of a USB packet uint32_t *pkt = outPkt + pktNum*128; isEnd = (len - numWritten <= 504); unsigned payloadLen = ((len - numWritten) < 504) ? (len-numWritten) : 504; pkt[0] = (isStart << 12 | isEnd << 11 | (RSSI & 0x3f) << 5 | CHAN) << 16 | payloadLen; pkt[1] = timestamp & 0x0ffffffffll; memcpy(pkt+2,buf+(numWritten/sizeof(short)),payloadLen); numWritten += payloadLen; timestamp += payloadLen/2/sizeof(short); isStart = 0; pkt[0] = host_to_usrp_u32(pkt[0]); pkt[1] = host_to_usrp_u32(pkt[1]); pktNum++; } m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL); samplesWritten += len/2/sizeof(short); writeLock.unlock(); return len/2/sizeof(short); #else int retVal = len; memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len); samplesWritten += retVal; loopbackBufferSize += retVal*2; return retVal; #endif } bool USRPDevice::updateAlignment(TIMESTAMP timestamp) { #ifndef SWLOOPBACK short data[] = {0x00,0x02,0x00,0x00}; uint32_t *wordPtr = (uint32_t *) data; *wordPtr = host_to_usrp_u32(*wordPtr); bool tmpUnderrun; std::vector buf(1, data); if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) { pingTimestamp = timestamp; return true; } return false; #else return true; #endif } #ifndef SWLOOPBACK bool USRPDevice::setTxFreq(double wFreq, size_t chan) { usrp_tune_result result; if (chan) { LOG(ALERT) << "Invalid channel " << chan; return false; } if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) { LOG(INFO) << "set TX: " << wFreq << std::endl << " baseband freq: " << result.baseband_freq << std::endl << " DDC freq: " << result.dxc_freq << std::endl << " residual freq: " << result.residual_freq; return true; } else { LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl << " baseband freq: " << result.baseband_freq << std::endl << " DDC freq: " << result.dxc_freq << std::endl << " residual freq: " << result.residual_freq; return false; } } bool USRPDevice::setRxFreq(double wFreq, size_t chan) { usrp_tune_result result; if (chan) { LOG(ALERT) << "Invalid channel " << chan; return false; } if (m_uRx->tune(0, m_dbRx, wFreq, &result)) { LOG(INFO) << "set RX: " << wFreq << std::endl << " baseband freq: " << result.baseband_freq << std::endl << " DDC freq: " << result.dxc_freq << std::endl << " residual freq: " << result.residual_freq; return true; } else { LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl << " baseband freq: " << result.baseband_freq << std::endl << " DDC freq: " << result.dxc_freq << std::endl << " residual freq: " << result.residual_freq; return false; } } #else bool USRPDevice::setTxFreq(double wFreq) { return true;}; bool USRPDevice::setRxFreq(double wFreq) { return true;}; #endif RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double offset, const std::vector& tx_paths, const std::vector& rx_paths) { return new USRPDevice(tx_sps); } osmo-trx-0.4.0/Transceiver52M/device/usrp1/USRPDevice.h0000644000175000017500000001531613272615542021474 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef _USRP_DEVICE_H_ #define _USRP_DEVICE_H_ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "radioDevice.h" #include #include #include #include #include #include #include #include typedef boost::shared_ptr usrp_standard_tx_sptr; typedef boost::shared_ptr usrp_standard_rx_sptr; /** A class to handle a USRP rev 4, with a two RFX900 daughterboards */ class USRPDevice: public RadioDevice { private: static const double masterClockRate; ///< the USRP clock rate double desiredSampleRate; ///< the desired sampling rate usrp_standard_rx_sptr m_uRx; ///< the USRP receiver usrp_standard_tx_sptr m_uTx; ///< the USRP transmitter db_base_sptr m_dbRx; ///< rx daughterboard db_base_sptr m_dbTx; ///< tx daughterboard usrp_subdev_spec rxSubdevSpec; usrp_subdev_spec txSubdevSpec; int sps; double actualSampleRate; ///< the actual USRP sampling rate unsigned int decimRate; ///< the USRP decimation rate unsigned long long samplesRead; ///< number of samples read from USRP unsigned long long samplesWritten; ///< number of samples sent to USRP bool started; ///< flag indicates USRP has started bool skipRx; ///< set if USRP is transmit-only. static const unsigned int currDataSize_log2 = 21; static const unsigned long currDataSize = (1 << currDataSize_log2); short *data; unsigned long dataStart; unsigned long dataEnd; TIMESTAMP timeStart; TIMESTAMP timeEnd; bool isAligned; Mutex writeLock; short *currData; ///< internal data buffer when reading from USRP TIMESTAMP currTimestamp; ///< timestamp of internal data buffer unsigned currLen; ///< size of internal data buffer TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response long long pingOffset; unsigned long hi32Timestamp; unsigned long lastPktTimestamp; double rxGain; #ifdef SWLOOPBACK short loopbackBuffer[1000000]; int loopbackBufferSize; double samplePeriod; struct timeval startTime; struct timeval lastReadTime; bool firstRead; #endif public: /** Object constructor */ USRPDevice(size_t sps); /** Instantiate the USRP */ int open(const std::string &, int, bool); /** Start the USRP */ bool start(); /** Stop the USRP */ bool stop(); /** Set priority not supported */ void setPriority(float prio = 0.5) { } enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; } /** Read samples from the USRP. @param buf preallocated buf to contain read result @param len number of samples desired @param overrun Set if read buffer has been overrun, e.g. data not being read fast enough @param timestamp The timestamp of the first samples to be read @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough @param RSSI The received signal strength of the read result @return The number of samples actually read */ int readSamples(std::vector &buf, int len, bool *overrun, TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL, unsigned *RSSI = NULL); /** Write samples to the USRP. @param buf Contains the data to be written. @param len number of samples to write. @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough @param timestamp The timestamp of the first sample of the data buffer. @param isControl Set if data is a control packet, e.g. a ping command @return The number of samples actually written */ int writeSamples(std::vector &bufs, int len, bool *underrun, TIMESTAMP timestamp = 0xffffffff, bool isControl = false); /** Update the alignment between the read and write timestamps */ bool updateAlignment(TIMESTAMP timestamp); /** Set the transmitter frequency */ bool setTxFreq(double wFreq, size_t chan = 0); /** Set the receiver frequency */ bool setRxFreq(double wFreq, size_t chan = 0); /** Returns the starting write Timestamp*/ TIMESTAMP initialWriteTimestamp(void) { return 20000;} /** Returns the starting read Timestamp*/ TIMESTAMP initialReadTimestamp(void) { return 20000;} /** returns the full-scale transmit amplitude **/ double fullScaleInputValue() {return 13500.0;} /** returns the full-scale receive amplitude **/ double fullScaleOutputValue() {return 9450.0;} /** sets the receive chan gain, returns the gain setting **/ double setRxGain(double dB, size_t chan = 0); /** get the current receive gain */ double getRxGain(size_t chan = 0) { return rxGain; } /** return maximum Rx Gain **/ double maxRxGain(void); /** return minimum Rx Gain **/ double minRxGain(void); /** sets the transmit chan gain, returns the gain setting **/ double setTxGain(double dB, size_t chan = 0); /** return maximum Tx Gain **/ double maxTxGain(void); /** return minimum Rx Gain **/ double minTxGain(void); /** sets the RX path to use, returns true if successful and false otherwise */ bool setRxAntenna(const std::string &ant, size_t chan = 0); /* return the used RX path */ std::string getRxAntenna(size_t chan = 0); /** sets the RX path to use, returns true if successful and false otherwise */ bool setTxAntenna(const std::string &ant, size_t chan = 0); /* return the used RX path */ std::string getTxAntenna(size_t chan = 0); /** return whether user drives synchronization of Tx/Rx of USRP */ bool requiresRadioAlign(); /** return whether user drives synchronization of Tx/Rx of USRP */ virtual GSM::Time minLatency(); /** Return internal status values */ inline double getTxFreq(size_t chan = 0) { return 0; } inline double getRxFreq(size_t chan = 0) { return 0; } inline double getSampleRate() { return actualSampleRate; } inline double numberRead() { return samplesRead; } inline double numberWritten() { return samplesWritten; } std::vector tx_paths, rx_paths; }; #endif // _USRP_DEVICE_H_ osmo-trx-0.4.0/Transceiver52M/signalVector.h0000644000175000017500000000236513272615542017712 0ustar rubenruben#ifndef _SIGNALVECTOR_H_ #define _SIGNALVECTOR_H_ #include #include /** Vector symmetry */ enum Symmetry { NONE = 0, ABSSYM = 1 }; class signalVector: public Vector { public: /** Default constructor */ signalVector(size_t size = 0); /** Construct with head room */ signalVector(size_t size, size_t start); /** Construct from existing buffer data (buffer not managed) */ signalVector(complex *data, size_t start, size_t span); /** Construct by from existing vector */ signalVector(const signalVector &vector); /** Construct by from existing vector and append head-tail room */ signalVector(const signalVector &vector, size_t start, size_t tail = 0); /** Override base assignment operator to include start offsets */ void operator=(const signalVector& vector); /** Return an alias to a segment of this signalVector. */ signalVector segment(size_t start, size_t span); /** Return head room */ size_t getStart() const; size_t updateHistory(); Symmetry getSymmetry() const; void setSymmetry(Symmetry symmetry); bool isReal() const; void isReal(bool real); bool isAligned() const; void setAligned(bool aligned); private: bool real; bool aligned; Symmetry symmetry; }; #endif /* _SIGNALVECTOR_H_ */ osmo-trx-0.4.0/Transceiver52M/Makefile.am0000644000175000017500000000474213272615542017136 0ustar rubenruben# # Copyright 2008 Free Software Foundation, Inc. # Copyright 2010 Range Networks, Inc. # # This software is distributed under the terms of the GNU Public License. # See the COPYING file in the main directory for details. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # include $(top_srcdir)/Makefile.common SUBDIRS = arch device AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) rev2dir = $(datadir)/usrp/rev2 rev4dir = $(datadir)/usrp/rev4 dist_rev2_DATA = std_inband.rbf dist_rev4_DATA = std_inband.rbf EXTRA_DIST = README noinst_LTLIBRARIES = libtransceiver_common.la COMMON_SOURCES = \ radioInterface.cpp \ radioVector.cpp \ radioClock.cpp \ radioBuffer.cpp \ sigProcLib.cpp \ signalVector.cpp \ Transceiver.cpp \ ChannelizerBase.cpp \ Channelizer.cpp \ Synthesis.cpp libtransceiver_common_la_SOURCES = \ $(COMMON_SOURCES) \ Resampler.cpp \ radioInterfaceResamp.cpp \ radioInterfaceMulti.cpp noinst_HEADERS = \ Complex.h \ radioInterface.h \ radioVector.h \ radioClock.h \ radioBuffer.h \ sigProcLib.h \ signalVector.h \ Transceiver.h \ Resampler.h \ ChannelizerBase.h \ Channelizer.h \ Synthesis.h COMMON_LDADD = \ libtransceiver_common.la \ $(ARCH_LA) \ $(GSM_LA) \ $(COMMON_LA) \ $(FFTWF_LIBS) \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOCTRL_LIBS) \ $(LIBOSMOVTY_LIBS) bin_PROGRAMS = if DEVICE_UHD bin_PROGRAMS += osmo-trx-uhd osmo_trx_uhd_SOURCES = osmo-trx.cpp osmo_trx_uhd_LDADD = \ $(builddir)/device/uhd/libdevice.la \ $(COMMON_LDADD) \ $(UHD_LIBS) osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) endif if DEVICE_USRP1 bin_PROGRAMS += osmo-trx-usrp1 osmo_trx_usrp1_SOURCES = osmo-trx.cpp osmo_trx_usrp1_LDADD = \ $(builddir)/device/usrp1/libdevice.la \ $(COMMON_LDADD) \ $(USRP_LIBS) osmo_trx_usrp1_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS) endif osmo-trx-0.4.0/Transceiver52M/radioBuffer.cpp0000644000175000017500000001264113272615542020033 0ustar rubenruben/* * Segmented Ring Buffer * * Copyright (C) 2015 Ettus Research LLC * * Author: Tom Tsou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include #include #include "radioBuffer.h" RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen, size_t hLen, bool outDirection) : writeIndex(0), readIndex(0), availSamples(0) { if (!outDirection) hLen = 0; buffer = new float[2 * (hLen + numSegments * segmentLen)]; bufferLen = numSegments * segmentLen; segments.resize(numSegments); for (size_t i = 0; i < numSegments; i++) segments[i] = &buffer[2 * (hLen + i * segmentLen)]; this->outDirection = outDirection; this->numSegments = numSegments; this->segmentLen = segmentLen; this->hLen = hLen; } RadioBuffer::~RadioBuffer() { delete[] buffer; } void RadioBuffer::reset() { writeIndex = 0; readIndex = 0; availSamples = 0; } /* * Output direction * * Return a pointer to the oldest segment or NULL if a complete segment is not * available. */ const float *RadioBuffer::getReadSegment() { if (!outDirection) { std::cout << "Invalid direction" << std::endl; return NULL; } if (availSamples < segmentLen) { std::cout << "Not enough samples " << std::endl; std::cout << availSamples << " available per segment " << segmentLen << std::endl; return NULL; } size_t num = readIndex / segmentLen; if (num >= numSegments) { std::cout << "Invalid segment" << std::endl; return NULL; } else if (!num) { memcpy(buffer, &buffer[2 * bufferLen], hLen * 2 * sizeof(float)); } availSamples -= segmentLen; readIndex = (readIndex + segmentLen) % bufferLen; return segments[num]; } /* * Output direction * * Write a non-segment length of samples to the buffer. */ bool RadioBuffer::write(const float *wr, size_t len) { if (!outDirection) { std::cout << "Invalid direction" << std::endl; return false; } if (availSamples + len > bufferLen) { std::cout << "Insufficient space" << std::endl; std::cout << bufferLen - availSamples << " available per write " << len << std::endl; return false; } if (writeIndex + len <= bufferLen) { memcpy(&buffer[2 * (writeIndex + hLen)], wr, len * 2 * sizeof(float)); } else { size_t len0 = bufferLen - writeIndex; size_t len1 = len - len0; memcpy(&buffer[2 * (writeIndex + hLen)], wr, len0 * 2 * sizeof(float)); memcpy(&buffer[2 * hLen], &wr[2 * len0], len1 * 2 * sizeof(float)); } availSamples += len; writeIndex = (writeIndex + len) % bufferLen; return true; } bool RadioBuffer::zero(size_t len) { if (!outDirection) { std::cout << "Invalid direction" << std::endl; return false; } if (availSamples + len > bufferLen) { std::cout << "Insufficient space" << std::endl; std::cout << bufferLen - availSamples << " available per zero " << len << std::endl; return false; } if (writeIndex + len <= bufferLen) { memset(&buffer[2 * (writeIndex + hLen)], 0, len * 2 * sizeof(float)); } else { size_t len0 = bufferLen - writeIndex; size_t len1 = len - len0; memset(&buffer[2 * (writeIndex + hLen)], 0, len0 * 2 * sizeof(float)); memset(&buffer[2 * hLen], 0, len1 * 2 * sizeof(float)); } availSamples += len; writeIndex = (writeIndex + len) % bufferLen; return true; } /* * Input direction */ float *RadioBuffer::getWriteSegment() { if (outDirection) { std::cout << "Invalid direction" << std::endl; return NULL; } if (bufferLen - availSamples < segmentLen) { std::cout << "Insufficient samples" << std::endl; std::cout << bufferLen - availSamples << " available for segment " << segmentLen << std::endl; return NULL; } if (writeIndex % segmentLen) { std::cout << "Internal segment error" << std::endl; return NULL; } size_t num = writeIndex / segmentLen; if (num >= numSegments) return NULL; availSamples += segmentLen; writeIndex = (writeIndex + segmentLen) % bufferLen; return segments[num]; } bool RadioBuffer::zeroWriteSegment() { float *segment = getWriteSegment(); if (!segment) return false; memset(segment, 0, segmentLen * 2 * sizeof(float)); return true; } bool RadioBuffer::read(float *rd, size_t len) { if (outDirection) { std::cout << "Invalid direction" << std::endl; return false; } if (availSamples < len) { std::cout << "Insufficient samples" << std::endl; std::cout << availSamples << " available for " << len << std::endl; return false; } if (readIndex + len <= bufferLen) { memcpy(rd, &buffer[2 * readIndex], len * 2 * sizeof(float)); } else { size_t len0 = bufferLen - readIndex; size_t len1 = len - len0; memcpy(rd, &buffer[2 * readIndex], len0 * 2 * sizeof(float)); memcpy(&rd[2 * len0], buffer, len1 * 2 * sizeof(float)); } availSamples -= len; readIndex = (readIndex + len) % bufferLen; return true; } osmo-trx-0.4.0/Transceiver52M/signalVector.cpp0000644000175000017500000000356713272615542020252 0ustar rubenruben#include "signalVector.h" signalVector::signalVector(size_t size) : Vector(size), real(false), aligned(false), symmetry(NONE) { } signalVector::signalVector(size_t size, size_t start) : Vector(size + start), real(false), aligned(false), symmetry(NONE) { mStart = mData + start; } signalVector::signalVector(complex *data, size_t start, size_t span) : Vector(NULL, data + start, data + start + span), real(false), aligned(false), symmetry(NONE) { } signalVector::signalVector(const signalVector &vector) : Vector(vector.size() + vector.getStart()), aligned(false) { mStart = mData + vector.getStart(); vector.copyTo(*this); symmetry = vector.getSymmetry(); real = vector.isReal(); }; signalVector::signalVector(const signalVector &vector, size_t start, size_t tail) : Vector(start + vector.size() + tail), aligned(false) { mStart = mData + start; vector.copyTo(*this); symmetry = vector.getSymmetry(); real = vector.isReal(); }; void signalVector::operator=(const signalVector& vector) { resize(vector.size() + vector.getStart()); memcpy(mData, vector.mData, bytes()); mStart = mData + vector.getStart(); } signalVector signalVector::segment(size_t start, size_t span) { return signalVector(mData, start, span); } size_t signalVector::getStart() const { return mStart - mData; } size_t signalVector::updateHistory() { size_t num = getStart(); memmove(mData, mStart + this->size() - num, num * sizeof(complex)); return num; } Symmetry signalVector::getSymmetry() const { return symmetry; } void signalVector::setSymmetry(Symmetry symmetry) { this->symmetry = symmetry; } bool signalVector::isReal() const { return real; } void signalVector::isReal(bool wOnly) { real = wOnly; } bool signalVector::isAligned() const { return aligned; } void signalVector::setAligned(bool aligned) { this->aligned = aligned; } osmo-trx-0.4.0/Transceiver52M/radioClock.cpp0000644000175000017500000000243713272615542017657 0ustar rubenruben/* * Written by Thomas Tsou * Based on code by Harvind S Samra * * Copyright 2011 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include "radioClock.h" void RadioClock::set(const GSM::Time& wTime) { ScopedLock lock(mLock); mClock = wTime; updateSignal.signal(); } void RadioClock::incTN() { ScopedLock lock(mLock); mClock.incTN(); updateSignal.signal(); } GSM::Time RadioClock::get() { ScopedLock lock(mLock); GSM::Time retVal = mClock; return retVal; } void RadioClock::wait() { ScopedLock lock(mLock); updateSignal.wait(mLock,1); } osmo-trx-0.4.0/Transceiver52M/sigProcLib.h0000644000175000017500000001055313272615542017305 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef SIGPROCLIB_H #define SIGPROCLIB_H #include "Vector.h" #include "Complex.h" #include "BitVector.h" #include "signalVector.h" /* Burst lengths */ #define NORMAL_BURST_NBITS 148 #define EDGE_BURST_NBITS 444 #define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3) /** Codes for burst types of received bursts*/ enum CorrType{ OFF, ///< timeslot is off TSC, ///< timeslot should contain a normal burst RACH, ///< timeslot should contain an access burst EDGE, ///< timeslot should contain an EDGE burst IDLE ///< timeslot is an idle (or dummy) burst }; enum SignalError { SIGERR_NONE, SIGERR_BOUNDS, SIGERR_CLIP, SIGERR_UNSUPPORTED, SIGERR_INTERNAL, }; /* * Burst detection threshold * * Decision threshold value for burst gating on peak-to-average value of * correlated synchronization sequences. Lower values pass more bursts up * to upper layers but will increase the false detection rate. */ #define BURST_THRESH 4.0 /** Setup the signal processing library */ bool sigProcLibSetup(); /** Destroy the signal processing library */ void sigProcLibDestroy(void); /** Operate soft slicer on a soft-bit vector */ bool vectorSlicer(SoftVector *x); /** GMSK modulate a GSM burst of bits */ signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength, int sps, bool emptyPulse = false); /** 8-PSK modulate a burst of bits */ signalVector *modulateEdgeBurst(const BitVector &bits, int sps, bool emptyPulse = false); /** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */ signalVector *generateEdgeBurst(int tsc); /** Generate an empty burst - 4 or 1 SPS */ signalVector *generateEmptyBurst(int sps, int tn); /** Generate a normal GSM burst with random payload - 4 or 1 SPS */ signalVector *genRandNormalBurst(int tsc, int sps, int tn); /** Generate an access GSM burst with random payload - 4 or 1 SPS */ signalVector *genRandAccessBurst(int delay, int sps, int tn); /** Generate a dummy GSM burst - 4 or 1 SPS */ signalVector *generateDummyBurst(int sps, int tn); /** Apply a scalar to a vector. @param x The vector of interest. @param scale The scalar. */ void scaleVector(signalVector &x, complex scale); /** Rough energy estimator. @param rxBurst A GSM burst. @param windowLength The number of burst samples used to compute burst energy @return The average power of the received burst. */ float energyDetect(const signalVector &rxBurst, unsigned windowLength); /** 8-PSK/GMSK/RACH burst detector @param burst The received GSM burst of interest @param tsc Midamble type (0..7) also known as TSC @param threshold The threshold that the received burst's post-correlator SNR is compared against to determine validity. @param sps The number of samples per GSM symbol. @param amplitude The estimated amplitude of received TSC burst. @param toa The estimate time-of-arrival of received TSC burst (in symbols). @param max_toa The maximum expected time-of-arrival (in symbols). @return positive value (CorrType) if threshold value is reached, negative value (-SignalError) on error, zero (SIGERR_NONE) if no burst is detected */ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold, int sps, CorrType type, complex &, float &toa, unsigned max_toa); /** Demodulate burst basde on type and output soft bits */ SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp, float toa, CorrType type); #endif /* SIGPROCLIB_H */ osmo-trx-0.4.0/Transceiver52M/README0000644000175000017500000000327113272615542015756 0ustar rubenrubenThe Transceiver The transceiver consists of three modules: --- transceiver --- radioInterface --- USRPDevice The USRPDevice module is basically a driver that reads/writes packets to a USRP with two RFX900 daughterboards, board A is the Tx chain and board B is the Rx chain. The radioInterface module is basically an interface b/w the transceiver and the USRP. It operates the basestation clock based upon the sample count of received USRP samples. Packets from the USRP are queued and segmented into GSM bursts that are passed up to the transceiver; bursts from the transceiver are passed down to the USRP. The transceiver basically operates "layer 0" of the GSM stack, performing the modulation, detection, and demodulation of GSM bursts. It communicates with the GSM stack via three UDP sockets, one socket for data, one for control messages, and one socket to pass clocking information. The transceiver contains a priority queue to sort to-be-transmitted bursts, and a filler table to fill in timeslots that do not have bursts in the priority queue. The transceiver tries to stay ahead of the basestation clock, adapting its latency when underruns are reported by the radioInterface/USRP. Received bursts (from the radioInterface) pass through a simple energy detector, a RACH or midamble correlator, and a DFE-based demodulator. NOTE: There's a SWLOOPBACK #define statement, where the USRP is replaced with a memory buffer. In this mode, data written to the USRP is actually stored in a buffer, and read commands to the USRP simply pull data from this buffer. This was very useful in early testing, and still may be useful in testing basic Transceiver and radioInterface functionality. osmo-trx-0.4.0/Transceiver52M/osmo-trx.cpp0000644000175000017500000003615513272615542017401 0ustar rubenruben/* * Copyright (C) 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "Transceiver.h" #include "radioDevice.h" #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include #include #include #include #include #include #include #include #include #include #include #include #include "convolve.h" #include "convert.h" #include "trx_vty.h" #include "debug.h" } #define DEFAULT_CONFIG_FILE "osmo-trx.cfg" #define charp2str(a) ((a) ? std::string(a) : std::string("")) static char* config_file = (char*)DEFAULT_CONFIG_FILE; volatile bool gshutdown = false; static void *tall_trx_ctx; static struct trx_ctx *g_trx_ctx; static struct ctrl_handle *g_ctrlh; static RadioDevice *usrp; static RadioInterface *radio; static Transceiver *transceiver; /* Create radio interface * The interface consists of sample rate changes, frequency shifts, * channel multiplexing, and other conversions. The transceiver core * accepts input vectors sampled at multiples of the GSM symbol rate. * The radio interface connects the main transceiver with the device * object, which may be operating some other rate. */ RadioInterface *makeRadioInterface(struct trx_ctx *trx, RadioDevice *usrp, int type) { RadioInterface *radio = NULL; switch (type) { case RadioDevice::NORMAL: radio = new RadioInterface(usrp, trx->cfg.tx_sps, trx->cfg.rx_sps, trx->cfg.num_chans); break; case RadioDevice::RESAMP_64M: case RadioDevice::RESAMP_100M: radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps, trx->cfg.rx_sps); break; case RadioDevice::MULTI_ARFCN: radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps, trx->cfg.rx_sps, trx->cfg.num_chans); break; default: LOG(ALERT) << "Unsupported radio interface configuration"; return NULL; } if (!radio->init(type)) { LOG(ALERT) << "Failed to initialize radio interface"; return NULL; } return radio; } /* Create transceiver core * The multi-threaded modem core operates at multiples of the GSM rate of * 270.8333 ksps and consists of GSM specific modulation, demodulation, * and decoding schemes. Also included are the socket interfaces for * connecting to the upper layer stack. */ int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio) { VectorFIFO *fifo; transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr, trx->cfg.remote_addr, trx->cfg.tx_sps, trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0), radio, trx->cfg.rssi_offset); if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc, trx->cfg.rach_delay, trx->cfg.egprs)) { LOG(ALERT) << "Failed to initialize transceiver"; return -1; } for (size_t i = 0; i < trx->cfg.num_chans; i++) { fifo = radio->receiveFIFO(i); if (fifo && transceiver->receiveFIFO(fifo, i)) continue; LOG(ALERT) << "Could not attach FIFO to channel " << i; return -1; } return 0; } static void sig_handler(int signo) { fprintf(stdout, "signal %d received\n", signo); switch (signo) { case SIGINT: case SIGTERM: fprintf(stdout, "shutting down\n"); gshutdown = true; break; case SIGABRT: case SIGUSR1: talloc_report(tall_trx_ctx, stderr); talloc_report_full(tall_trx_ctx, stderr); break; case SIGUSR2: talloc_report_full(tall_trx_ctx, stderr); break; default: break; } } static void setup_signal_handlers() { /* Handle keyboard interrupt SIGINT */ signal(SIGINT, &sig_handler); signal(SIGTERM, &sig_handler); signal(SIGABRT, &sig_handler); signal(SIGUSR1, &sig_handler); signal(SIGUSR2, &sig_handler); osmo_init_ignore_signals(); } static std::vector comma_delimited_to_vector(char* opt) { std::string str = std::string(opt); std::vector result; std::stringstream ss(str); while( ss.good() ) { std::string substr; getline(ss, substr, ','); result.push_back(substr); } return result; } static void print_help() { fprintf(stdout, "Options:\n" " -h This text\n" " -C Filename The config file to use\n" ); } static void print_deprecated(char opt) { LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed." << " Please use VTY cfg option instead." << " All cmd line options are already being overriden by VTY options if set."; } static void handle_options(int argc, char **argv, struct trx_ctx* trx) { int option; unsigned int i; std::vector rx_paths, tx_paths; bool rx_paths_set = false, tx_paths_set = false; while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:")) != -1) { switch (option) { case 'h': print_help(); exit(0); break; case 'a': print_deprecated(option); osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg); break; case 'l': print_deprecated(option); log_set_log_level(osmo_stderr_target, atoi(optarg)); break; case 'i': print_deprecated(option); osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg); break; case 'j': print_deprecated(option); osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg); break; case 'p': print_deprecated(option); trx->cfg.base_port = atoi(optarg); break; case 'c': print_deprecated(option); trx->cfg.num_chans = atoi(optarg); break; case 'm': print_deprecated(option); trx->cfg.multi_arfcn = true; break; case 'x': print_deprecated(option); trx->cfg.clock_ref = REF_EXTERNAL; break; case 'g': print_deprecated(option); trx->cfg.clock_ref = REF_GPS; break; case 'f': print_deprecated(option); trx->cfg.filler = FILLER_DUMMY; break; case 'o': print_deprecated(option); trx->cfg.offset = atof(optarg); break; case 's': print_deprecated(option); trx->cfg.tx_sps = atoi(optarg); break; case 'b': print_deprecated(option); trx->cfg.rx_sps = atoi(optarg); break; case 'r': print_deprecated(option); trx->cfg.rtsc_set = true; trx->cfg.rtsc = atoi(optarg); if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */ trx->cfg.filler = FILLER_NORM_RAND; break; case 'A': print_deprecated(option); trx->cfg.rach_delay_set = true; trx->cfg.rach_delay = atoi(optarg); trx->cfg.filler = FILLER_ACCESS_RAND; break; case 'R': print_deprecated(option); trx->cfg.rssi_offset = atof(optarg); break; case 'S': print_deprecated(option); trx->cfg.swap_channels = true; break; case 'e': print_deprecated(option); trx->cfg.egprs = true; break; case 't': print_deprecated(option); trx->cfg.sched_rr = atoi(optarg); break; case 'y': print_deprecated(option); tx_paths = comma_delimited_to_vector(optarg); tx_paths_set = true; break; case 'z': print_deprecated(option); rx_paths = comma_delimited_to_vector(optarg); rx_paths_set = true; break; case 'C': config_file = optarg; break; default: goto bad_config; } } /* Cmd line option specific validation & setup */ if (trx->cfg.num_chans > TRX_CHAN_MAX) { LOG(ERROR) << "Too many channels requested, maximum is " << TRX_CHAN_MAX; goto bad_config; } if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) || (rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) { LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match"; goto bad_config; } for (i = 0; i < trx->cfg.num_chans; i++) { trx->cfg.chans[i].trx = trx; trx->cfg.chans[i].idx = i; if (tx_paths_set) osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str()); if (rx_paths_set) osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str()); } return; bad_config: print_help(); exit(0); } int trx_validate_config(struct trx_ctx *trx) { if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) { LOG(ERROR) << "Unsupported number of channels"; return -1; } /* Force 4 SPS for EDGE or multi-ARFCN configurations */ if ((trx->cfg.egprs || trx->cfg.multi_arfcn) && (trx->cfg.tx_sps!=4 || trx->cfg.tx_sps!=4)) { LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config."; return -1; } return 0; } static int set_sched_rr(unsigned int prio) { struct sched_param param; int rc; memset(¶m, 0, sizeof(param)); param.sched_priority = prio; printf("Setting SCHED_RR priority(%d)\n", param.sched_priority); rc = sched_setscheduler(getpid(), SCHED_RR, ¶m); if (rc != 0) { LOG(ERROR) << "Config: Setting SCHED_RR failed"; return -1; } return 0; } static void print_config(struct trx_ctx *trx) { unsigned int i; std::ostringstream ost(""); ost << "Config Settings" << std::endl; ost << " Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl; ost << " Device args............. " << charp2str(trx->cfg.dev_args) << std::endl; ost << " TRX Base Port........... " << trx->cfg.base_port << std::endl; ost << " TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl; ost << " GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl; ost << " Channels................ " << trx->cfg.num_chans << std::endl; ost << " Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl; ost << " Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl; ost << " EDGE support............ " << trx->cfg.egprs << std::endl; ost << " Reference............... " << trx->cfg.clock_ref << std::endl; ost << " C0 Filler Table......... " << trx->cfg.filler << std::endl; ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl; ost << " Tuning offset........... " << trx->cfg.offset << std::endl; ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl; ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl; ost << " Tx Antennas............."; for (i = 0; i < trx->cfg.num_chans; i++) { std::string p = charp2str(trx->cfg.chans[i].tx_path); ost << " '" << ((p != "") ? p : "") << "'"; } ost << std::endl; ost << " Rx Antennas............."; for (i = 0; i < trx->cfg.num_chans; i++) { std::string p = charp2str(trx->cfg.chans[i].rx_path); ost << " '" << ((p != "") ? p : "") << "'"; } ost << std::endl; std::cout << ost << std::endl; } static void trx_stop() { std::cout << "Shutting down transceiver..." << std::endl; delete transceiver; delete radio; delete usrp; } static int trx_start(struct trx_ctx *trx) { int type, chans; unsigned int i; std::vector rx_paths, tx_paths; RadioDevice::InterfaceType iface = RadioDevice::NORMAL; /* Create the low level device object */ if (trx->cfg.multi_arfcn) iface = RadioDevice::MULTI_ARFCN; /* Generate vector of rx/tx_path: */ for (i = 0; i < trx->cfg.num_chans; i++) { rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path)); tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path)); } usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface, trx->cfg.num_chans, trx->cfg.offset, tx_paths, rx_paths); type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels); if (type < 0) { LOG(ALERT) << "Failed to create radio device" << std::endl; goto shutdown; } /* Setup the appropriate device interface */ radio = makeRadioInterface(trx, usrp, type); if (!radio) goto shutdown; /* Create the transceiver core */ if (makeTransceiver(trx, radio) < 0) goto shutdown; chans = transceiver->numChans(); std::cout << "-- Transceiver active with " << chans << " channel(s)" << std::endl; return 0; shutdown: trx_stop(); return -1; } int main(int argc, char *argv[]) { int rc; tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX"); msgb_talloc_ctx_init(tall_trx_ctx, 0); g_vty_info.tall_ctx = tall_trx_ctx; setup_signal_handlers(); g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx); #ifdef HAVE_SSE3 printf("Info: SSE3 support compiled in"); #ifdef HAVE___BUILTIN_CPU_SUPPORTS if (__builtin_cpu_supports("sse3")) printf(" and supported by CPU\n"); else printf(", but not supported by CPU\n"); #else printf(", but runtime SIMD detection disabled\n"); #endif #endif #ifdef HAVE_SSE4_1 printf("Info: SSE4.1 support compiled in"); #ifdef HAVE___BUILTIN_CPU_SUPPORTS if (__builtin_cpu_supports("sse4.1")) printf(" and supported by CPU\n"); else printf(", but not supported by CPU\n"); #else printf(", but runtime SIMD detection disabled\n"); #endif #endif convolve_init(); convert_init(); osmo_init_logging2(tall_trx_ctx, &log_info); osmo_stats_init(tall_trx_ctx); vty_init(&g_vty_info); ctrl_vty_init(tall_trx_ctx); trx_vty_init(g_trx_ctx); logging_vty_add_cmds(); osmo_talloc_vty_add_cmds(); osmo_stats_vty_add_cmds(); handle_options(argc, argv, g_trx_ctx); rate_ctr_init(tall_trx_ctx); rc = vty_read_config_file(config_file, NULL); if (rc < 0) { fprintf(stderr, "Failed to open config file: '%s'\n", config_file); exit(2); } rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX); if (rc < 0) exit(1); g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL); if (!g_ctrlh) { fprintf(stderr, "Failed to create CTRL interface.\n"); exit(1); } /* Backward compatibility: Hack to have 1 channel allocated by default. * Can be Dropped once we * get rid of "-c" cmdline param */ if (g_trx_ctx->cfg.num_chans == 0) { g_trx_ctx->cfg.num_chans = 1; g_trx_ctx->cfg.chans[0].trx = g_trx_ctx; g_trx_ctx->cfg.chans[0].idx = 0; LOG(ERROR) << "No explicit channel config found. Make sure you" \ " configure channels in VTY config. Using 1 channel as default," \ " but expect your config to break in the future."; } print_config(g_trx_ctx); if (trx_validate_config(g_trx_ctx) < 0) { LOG(ERROR) << "Config failure - exiting"; return EXIT_FAILURE; } if (g_trx_ctx->cfg.sched_rr) { if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0) return EXIT_FAILURE; } srandom(time(NULL)); if(trx_start(g_trx_ctx) < 0) return EXIT_FAILURE; while (!gshutdown) osmo_select_main(0); trx_stop(); return 0; } osmo-trx-0.4.0/Transceiver52M/arch/0000755000175000017500000000000013272615542016010 5ustar rubenrubenosmo-trx-0.4.0/Transceiver52M/arch/x86/0000755000175000017500000000000013272615542016435 5ustar rubenrubenosmo-trx-0.4.0/Transceiver52M/arch/x86/convolve_sse_3.c0000644000175000017500000003657213272615542021545 0ustar rubenruben/* * SSE Convolution * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "convolve_sse_3.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SSE3 #include #include /* 4-tap SSE complex-real convolution */ void sse_conv_real4(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { /* NOTE: The parameter list of this function has to match the parameter * list of _base_convolve_real() in convolve_base.c. This specific * implementation, ignores some of the parameters of * _base_convolve_complex(), which are: x_len, y_len, offset, step */ __m128 m0, m1, m2, m3, m4, m5, m6, m7; const float *_x = &x[2 * (-(h_len - 1) + start)]; /* Load (aligned) filter taps */ m0 = _mm_load_ps(&h[0]); m1 = _mm_load_ps(&h[4]); m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); for (int i = 0; i < len; i++) { /* Load (unaligned) input data */ m0 = _mm_loadu_ps(&_x[2 * i + 0]); m1 = _mm_loadu_ps(&_x[2 * i + 4]); m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); /* Quad multiply */ m4 = _mm_mul_ps(m2, m7); m5 = _mm_mul_ps(m3, m7); /* Sum and store */ m6 = _mm_hadd_ps(m4, m5); m0 = _mm_hadd_ps(m6, m6); _mm_store_ss(&y[2 * i + 0], m0); m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1)); _mm_store_ss(&y[2 * i + 1], m0); } } /* 8-tap SSE complex-real convolution */ void sse_conv_real8(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { /* See NOTE in sse_conv_real4() */ __m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9; const float *_x = &x[2 * (-(h_len - 1) + start)]; /* Load (aligned) filter taps */ m0 = _mm_load_ps(&h[0]); m1 = _mm_load_ps(&h[4]); m2 = _mm_load_ps(&h[8]); m3 = _mm_load_ps(&h[12]); m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m5 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); for (int i = 0; i < len; i++) { /* Load (unaligned) input data */ m0 = _mm_loadu_ps(&_x[2 * i + 0]); m1 = _mm_loadu_ps(&_x[2 * i + 4]); m2 = _mm_loadu_ps(&_x[2 * i + 8]); m3 = _mm_loadu_ps(&_x[2 * i + 12]); m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); /* Quad multiply */ m6 = _mm_mul_ps(m6, m4); m7 = _mm_mul_ps(m7, m4); m8 = _mm_mul_ps(m8, m5); m9 = _mm_mul_ps(m9, m5); /* Sum and store */ m6 = _mm_add_ps(m6, m8); m7 = _mm_add_ps(m7, m9); m6 = _mm_hadd_ps(m6, m7); m6 = _mm_hadd_ps(m6, m6); _mm_store_ss(&y[2 * i + 0], m6); m6 = _mm_shuffle_ps(m6, m6, _MM_SHUFFLE(0, 3, 2, 1)); _mm_store_ss(&y[2 * i + 1], m6); } } /* 12-tap SSE complex-real convolution */ void sse_conv_real12(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { /* See NOTE in sse_conv_real4() */ __m128 m0, m1, m2, m3, m4, m5, m6, m7; __m128 m8, m9, m10, m11, m12, m13, m14; const float *_x = &x[2 * (-(h_len - 1) + start)]; /* Load (aligned) filter taps */ m0 = _mm_load_ps(&h[0]); m1 = _mm_load_ps(&h[4]); m2 = _mm_load_ps(&h[8]); m3 = _mm_load_ps(&h[12]); m4 = _mm_load_ps(&h[16]); m5 = _mm_load_ps(&h[20]); m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2)); for (int i = 0; i < len; i++) { /* Load (unaligned) input data */ m0 = _mm_loadu_ps(&_x[2 * i + 0]); m1 = _mm_loadu_ps(&_x[2 * i + 4]); m2 = _mm_loadu_ps(&_x[2 * i + 8]); m3 = _mm_loadu_ps(&_x[2 * i + 12]); m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); m0 = _mm_loadu_ps(&_x[2 * i + 16]); m1 = _mm_loadu_ps(&_x[2 * i + 20]); m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); /* Quad multiply */ m0 = _mm_mul_ps(m4, m12); m1 = _mm_mul_ps(m5, m12); m2 = _mm_mul_ps(m6, m13); m3 = _mm_mul_ps(m7, m13); m4 = _mm_mul_ps(m8, m14); m5 = _mm_mul_ps(m9, m14); /* Sum and store */ m8 = _mm_add_ps(m0, m2); m9 = _mm_add_ps(m1, m3); m10 = _mm_add_ps(m8, m4); m11 = _mm_add_ps(m9, m5); m2 = _mm_hadd_ps(m10, m11); m3 = _mm_hadd_ps(m2, m2); _mm_store_ss(&y[2 * i + 0], m3); m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1)); _mm_store_ss(&y[2 * i + 1], m3); } } /* 16-tap SSE complex-real convolution */ void sse_conv_real16(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { /* See NOTE in sse_conv_real4() */ __m128 m0, m1, m2, m3, m4, m5, m6, m7; __m128 m8, m9, m10, m11, m12, m13, m14, m15; const float *_x = &x[2 * (-(h_len - 1) + start)]; /* Load (aligned) filter taps */ m0 = _mm_load_ps(&h[0]); m1 = _mm_load_ps(&h[4]); m2 = _mm_load_ps(&h[8]); m3 = _mm_load_ps(&h[12]); m4 = _mm_load_ps(&h[16]); m5 = _mm_load_ps(&h[20]); m6 = _mm_load_ps(&h[24]); m7 = _mm_load_ps(&h[28]); m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2)); m15 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2)); for (int i = 0; i < len; i++) { /* Load (unaligned) input data */ m0 = _mm_loadu_ps(&_x[2 * i + 0]); m1 = _mm_loadu_ps(&_x[2 * i + 4]); m2 = _mm_loadu_ps(&_x[2 * i + 8]); m3 = _mm_loadu_ps(&_x[2 * i + 12]); m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); m0 = _mm_loadu_ps(&_x[2 * i + 16]); m1 = _mm_loadu_ps(&_x[2 * i + 20]); m2 = _mm_loadu_ps(&_x[2 * i + 24]); m3 = _mm_loadu_ps(&_x[2 * i + 28]); m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); /* Quad multiply */ m0 = _mm_mul_ps(m4, m12); m1 = _mm_mul_ps(m5, m12); m2 = _mm_mul_ps(m6, m13); m3 = _mm_mul_ps(m7, m13); m4 = _mm_mul_ps(m8, m14); m5 = _mm_mul_ps(m9, m14); m6 = _mm_mul_ps(m10, m15); m7 = _mm_mul_ps(m11, m15); /* Sum and store */ m8 = _mm_add_ps(m0, m2); m9 = _mm_add_ps(m1, m3); m10 = _mm_add_ps(m4, m6); m11 = _mm_add_ps(m5, m7); m0 = _mm_add_ps(m8, m10); m1 = _mm_add_ps(m9, m11); m2 = _mm_hadd_ps(m0, m1); m3 = _mm_hadd_ps(m2, m2); _mm_store_ss(&y[2 * i + 0], m3); m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1)); _mm_store_ss(&y[2 * i + 1], m3); } } /* 20-tap SSE complex-real convolution */ void sse_conv_real20(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { /* See NOTE in sse_conv_real4() */ __m128 m0, m1, m2, m3, m4, m5, m6, m7; __m128 m8, m9, m11, m12, m13, m14, m15; const float *_x = &x[2 * (-(h_len - 1) + start)]; /* Load (aligned) filter taps */ m0 = _mm_load_ps(&h[0]); m1 = _mm_load_ps(&h[4]); m2 = _mm_load_ps(&h[8]); m3 = _mm_load_ps(&h[12]); m4 = _mm_load_ps(&h[16]); m5 = _mm_load_ps(&h[20]); m6 = _mm_load_ps(&h[24]); m7 = _mm_load_ps(&h[28]); m8 = _mm_load_ps(&h[32]); m9 = _mm_load_ps(&h[36]); m11 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m12 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m13 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2)); m14 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2)); m15 = _mm_shuffle_ps(m8, m9, _MM_SHUFFLE(0, 2, 0, 2)); for (int i = 0; i < len; i++) { /* Multiply-accumulate first 12 taps */ m0 = _mm_loadu_ps(&_x[2 * i + 0]); m1 = _mm_loadu_ps(&_x[2 * i + 4]); m2 = _mm_loadu_ps(&_x[2 * i + 8]); m3 = _mm_loadu_ps(&_x[2 * i + 12]); m4 = _mm_loadu_ps(&_x[2 * i + 16]); m5 = _mm_loadu_ps(&_x[2 * i + 20]); m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2)); m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3)); m2 = _mm_mul_ps(m6, m11); m3 = _mm_mul_ps(m7, m11); m4 = _mm_mul_ps(m8, m12); m5 = _mm_mul_ps(m9, m12); m6 = _mm_mul_ps(m0, m13); m7 = _mm_mul_ps(m1, m13); m0 = _mm_add_ps(m2, m4); m1 = _mm_add_ps(m3, m5); m8 = _mm_add_ps(m0, m6); m9 = _mm_add_ps(m1, m7); /* Multiply-accumulate last 8 taps */ m0 = _mm_loadu_ps(&_x[2 * i + 24]); m1 = _mm_loadu_ps(&_x[2 * i + 28]); m2 = _mm_loadu_ps(&_x[2 * i + 32]); m3 = _mm_loadu_ps(&_x[2 * i + 36]); m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); m0 = _mm_mul_ps(m4, m14); m1 = _mm_mul_ps(m5, m14); m2 = _mm_mul_ps(m6, m15); m3 = _mm_mul_ps(m7, m15); m4 = _mm_add_ps(m0, m2); m5 = _mm_add_ps(m1, m3); /* Final sum and store */ m0 = _mm_add_ps(m8, m4); m1 = _mm_add_ps(m9, m5); m2 = _mm_hadd_ps(m0, m1); m3 = _mm_hadd_ps(m2, m2); _mm_store_ss(&y[2 * i + 0], m3); m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1)); _mm_store_ss(&y[2 * i + 1], m3); } } /* 4*N-tap SSE complex-real convolution */ void sse_conv_real4n(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { /* See NOTE in sse_conv_real4() */ __m128 m0, m1, m2, m4, m5, m6, m7; const float *_x = &x[2 * (-(h_len - 1) + start)]; for (int i = 0; i < len; i++) { /* Zero */ m6 = _mm_setzero_ps(); m7 = _mm_setzero_ps(); for (int n = 0; n < h_len / 4; n++) { /* Load (aligned) filter taps */ m0 = _mm_load_ps(&h[8 * n + 0]); m1 = _mm_load_ps(&h[8 * n + 4]); m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); /* Load (unaligned) input data */ m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]); m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]); m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); /* Quad multiply */ m0 = _mm_mul_ps(m2, m4); m1 = _mm_mul_ps(m2, m5); /* Accumulate */ m6 = _mm_add_ps(m6, m0); m7 = _mm_add_ps(m7, m1); } m0 = _mm_hadd_ps(m6, m7); m0 = _mm_hadd_ps(m0, m0); _mm_store_ss(&y[2 * i + 0], m0); m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1)); _mm_store_ss(&y[2 * i + 1], m0); } } /* 4*N-tap SSE complex-complex convolution */ void sse_conv_cmplx_4n(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { /* NOTE: The parameter list of this function has to match the parameter * list of _base_convolve_complex() in convolve_base.c. This specific * implementation, ignores some of the parameters of * _base_convolve_complex(), which are: x_len, y_len, offset, step. */ __m128 m0, m1, m2, m3, m4, m5, m6, m7; const float *_x = &x[2 * (-(h_len - 1) + start)]; for (int i = 0; i < len; i++) { /* Zero */ m6 = _mm_setzero_ps(); m7 = _mm_setzero_ps(); for (int n = 0; n < h_len / 4; n++) { /* Load (aligned) filter taps */ m0 = _mm_load_ps(&h[8 * n + 0]); m1 = _mm_load_ps(&h[8 * n + 4]); m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); /* Load (unaligned) input data */ m0 = _mm_loadu_ps(&_x[2 * i + 8 * n + 0]); m1 = _mm_loadu_ps(&_x[2 * i + 8 * n + 4]); m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); /* Quad multiply */ m0 = _mm_mul_ps(m2, m4); m1 = _mm_mul_ps(m3, m5); m2 = _mm_mul_ps(m2, m5); m3 = _mm_mul_ps(m3, m4); /* Sum */ m0 = _mm_sub_ps(m0, m1); m2 = _mm_add_ps(m2, m3); /* Accumulate */ m6 = _mm_add_ps(m6, m0); m7 = _mm_add_ps(m7, m2); } m0 = _mm_hadd_ps(m6, m7); m0 = _mm_hadd_ps(m0, m0); _mm_store_ss(&y[2 * i + 0], m0); m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1)); _mm_store_ss(&y[2 * i + 1], m0); } } /* 8*N-tap SSE complex-complex convolution */ void sse_conv_cmplx_8n(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { /* See NOTE in sse_conv_cmplx_4n() */ __m128 m0, m1, m2, m3, m4, m5, m6, m7; __m128 m8, m9, m10, m11, m12, m13, m14, m15; const float *_x = &x[2 * (-(h_len - 1) + start)]; for (int i = 0; i < len; i++) { /* Zero */ m12 = _mm_setzero_ps(); m13 = _mm_setzero_ps(); m14 = _mm_setzero_ps(); m15 = _mm_setzero_ps(); for (int n = 0; n < h_len / 8; n++) { /* Load (aligned) filter taps */ m0 = _mm_load_ps(&h[16 * n + 0]); m1 = _mm_load_ps(&h[16 * n + 4]); m2 = _mm_load_ps(&h[16 * n + 8]); m3 = _mm_load_ps(&h[16 * n + 12]); m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); /* Load (unaligned) input data */ m0 = _mm_loadu_ps(&_x[2 * i + 16 * n + 0]); m1 = _mm_loadu_ps(&_x[2 * i + 16 * n + 4]); m2 = _mm_loadu_ps(&_x[2 * i + 16 * n + 8]); m3 = _mm_loadu_ps(&_x[2 * i + 16 * n + 12]); m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2)); m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3)); m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2)); m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3)); /* Quad multiply */ m0 = _mm_mul_ps(m4, m8); m1 = _mm_mul_ps(m5, m9); m2 = _mm_mul_ps(m6, m10); m3 = _mm_mul_ps(m7, m11); m4 = _mm_mul_ps(m4, m9); m5 = _mm_mul_ps(m5, m8); m6 = _mm_mul_ps(m6, m11); m7 = _mm_mul_ps(m7, m10); /* Sum */ m0 = _mm_sub_ps(m0, m1); m2 = _mm_sub_ps(m2, m3); m4 = _mm_add_ps(m4, m5); m6 = _mm_add_ps(m6, m7); /* Accumulate */ m12 = _mm_add_ps(m12, m0); m13 = _mm_add_ps(m13, m2); m14 = _mm_add_ps(m14, m4); m15 = _mm_add_ps(m15, m6); } m0 = _mm_add_ps(m12, m13); m1 = _mm_add_ps(m14, m15); m2 = _mm_hadd_ps(m0, m1); m2 = _mm_hadd_ps(m2, m2); _mm_store_ss(&y[2 * i + 0], m2); m2 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(0, 3, 2, 1)); _mm_store_ss(&y[2 * i + 1], m2); } } #endif osmo-trx-0.4.0/Transceiver52M/arch/x86/convolve.c0000644000175000017500000001247713272615542020447 0ustar rubenruben/* * SSE Convolution * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "convolve.h" #include "convolve_sse_3.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Architecture dependant function pointers */ struct convolve_cpu_context { void (*conv_cmplx_4n) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_cmplx_8n) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_cmplx) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_real4) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_real8) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_real12) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_real16) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_real20) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_real4n) (const float *, int, const float *, int, float *, int, int, int, int, int); void (*conv_real) (const float *, int, const float *, int, float *, int, int, int, int, int); }; static struct convolve_cpu_context c; /* Forward declarations from base implementation */ int _base_convolve_real(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); int _base_convolve_complex(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); int bounds_check(int x_len, int h_len, int y_len, int start, int len, int step); /* API: Initalize convolve module */ void convolve_init(void) { c.conv_cmplx_4n = (void *)_base_convolve_complex; c.conv_cmplx_8n = (void *)_base_convolve_complex; c.conv_cmplx = (void *)_base_convolve_complex; c.conv_real4 = (void *)_base_convolve_real; c.conv_real8 = (void *)_base_convolve_real; c.conv_real12 = (void *)_base_convolve_real; c.conv_real16 = (void *)_base_convolve_real; c.conv_real20 = (void *)_base_convolve_real; c.conv_real4n = (void *)_base_convolve_real; c.conv_real = (void *)_base_convolve_real; #if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS) if (__builtin_cpu_supports("sse3")) { c.conv_cmplx_4n = sse_conv_cmplx_4n; c.conv_cmplx_8n = sse_conv_cmplx_8n; c.conv_real4 = sse_conv_real4; c.conv_real8 = sse_conv_real8; c.conv_real12 = sse_conv_real12; c.conv_real16 = sse_conv_real16; c.conv_real20 = sse_conv_real20; c.conv_real4n = sse_conv_real4n; } #endif } /* API: Aligned complex-real */ int convolve_real(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) return -1; memset(y, 0, len * 2 * sizeof(float)); if (step <= 4) { switch (h_len) { case 4: c.conv_real4(x, x_len, h, h_len, y, y_len, start, len, step, offset); break; case 8: c.conv_real8(x, x_len, h, h_len, y, y_len, start, len, step, offset); break; case 12: c.conv_real12(x, x_len, h, h_len, y, y_len, start, len, step, offset); break; case 16: c.conv_real16(x, x_len, h, h_len, y, y_len, start, len, step, offset); break; case 20: c.conv_real20(x, x_len, h, h_len, y, y_len, start, len, step, offset); break; default: if (!(h_len % 4)) c.conv_real4n(x, x_len, h, h_len, y, y_len, start, len, step, offset); else c.conv_real(x, x_len, h, h_len, y, y_len, start, len, step, offset); } } else c.conv_real(x, x_len, h, h_len, y, y_len, start, len, step, offset); return len; } /* API: Aligned complex-complex */ int convolve_complex(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) return -1; memset(y, 0, len * 2 * sizeof(float)); if (step <= 4) { if (!(h_len % 8)) c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start, len, step, offset); else if (!(h_len % 4)) c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start, len, step, offset); else c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len, step, offset); } else c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len, step, offset); return len; } osmo-trx-0.4.0/Transceiver52M/arch/x86/convert_sse_3.h0000644000175000017500000000261013272615542021361 0ustar rubenruben/* * SSE type conversions * Copyright (C) 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once /* 8*N single precision floats scaled and converted to 16-bit signed integer */ void _sse_convert_scale_ps_si16_8n(short *restrict out, const float *restrict in, float scale, int len); /* 8*N single precision floats scaled and converted with remainder */ void _sse_convert_scale_ps_si16(short *restrict out, const float *restrict in, float scale, int len); /* 16*N single precision floats scaled and converted to 16-bit signed integer */ void _sse_convert_scale_ps_si16_16n(short *restrict out, const float *restrict in, float scale, int len); osmo-trx-0.4.0/Transceiver52M/arch/x86/convert_sse_3.c0000644000175000017500000000565713272615542021372 0ustar rubenruben/* * SSE type conversions * Copyright (C) 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "convert_sse_3.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SSE3 #include #include /* 8*N single precision floats scaled and converted to 16-bit signed integer */ void _sse_convert_scale_ps_si16_8n(short *restrict out, const float *restrict in, float scale, int len) { __m128 m0, m1, m2; __m128i m4, m5; for (int i = 0; i < len / 8; i++) { /* Load (unaligned) packed floats */ m0 = _mm_loadu_ps(&in[8 * i + 0]); m1 = _mm_loadu_ps(&in[8 * i + 4]); m2 = _mm_load1_ps(&scale); /* Scale */ m0 = _mm_mul_ps(m0, m2); m1 = _mm_mul_ps(m1, m2); /* Convert */ m4 = _mm_cvtps_epi32(m0); m5 = _mm_cvtps_epi32(m1); /* Pack and store */ m5 = _mm_packs_epi32(m4, m5); _mm_storeu_si128((__m128i *) & out[8 * i], m5); } } /* 8*N single precision floats scaled and converted with remainder */ void _sse_convert_scale_ps_si16(short *restrict out, const float *restrict in, float scale, int len) { int start = len / 8 * 8; _sse_convert_scale_ps_si16_8n(out, in, scale, len); for (int i = 0; i < len % 8; i++) out[start + i] = in[start + i] * scale; } /* 16*N single precision floats scaled and converted to 16-bit signed integer */ void _sse_convert_scale_ps_si16_16n(short *restrict out, const float *restrict in, float scale, int len) { __m128 m0, m1, m2, m3, m4; __m128i m5, m6, m7, m8; for (int i = 0; i < len / 16; i++) { /* Load (unaligned) packed floats */ m0 = _mm_loadu_ps(&in[16 * i + 0]); m1 = _mm_loadu_ps(&in[16 * i + 4]); m2 = _mm_loadu_ps(&in[16 * i + 8]); m3 = _mm_loadu_ps(&in[16 * i + 12]); m4 = _mm_load1_ps(&scale); /* Scale */ m0 = _mm_mul_ps(m0, m4); m1 = _mm_mul_ps(m1, m4); m2 = _mm_mul_ps(m2, m4); m3 = _mm_mul_ps(m3, m4); /* Convert */ m5 = _mm_cvtps_epi32(m0); m6 = _mm_cvtps_epi32(m1); m7 = _mm_cvtps_epi32(m2); m8 = _mm_cvtps_epi32(m3); /* Pack and store */ m5 = _mm_packs_epi32(m5, m6); m7 = _mm_packs_epi32(m7, m8); _mm_storeu_si128((__m128i *) & out[16 * i + 0], m5); _mm_storeu_si128((__m128i *) & out[16 * i + 8], m7); } } #endif osmo-trx-0.4.0/Transceiver52M/arch/x86/Makefile.am0000644000175000017500000000135713272615542020477 0ustar rubenrubenAM_CFLAGS = -Wall -std=gnu99 -I${srcdir}/../common noinst_LTLIBRARIES = libarch.la noinst_LTLIBRARIES += libarch_sse_3.la noinst_LTLIBRARIES += libarch_sse_4_1.la noinst_HEADERS = \ convert_sse_3.h \ convert_sse_4_1.h \ convolve_sse_3.h libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la # SSE 3 specific code if HAVE_SSE3 libarch_sse_3_la_SOURCES = \ convert_sse_3.c \ convolve_sse_3.c libarch_sse_3_la_CFLAGS = $(AM_CFLAGS) -msse3 libarch_la_LIBADD += libarch_sse_3.la endif # SSE 4.1 specific code if HAVE_SSE4_1 libarch_sse_4_1_la_SOURCES = \ convert_sse_4_1.c libarch_sse_4_1_la_CFLAGS = $(AM_CFLAGS) -msse4.1 libarch_la_LIBADD += libarch_sse_4_1.la endif libarch_la_SOURCES = \ convert.c \ convolve.c osmo-trx-0.4.0/Transceiver52M/arch/x86/convert_sse_4_1.h0000644000175000017500000000217613272615542021611 0ustar rubenruben/* * SSE type conversions * Copyright (C) 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once /* 16*N 16-bit signed integer converted to single precision floats */ void _sse_convert_si16_ps_16n(float *restrict out, const short *restrict in, int len); /* 16*N 16-bit signed integer conversion with remainder */ void _sse_convert_si16_ps(float *restrict out, const short *restrict in, int len); osmo-trx-0.4.0/Transceiver52M/arch/x86/convolve_sse_3.h0000644000175000017500000000465313272615542021545 0ustar rubenruben/* * SSE Convolution * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once /* 4-tap SSE complex-real convolution */ void sse_conv_real4(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); /* 8-tap SSE complex-real convolution */ void sse_conv_real8(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); /* 12-tap SSE complex-real convolution */ void sse_conv_real12(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); /* 16-tap SSE complex-real convolution */ void sse_conv_real16(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); /* 20-tap SSE complex-real convolution */ void sse_conv_real20(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); /* 4*N-tap SSE complex-real convolution */ void sse_conv_real4n(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); /* 4*N-tap SSE complex-complex convolution */ void sse_conv_cmplx_4n(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); /* 8*N-tap SSE complex-complex convolution */ void sse_conv_cmplx_8n(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); osmo-trx-0.4.0/Transceiver52M/arch/x86/convert_sse_4_1.c0000644000175000017500000000431213272615542021576 0ustar rubenruben/* * SSE type conversions * Copyright (C) 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "convert_sse_4_1.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SSE4_1 #include /* 16*N 16-bit signed integer converted to single precision floats */ void _sse_convert_si16_ps_16n(float *restrict out, const short *restrict in, int len) { __m128i m0, m1, m2, m3, m4, m5; __m128 m6, m7, m8, m9; for (int i = 0; i < len / 16; i++) { /* Load (unaligned) packed floats */ m0 = _mm_loadu_si128((__m128i *) & in[16 * i + 0]); m1 = _mm_loadu_si128((__m128i *) & in[16 * i + 8]); /* Unpack */ m2 = _mm_cvtepi16_epi32(m0); m4 = _mm_cvtepi16_epi32(m1); m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2)); m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2)); m3 = _mm_cvtepi16_epi32(m0); m5 = _mm_cvtepi16_epi32(m1); /* Convert */ m6 = _mm_cvtepi32_ps(m2); m7 = _mm_cvtepi32_ps(m3); m8 = _mm_cvtepi32_ps(m4); m9 = _mm_cvtepi32_ps(m5); /* Store */ _mm_storeu_ps(&out[16 * i + 0], m6); _mm_storeu_ps(&out[16 * i + 4], m7); _mm_storeu_ps(&out[16 * i + 8], m8); _mm_storeu_ps(&out[16 * i + 12], m9); } } /* 16*N 16-bit signed integer conversion with remainder */ void _sse_convert_si16_ps(float *restrict out, const short *restrict in, int len) { int start = len / 16 * 16; _sse_convert_si16_ps_16n(out, in, len); for (int i = 0; i < len % 16; i++) out[start + i] = in[start + i]; } #endif osmo-trx-0.4.0/Transceiver52M/arch/x86/convert.c0000644000175000017500000000510613272615542020263 0ustar rubenruben/* * SSE type conversions * Copyright (C) 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "convert.h" #include "convert_sse_3.h" #include "convert_sse_4_1.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Architecture dependant function pointers */ struct convert_cpu_context { void (*convert_si16_ps_16n) (float *, const short *, int); void (*convert_si16_ps) (float *, const short *, int); void (*convert_scale_ps_si16_16n)(short *, const float *, float, int); void (*convert_scale_ps_si16_8n)(short *, const float *, float, int); void (*convert_scale_ps_si16)(short *, const float *, float, int); }; static struct convert_cpu_context c; void convert_init(void) { c.convert_scale_ps_si16_16n = base_convert_float_short; c.convert_scale_ps_si16_8n = base_convert_float_short; c.convert_scale_ps_si16 = base_convert_float_short; c.convert_si16_ps_16n = base_convert_short_float; c.convert_si16_ps = base_convert_short_float; #ifdef HAVE___BUILTIN_CPU_SUPPORTS #ifdef HAVE_SSE4_1 if (__builtin_cpu_supports("sse4.1")) { c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n; c.convert_si16_ps = &_sse_convert_si16_ps; } #endif #ifdef HAVE_SSE3 if (__builtin_cpu_supports("sse3")) { c.convert_scale_ps_si16_16n = _sse_convert_scale_ps_si16_16n; c.convert_scale_ps_si16_8n = _sse_convert_scale_ps_si16_8n; c.convert_scale_ps_si16 = _sse_convert_scale_ps_si16; } #endif #endif } void convert_float_short(short *out, const float *in, float scale, int len) { if (!(len % 16)) c.convert_scale_ps_si16_16n(out, in, scale, len); else if (!(len % 8)) c.convert_scale_ps_si16_8n(out, in, scale, len); else c.convert_scale_ps_si16(out, in, scale, len); } void convert_short_float(float *out, const short *in, int len) { if (!(len % 16)) c.convert_si16_ps_16n(out, in, len); else c.convert_si16_ps(out, in, len); } osmo-trx-0.4.0/Transceiver52M/arch/Makefile.am0000644000175000017500000000015513272615542020045 0ustar rubenrubeninclude $(top_srcdir)/Makefile.common SUBDIRS = common if ARCH_ARM SUBDIRS += arm else SUBDIRS += x86 endif osmo-trx-0.4.0/Transceiver52M/arch/arm/0000755000175000017500000000000013272615542016567 5ustar rubenrubenosmo-trx-0.4.0/Transceiver52M/arch/arm/convolve.c0000644000175000017500000000713413272615542020573 0ustar rubenruben/* * NEON Convolution * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Forward declarations from base implementation */ int _base_convolve_real(float *x, int x_len, float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); int _base_convolve_complex(float *x, int x_len, float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); int bounds_check(int x_len, int h_len, int y_len, int start, int len, int step); #ifdef HAVE_NEON /* Calls into NEON assembler */ void neon_conv_real4(float *x, float *h, float *y, int len); void neon_conv_real8(float *x, float *h, float *y, int len); void neon_conv_real12(float *x, float *h, float *y, int len); void neon_conv_real16(float *x, float *h, float *y, int len); void neon_conv_real20(float *x, float *h, float *y, int len); void mac_cx_neon4(float *x, float *h, float *y, int len); /* Complex-complex convolution */ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len) { for (int i = 0; i < len; i++) mac_cx_neon4(&x[2 * i], h, &y[2 * i], h_len >> 2); } #endif /* API: Initalize convolve module */ void convolve_init(void) { /* Stub */ return; } /* API: Aligned complex-real */ int convolve_real(float *x, int x_len, float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { void (*conv_func)(float *, float *, float *, int) = NULL; if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) return -1; memset(y, 0, len * 2 * sizeof(float)); #ifdef HAVE_NEON if (step <= 4) { switch (h_len) { case 4: conv_func = neon_conv_real4; break; case 8: conv_func = neon_conv_real8; break; case 12: conv_func = neon_conv_real12; break; case 16: conv_func = neon_conv_real16; break; case 20: conv_func = neon_conv_real20; break; } } #endif if (conv_func) { conv_func(&x[2 * (-(h_len - 1) + start)], h, y, len); } else { _base_convolve_real(x, x_len, h, h_len, y, y_len, start, len, step, offset); } return len; } /* API: Aligned complex-complex */ int convolve_complex(float *x, int x_len, float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { void (*conv_func)(float *, float *, float *, int, int) = NULL; if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) return -1; memset(y, 0, len * 2 * sizeof(float)); #ifdef HAVE_NEON if (step <= 4 && !(h_len % 4)) conv_func = neon_conv_cmplx_4n; #endif if (conv_func) { conv_func(&x[2 * (-(h_len - 1) + start)], h, y, h_len, len); } else { _base_convolve_complex(x, x_len, h, h_len, y, y_len, start, len, step, offset); } return len; } osmo-trx-0.4.0/Transceiver52M/arch/arm/mult.c0000644000175000017500000000272613272615542017723 0ustar rubenruben/* * NEON scaling * Copyright (C) 2012,2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif void neon_cmplx_mul_4n(float *, float *, float *, int); static void cmplx_mul_ps(float *out, float *a, float *b, int len) { float ai, aq, bi, bq; for (int i = 0; i < len; i++) { ai = a[2 * i + 0]; aq = a[2 * i + 1]; bi = b[2 * i + 0]; bq = b[2 * i + 1]; out[2 * i + 0] = ai * bi - aq * bq; out[2 * i + 1] = ai * bq + aq * bi; } } void mul_complex(float *out, float *a, float *b, int len) { #ifdef HAVE_NEON if (len % 4) cmplx_mul_ps(out, a, b, len); else neon_cmplx_mul_4n(out, a, b, len >> 2); #else cmplx_mul_ps(out, a, b, len); #endif } osmo-trx-0.4.0/Transceiver52M/arch/arm/mult_neon.S0000644000175000017500000000260513272615542020716 0ustar rubenruben/* * NEON complex multiplication * Copyright (C) 2012,2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ .syntax unified .text .align 2 .global neon_cmplx_mul_4n .type neon_cmplx_mul_4n, %function neon_cmplx_mul_4n: vpush {q4-q7} .loop_mul: vld2.32 {q0-q1}, [r1]! vld2.32 {q2-q3}, [r2]! vmul.f32 q4, q0, q2 vmul.f32 q5, q1, q3 vmul.f32 q6, q0, q3 vmul.f32 q7, q2, q1 vsub.f32 q8, q4, q5 vadd.f32 q9, q6, q7 vst2.32 {q8-q9}, [r0]! subs r3, #1 bne .loop_mul vpop {q4-q7} bx lr .size neon_cmplx_mul_4n, .-neon_cmplx_mul_4n .section .note.GNU-stack,"",%progbits osmo-trx-0.4.0/Transceiver52M/arch/arm/Makefile.am0000644000175000017500000000065513272615542020631 0ustar rubenrubenif ARCH_ARM_A15 ARCH_FLAGS = -mfpu=neon-vfpv4 else ARCH_FLAGS = -mfpu=neon endif AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common AM_CCASFLAGS = $(ARCH_FLAGS) noinst_LTLIBRARIES = libarch.la libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la libarch_la_SOURCES = \ convert.c \ convert_neon.S \ convolve.c \ convolve_neon.S \ scale.c \ scale_neon.S \ mult.c \ mult_neon.S osmo-trx-0.4.0/Transceiver52M/arch/arm/scale_neon.S0000644000175000017500000000301413272615542021017 0ustar rubenruben/* * ARM NEON Scaling * Copyright (C) 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ .syntax unified .text .align 2 .global neon_scale_4n .type neon_scale_4n, %function neon_scale_4n: push {r4, lr} ldr r4, =32 vld1.64 d0, [r1] vmov.32 s4, s1 vmov.32 s1, s0 vmov.64 d1, d0 vmov.32 s5, s4 vmov.64 d3, d2 .loop_mul_const: vld2.32 {q2-q3}, [r0], r4 vmul.f32 q8, q0, q2 vmul.f32 q9, q1, q3 vmul.f32 q10, q0, q3 vmul.f32 q11, q1, q2 vsub.f32 q8, q8, q9 vadd.f32 q9, q10, q11 vst2.32 {q8-q9}, [r2]! subs r3, #1 bne .loop_mul_const pop {r4, pc} .size neon_scale_4n, .-neon_scale_4n .section .note.GNU-stack,"",%progbits osmo-trx-0.4.0/Transceiver52M/arch/arm/scale.c0000644000175000017500000000272613272615542020031 0ustar rubenruben/* * NEON scaling * Copyright (C) 2012,2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif void neon_scale_4n(float *, float *, float *, int); static void scale_ps(float *out, float *in, float *scale, int len) { float ai, aq, bi, bq; bi = scale[0]; bq = scale[1]; for (int i = 0; i < len; i++) { ai = in[2 * i + 0]; aq = in[2 * i + 1]; out[2 * i + 0] = ai * bi - aq * bq; out[2 * i + 1] = ai * bq + aq * bi; } } void scale_complex(float *out, float *in, float* scale, int len) { #ifdef HAVE_NEON if (len % 4) scale_ps(out, in, scale, len); else neon_scale_4n(in, scale, out, len >> 2); #else scale_ps(out, in, scale, len); #endif } osmo-trx-0.4.0/Transceiver52M/arch/arm/convert_neon.S0000644000175000017500000000325413272615542021416 0ustar rubenruben/* * NEON type conversions * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ .syntax unified .text .align 2 .global neon_convert_ps_si16_4n .type neon_convert_ps_si16_4n, %function neon_convert_ps_si16_4n: vld1.32 {q1}, [r2] .loop_fltint: vld1.64 {d0-d1}, [r1]! vmul.f32 q0, q1 vcvt.s32.f32 q2, q0 vqmovn.s32 d0, q2 vst1.64 {d0}, [r0]! subs r3, #1 bne .loop_fltint bx lr .size neon_convert_ps_si16_4n, .-neon_convert_ps_si16_4n .text .align 2 .global neon_convert_si16_ps_4n .type neon_convert_si16_ps_4n, %function neon_convert_si16_ps_4n: .loop_intflt: vld1.64 {d0}, [r1]! vmovl.s16 q1, d0 vcvt.f32.s32 q0, q1 vst1.64 {q0}, [r0]! subs r2, #1 bne .loop_intflt bx lr .size neon_convert_si16_ps_4n, .-neon_convert_si16_ps_4n .section .note.GNU-stack,"",%progbits osmo-trx-0.4.0/Transceiver52M/arch/arm/convolve_neon.S0000644000175000017500000001675313272615542021601 0ustar rubenruben/* * NEON Convolution * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif .syntax unified .text .align 2 .global neon_conv_real4 .type neon_conv_real4, %function neon_conv_real4: push {r4, lr} vpush {q4-q7} vld2.32 {q0-q1}, [r1] ldr r4, =8 .neon_conv_loop4: vld2.32 {q2-q3}, [r0], r4 vmul.f32 q4, q2, q0 vmul.f32 q5, q3, q0 vpadd.f32 d12, d8, d9 vpadd.f32 d13, d10, d11 vpadd.f32 d14, d12, d13 vst1.64 {d14}, [r2]! subs r3, r3, #1 bne .neon_conv_loop4 vpop {q4-q7} pop {r4, pc} .size neon_conv_real4, .-neon_conv_real4 .align 2 .p2align 4,,15 .global neon_conv_real8 .type neon_conv_real8, %function neon_conv_real8: push {r4-r5, lr} vpush {q4-q7} vld2.32 {q0-q1}, [r1]! vld2.32 {q2-q3}, [r1] add r4, r0, #32 ldr r5, =8 .neon_conv_loop8: vld2.32 {q4-q5}, [r0], r5 vld2.32 {q6-q7}, [r4], r5 vmul.f32 q8, q4, q0 vmul.f32 q9, q5, q0 vmul.f32 q10, q6, q2 vmul.f32 q11, q7, q2 vadd.f32 q12, q8, q10 vadd.f32 q13, q9, q11 vpadd.f32 d22, d24, d25 vpadd.f32 d23, d26, d27 vpadd.f32 d24, d22, d23 vst1.64 {d24}, [r2]! subs r3, r3, #1 bne .neon_conv_loop8 vpop {q4-q7} pop {r4-r5, pc} .size neon_conv_real8, .-neon_conv_real8 .align 2 .global neon_conv_real12 .type neon_conv_real12, %function neon_conv_real12: push {r4-r6, lr} vpush {q4-q7} vld2.32 {q0-q1}, [r1]! vld2.32 {q2-q3}, [r1]! vld2.32 {q4-q5}, [r1]! add r4, r0, #32 add r5, r0, #64 ldr r6, =8 .neon_conv_loop12: vld2.32 {q6-q7}, [r0], r6 vld2.32 {q8-q9}, [r4], r6 vld2.32 {q10-q11}, [r5], r6 #ifdef HAVE_NEON_FMA vfma.f32 q1, q6, q0 vfma.f32 q3, q7, q0 vfma.f32 q1, q8, q2 vfma.f32 q3, q9, q2 vfma.f32 q1, q10, q4 vfma.f32 q3, q11, q4 #else vmul.f32 q12, q6, q0 vmul.f32 q13, q7, q0 vmul.f32 q14, q8, q2 vmul.f32 q15, q9, q2 vmul.f32 q1, q10, q4 vmul.f32 q3, q11, q4 vadd.f32 q5, q12, q14 vadd.f32 q6, q13, q15 vadd.f32 q1, q5, q1 vadd.f32 q3, q6, q3 #endif vpadd.f32 d2, d2, d3 vpadd.f32 d3, d6, d7 vpadd.f32 d6, d2, d3 vst1.64 {d6}, [r2]! subs r3, r3, #1 bne .neon_conv_loop12 vpop {q4-q7} pop {r4-r6, pc} .size neon_conv_real12, .-neon_conv_real12 .align 2 .global neon_conv_real16 .type neon_conv_real16, %function neon_conv_real16: push {r4-r7, lr} vpush {q4-q7} vld2.32 {q0-q1}, [r1]! vld2.32 {q2-q3}, [r1]! vld2.32 {q4-q5}, [r1]! vld2.32 {q6-q7}, [r1] add r4, r0, #32 add r5, r0, #64 add r6, r0, #96 ldr r7, =8 .neon_conv_loop16: vld2.32 {q8-q9}, [r0], r7 vld2.32 {q10-q11}, [r4], r7 vld2.32 {q12-q13}, [r5], r7 vld2.32 {q14-q15}, [r6], r7 #ifdef HAVE_NEON_FMA vmul.f32 q1, q8, q0 vmul.f32 q3, q9, q0 vfma.f32 q1, q10, q2 vfma.f32 q3, q11, q2 vfma.f32 q1, q12, q4 vfma.f32 q3, q13, q4 vfma.f32 q1, q14, q6 vfma.f32 q3, q15, q6 #else vmul.f32 q1, q8, q0 vmul.f32 q3, q9, q0 vmul.f32 q5, q10, q2 vmul.f32 q7, q11, q2 vmul.f32 q8, q12, q4 vmul.f32 q9, q13, q4 vmul.f32 q10, q14, q6 vmul.f32 q11, q15, q6 vadd.f32 q1, q1, q5 vadd.f32 q3, q3, q7 vadd.f32 q5, q8, q10 vadd.f32 q7, q9, q11 vadd.f32 q1, q1, q5 vadd.f32 q3, q3, q7 #endif vpadd.f32 d2, d2, d3 vpadd.f32 d3, d6, d7 vpadd.f32 d6, d2, d3 vst1.64 {d6}, [r2]! subs r3, r3, #1 bne .neon_conv_loop16 vpop {q4-q7} pop {r4-r7, pc} .size neon_conv_real16, .-neon_conv_real16 .align 2 .global neon_conv_real20 .type neon_conv_real20, %function neon_conv_real20: push {r4-r8, lr} vpush {q4-q7} vld2.32 {q0-q1}, [r1]! vld2.32 {q2-q3}, [r1]! vld2.32 {q4-q5}, [r1]! vld2.32 {q6-q7}, [r1]! vld2.32 {q8-q9}, [r1] add r4, r0, #32 add r5, r0, #64 add r6, r0, #96 add r7, r0, #128 ldr r8, =8 .neon_conv_loop20: vld2.32 {q10-q11}, [r0], r8 vld2.32 {q12-q13}, [r4], r8 vld2.32 {q14-q15}, [r5], r8 #ifdef HAVE_NEON_FMA vmul.f32 q1, q10, q0 vfma.f32 q1, q12, q2 vfma.f32 q1, q14, q4 vmul.f32 q3, q11, q0 vfma.f32 q3, q13, q2 vfma.f32 q3, q15, q4 vld2.32 {q12-q13}, [r6], r8 vld2.32 {q14-q15}, [r7], r8 vfma.f32 q1, q12, q6 vfma.f32 q3, q13, q6 vfma.f32 q1, q14, q8 vfma.f32 q3, q15, q8 #else vmul.f32 q1, q10, q0 vmul.f32 q3, q12, q2 vmul.f32 q5, q14, q4 vmul.f32 q7, q11, q0 vmul.f32 q9, q13, q2 vmul.f32 q10, q15, q4 vadd.f32 q1, q1, q3 vadd.f32 q3, q7, q9 vadd.f32 q9, q1, q5 vadd.f32 q10, q3, q10 vld2.32 {q12-q13}, [r6], r8 vld2.32 {q14-q15}, [r7], r8 vmul.f32 q1, q12, q6 vmul.f32 q3, q13, q6 vmul.f32 q5, q14, q8 vmul.f32 q7, q15, q8 vadd.f32 q12, q1, q9 vadd.f32 q14, q3, q10 vadd.f32 q1, q12, q5 vadd.f32 q3, q14, q7 #endif vpadd.f32 d2, d2, d3 vpadd.f32 d3, d6, d7 vpadd.f32 d6, d2, d3 vst1.64 {d6}, [r2]! subs r3, r3, #1 bne .neon_conv_loop20 vpop {q4-q7} pop {r4-r8, pc} .size neon_conv_real20, .-neon_conv_real20 .align 2 .global mac_cx_neon4 .type mac_cx_neon4, %function mac_cx_neon4: push {r4, lr} ldr r4, =32 veor q14, q14 veor q15, q15 .neon_conv_loop_mac4: vld2.32 {q0-q1}, [r0], r4 vld2.32 {q2-q3}, [r1]! vmul.f32 q10, q0, q2 vmul.f32 q11, q1, q3 vmul.f32 q12, q0, q3 vmul.f32 q13, q2, q1 vsub.f32 q8, q10, q11 vadd.f32 q9, q12, q13 vadd.f32 q14, q8 vadd.f32 q15, q9 subs r3, #1 bne .neon_conv_loop_mac4 vld1.64 d0, [r2] vpadd.f32 d28, d28, d29 vpadd.f32 d30, d30, d31 vpadd.f32 d1, d28, d30 vadd.f32 d1, d0 vst1.64 d1, [r2] pop {r4, pc} .size mac_cx_neon4, .-mac_cx_neon4 .section .note.GNU-stack,"",%progbits osmo-trx-0.4.0/Transceiver52M/arch/arm/convert.c0000644000175000017500000000437513272615542020424 0ustar rubenruben/* * NEON type conversions * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "convert.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif void neon_convert_ps_si16_4n(short *, const float *, const float *, int); void neon_convert_si16_ps_4n(float *, const short *, int); void convert_init(void) { } /* 4*N 16-bit signed integer conversion with remainder */ static void neon_convert_si16_ps(float *out, const short *in, int len) { int start = len / 4 * 4; neon_convert_si16_ps_4n(out, in, len >> 2); for (int i = 0; i < len % 4; i++) out[start + i] = (float) in[start + i]; } /* 4*N 16-bit signed integer conversion with remainder */ static void neon_convert_ps_si16(short *out, const float *in, const float *scale, int len) { int start = len / 4 * 4; neon_convert_ps_si16_4n(out, in, scale, len >> 2); for (int i = 0; i < len % 4; i++) out[start + i] = (short) (in[start + i] * (*scale)); } void convert_float_short(short *out, const float *in, float scale, int len) { #ifdef HAVE_NEON float q[4] = { scale, scale, scale, scale }; if (len % 4) neon_convert_ps_si16(out, in, q, len); else neon_convert_ps_si16_4n(out, in, q, len >> 2); #else base_convert_float_short(out, in, scale, len); #endif } void convert_short_float(float *out, const short *in, int len) { #ifdef HAVE_NEON if (len % 4) neon_convert_si16_ps(out, in, len); else neon_convert_si16_ps_4n(out, in, len >> 2); #else base_convert_short_float(out, in, len); #endif } osmo-trx-0.4.0/Transceiver52M/arch/common/0000755000175000017500000000000013272615542017300 5ustar rubenrubenosmo-trx-0.4.0/Transceiver52M/arch/common/convert.h0000644000175000017500000000061513272615542021133 0ustar rubenruben#ifndef _CONVERT_H_ #define _CONVERT_H_ void convert_float_short(short *out, const float *in, float scale, int len); void convert_short_float(float *out, const short *in, int len); void base_convert_float_short(short *out, const float *in, float scale, int len); void base_convert_short_float(float *out, const short *in, int len); void convert_init(void); #endif /* _CONVERT_H_ */ osmo-trx-0.4.0/Transceiver52M/arch/common/convert_base.c0000644000175000017500000000213213272615542022114 0ustar rubenruben/* * Conversion * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "convert.h" void base_convert_float_short(short *out, const float *in, float scale, int len) { for (int i = 0; i < len; i++) out[i] = in[i] * scale; } void base_convert_short_float(float *out, const short *in, int len) { for (int i = 0; i < len; i++) out[i] = in[i]; } osmo-trx-0.4.0/Transceiver52M/arch/common/convolve.h0000644000175000017500000000143213272615542021304 0ustar rubenruben#ifndef _CONVOLVE_H_ #define _CONVOLVE_H_ void *convolve_h_alloc(int num); int convolve_real(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); int convolve_complex(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); int base_convolve_real(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); int base_convolve_complex(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset); void convolve_init(void); #endif /* _CONVOLVE_H_ */ osmo-trx-0.4.0/Transceiver52M/arch/common/scale.h0000644000175000017500000000017713272615542020545 0ustar rubenruben#ifndef _SCALE_H_ #define _SCALE_H_ void scale_complex(float *out, float *in, float *scale, int len); #endif /* _SCALE_H_ */ osmo-trx-0.4.0/Transceiver52M/arch/common/Makefile.am0000644000175000017500000000042313272615542021333 0ustar rubenrubenAM_CFLAGS = -Wall -std=gnu99 noinst_LTLIBRARIES = libarch_common.la noinst_HEADERS = \ convolve.h \ convert.h \ scale.h \ mult.h \ fft.h libarch_common_la_SOURCES = \ convolve_base.c \ convert_base.c \ fft.c osmo-trx-0.4.0/Transceiver52M/arch/common/convolve_base.c0000644000175000017500000001001213272615542022263 0ustar rubenruben/* * Convolution * Copyright (C) 2012, 2013 Thomas Tsou * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Base multiply and accumulate complex-real */ static void mac_real(const float *x, const float *h, float *y) { y[0] += x[0] * h[0]; y[1] += x[1] * h[0]; } /* Base multiply and accumulate complex-complex */ static void mac_cmplx(const float *x, const float *h, float *y) { y[0] += x[0] * h[0] - x[1] * h[1]; y[1] += x[0] * h[1] + x[1] * h[0]; } /* Base vector complex-complex multiply and accumulate */ static void mac_real_vec_n(const float *x, const float *h, float *y, int len, int step, int offset) { for (int i = offset; i < len; i += step) mac_real(&x[2 * i], &h[2 * i], y); } /* Base vector complex-complex multiply and accumulate */ static void mac_cmplx_vec_n(const float *x, const float *h, float *y, int len, int step, int offset) { for (int i = offset; i < len; i += step) mac_cmplx(&x[2 * i], &h[2 * i], y); } /* Base complex-real convolution */ int _base_convolve_real(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { for (int i = 0; i < len; i++) { mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)], h, &y[2 * i], h_len, step, offset); } return len; } /* Base complex-complex convolution */ int _base_convolve_complex(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { for (int i = 0; i < len; i++) { mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)], h, &y[2 * i], h_len, step, offset); } return len; } /* Buffer validity checks */ int bounds_check(int x_len, int h_len, int y_len, int start, int len, int step) { if ((x_len < 1) || (h_len < 1) || (y_len < 1) || (len < 1) || (step < 1)) { fprintf(stderr, "Convolve: Invalid input\n"); return -1; } if ((start + len > x_len) || (len > y_len) || (x_len < h_len)) { fprintf(stderr, "Convolve: Boundary exception\n"); fprintf(stderr, "start: %i, len: %i, x: %i, h: %i, y: %i\n", start, len, x_len, h_len, y_len); return -1; } return 0; } /* API: Non-aligned (no SSE) complex-real */ int base_convolve_real(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) return -1; memset(y, 0, len * 2 * sizeof(float)); return _base_convolve_real(x, x_len, h, h_len, y, y_len, start, len, step, offset); } /* API: Non-aligned (no SSE) complex-complex */ int base_convolve_complex(const float *x, int x_len, const float *h, int h_len, float *y, int y_len, int start, int len, int step, int offset) { if (bounds_check(x_len, h_len, y_len, start, len, step) < 0) return -1; memset(y, 0, len * 2 * sizeof(float)); return _base_convolve_complex(x, x_len, h, h_len, y, y_len, start, len, step, offset); } /* Aligned filter tap allocation */ void *convolve_h_alloc(int len) { #ifdef HAVE_SSE3 return memalign(16, len * 2 * sizeof(float)); #else return malloc(len * 2 * sizeof(float)); #endif } osmo-trx-0.4.0/Transceiver52M/arch/common/fft.c0000644000175000017500000000557413272615542020236 0ustar rubenruben/* * Fast Fourier transform * * Copyright (C) 2012 Tom Tsou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, see . * See the COPYING file in the main directory for details. */ #include #include #include #include #include "fft.h" struct fft_hdl { float *fft_in; float *fft_out; int len; fftwf_plan fft_plan; }; /*! \brief Initialize FFT backend * \param[in] reverse FFT direction * \param[in] m FFT length * \param[in] istride input stride count * \param[in] ostride output stride count * \param[in] in input buffer (FFTW aligned) * \param[in] out output buffer (FFTW aligned) * \param[in] ooffset initial offset into output buffer * * If the reverse is non-NULL, then an inverse FFT will be used. This is a * wrapper for advanced non-contiguous FFTW usage. See FFTW documentation for * further details. * * http://www.fftw.org/doc/Advanced-Complex-DFTs.html * * It is currently unknown how the offset of the output buffer affects FFTW * memory alignment. */ struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride, float *in, float *out, int ooffset) { int rank = 1; int n[] = { m }; int howmany = istride; int idist = 1; int odist = 1; int *inembed = n; int *onembed = n; fftwf_complex *obuffer, *ibuffer; struct fft_hdl *hdl = (struct fft_hdl *) malloc(sizeof(struct fft_hdl)); if (!hdl) return NULL; int direction = FFTW_FORWARD; if (reverse) direction = FFTW_BACKWARD; ibuffer = (fftwf_complex *) in; obuffer = (fftwf_complex *) out + ooffset; hdl->fft_in = in; hdl->fft_out = out; hdl->fft_plan = fftwf_plan_many_dft(rank, n, howmany, ibuffer, inembed, istride, idist, obuffer, onembed, ostride, odist, direction, FFTW_MEASURE); return hdl; } void *fft_malloc(size_t size) { return fftwf_malloc(size); } void fft_free(void *ptr) { free(ptr); } /*! \brief Free FFT backend resources */ void free_fft(struct fft_hdl *hdl) { fftwf_destroy_plan(hdl->fft_plan); free(hdl); } /*! \brief Run multiple DFT operations with the initialized plan * \param[in] hdl handle to an intitialized fft struct * * Input and output buffers are configured with init_fft(). */ int cxvec_fft(struct fft_hdl *hdl) { fftwf_execute(hdl->fft_plan); return 0; } osmo-trx-0.4.0/Transceiver52M/arch/common/mult.h0000644000175000017500000000016513272615542020434 0ustar rubenruben#ifndef _MULT_H_ #define _MULT_H_ void mul_complex(float *out, float *a, float *b, int len); #endif /* _MULT_H_ */ osmo-trx-0.4.0/Transceiver52M/arch/common/fft.h0000644000175000017500000000047113272615542020232 0ustar rubenruben#ifndef _FFT_H_ #define _FFT_H_ struct fft_hdl; struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride, float *in, float *out, int ooffset); void *fft_malloc(size_t size); void fft_free(void *ptr); void free_fft(struct fft_hdl *hdl); int cxvec_fft(struct fft_hdl *hdl); #endif /* _FFT_H_ */ osmo-trx-0.4.0/Transceiver52M/inband-signaling-usb0000644000175000017500000003036113272615542021014 0ustar rubenrubenThis file specifies the format of USB packets used for in-band data transmission and signaling on the USRP. All packets are 512-byte long, and are transfered using USB "bulk" transfers. IN packets are sent towards the host. OUT packets are sent away from the host. The layout is 32-bits wide. All data is transmitted in little-endian format across the USB. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |O|U|D|S|E| RSSI | Chan | mbz | Tag | Payload Len | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | Payload | . . . . . . | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ... | . +-+-+-+-+-+-+-+ . . . . Padding . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ mbz Must be Zero: these bits must be zero in both IN and OUT packets. O Overrun Flag: set in an IN packet if an overrun condition was detected. Must be zero in OUT packets. Overrun occurs when the FPGA has data to transmit to the host and there is no buffer space available. This generally indicates a problem on the host. Either it is not keeping up, or it has configured the FPGA to transmit data at a higher rate than the transport (USB) can support. U Underrun Flag: set in an IN packet if an underrun condition was detected. Must be zero in OUT packets. Underrun occurs when the FPGA runs out of samples, and it's not between bursts. See the "End of Burst flag" below. D Dropped Packet Flag: Set in an IN packet if the FPGA discarded an OUT packet because its timestamp had already passed. S Start of Burst Flag: Set in an OUT packet if the data is the first segment of what is logically a continuous burst of data. Must be zero in IN packets. E End of Burst Flag: Set in an OUT packet if the data is the last segment of what is logically a continuous burst of data. Must be zero in IN packets. Underruns are not reported when the FPGA runs out of samples between bursts. RSSI 6-bit Received Strength Signal Indicator: Must be zero in OUT packets. In IN packets, indicates RSSI as reported by front end. FIXME The format and interpretation are to be determined. Chan 5-bit logical channel number. Channel number 0x1f is reserved for control information. See "Control Channel" below. Other channels are "data channels." Each data channel is logically independent of the others. A data channel payload field contains a sequence of homogeneous samples. The format of the samples is determined by the configuration associated with the given channel. It is often the case that the payload field contains 32-bit complex samples, each containing 16-bit real and imaginary components. Tag 4-bit tag for matching IN packets with OUT packets. [FIXME, write more...] Payload Len: 9-bit field that specifies the length of the payload field in bytes. Must be in the range 0 to 504 inclusive. Timestamp: 32-bit timestamp. On IN packets, the timestamp indicates the time at which the first sample of the packet was produced by the A/D converter(s) for that channel. On OUT packets, the timestamp specifies the time at which the first sample in the packet should go out the D/A converter(s) for that channel. If a packet reaches the head of the transmit queue, and the current time is later than the timestamp, an error is assumed to have occurred and the packet is discarded. As a special case, the timestamp 0xffffffff is interpreted as "Now". The time base is a free running 32-bit counter that is incremented by the A/D sample-clock. Payload: Variable length field. Length is specified by the Payload Len field. Padding: This field is 504 - Payload Len bytes long, and its content is unspecified. This field pads the packet out to a constant 512 bytes. "Data Channel" payload format: ------------------------------- If Chan != 0x1f, the packet is a "data packet" and the payload is a sequence of homogeneous samples. The format of the samples is determined by the configuration associated with the given channel. It is often the case that the payload field contains 32-bit complex samples, each containing 16-bit real and imaginary components. "Control Channel" payload format: --------------------------------- If Chan == 0x1f, the packet is a "control packet". The control channel payload consists of a sequence of 0 or more sub-packets. Each sub-packet starts on a 32-bit boundary, and consists of an 8-bit Opcode field, an 8-bit Length field, Length bytes of arguments, and 0, 1, 2 or 3 bytes of padding to align the tail of the sub-packet to a 32-bit boundary. Control channel packets shall be processed at the head of the queue, and shall observe the timestamp semantics described above. General sub-packet format: -------------------------- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+ | Opcode | Length | ... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+ Specific sub-packet formats: ---------------------------- RID: 6-bit Request-ID. Copied from request sub-packet into corresponding reply sub-packet. RID allows the host to match requests and replies. Reg Number: 10-bit Register Number. Ping Fixed Length: Opcode: OP_PING_FIXED +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 2 | RID | Ping Value | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Ping Fixed Length Reply: Opcode: OP_PING_FIXED_REPLY +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 2 | RID | Ping Value | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Write Register: Opcode: OP_WRITE_REG +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 6 | mbz | Reg Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Register Value | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Write Register Masked: Opcode: OP_WRITE_REG_MASKED REG[Num] = (REG[Num] & ~Mask) | (Value & Mask) That is, only the register bits that correspond to 1's in the mask are written with the new value. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 10 | mbz | Reg Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Register Value | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Mask Value | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Read Register: Opcode: OP_READ_REG +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 2 | RID | Reg Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Read Register Reply: Opcode: OP_READ_REG_REPLY +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 6 | RID | Reg Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Register Value | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ I2C Write: Opcode: OP_I2C_WRITE I2C Addr: 7-bit I2C address Data: The bytes to write to the I2C bus Length: Length of Data + 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | Length | mbz | I2C Addr | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data ... . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ I2C Read: Opcode: OP_I2C_READ I2C Addr: 7-bit I2C address Nbytes: Number of bytes to read from I2C bus +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 3 | RID | mbz | I2C Addr | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Nbytes | unspecified padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ I2C Read Reply: Opcode: OP_I2C_READ_REPLY I2C Addr: 7-bit I2C address Data: Length - 2 bytes of data read from I2C bus. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | Length | RID | mbz | I2C Addr | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data ... . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ SPI Write: Opcode: OP_SPI_WRITE Enables: Which SPI enables to assert (mask) Format: Specifies format of SPI data and Opt Header Bytes Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format Data: The bytes to write to the SPI bus Length: Length of Data + 6 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | Length | mbz | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Enables | Format | Opt Header Bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data ... . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ SPI Read: Opcode: OP_SPI_READ Enables: Which SPI enables to assert (mask) Format: Specifies format of SPI data and Opt Header Bytes Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format Nbytes: Number of bytes to read from SPI bus. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 7 | RID | mbz | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Enables | Format | Opt Header Bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Nbytes | unspecified padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ SPI Read Reply: Opcode: OP_SPI_READ_REPLY Data: Length - 2 bytes of data read from SPI bus. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | Length | RID | mbz | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data ... . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Delay: Opcode: OP_DELAY Ticks: 16-bit unsigned delay count Delay Ticks clock ticks before executing next operation. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opcode | 2 | Ticks | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ osmo-trx-0.4.0/Transceiver52M/ChannelizerBase.h0000644000175000017500000000151313272615542020301 0ustar rubenruben#ifndef _CHANNELIZER_BASE_H_ #define _CHANNELIZER_BASE_H_ class ChannelizerBase { protected: ChannelizerBase(size_t m, size_t blockLen, size_t hLen); ~ChannelizerBase(); /* Channelizer parameters */ size_t m; size_t hLen; size_t blockLen; /* Channelizer filterbank sub-filters */ float **subFilters; /* Input/Output buffers */ float **hInputs, **hOutputs, **hist; float *fftInput, *fftOutput; /* Pointer to opaque FFT instance */ struct fft_hdl *fftHandle; /* Initializer internals */ bool initFilters(); bool initFFT(); void releaseFilters(); /* Map overlapped FFT and filter I/O buffers */ bool mapBuffers(); /* Buffer length validity checking */ bool checkLen(size_t innerLen, size_t outerLen); public: /* Initilize channelizer/synthesis filter internals */ bool init(); }; #endif /* _CHANNELIZER_BASE_H_ */ osmo-trx-0.4.0/Transceiver52M/radioInterfaceResamp.cpp0000644000175000017500000001404313272615542021670 0ustar rubenruben/* * Radio device interface with sample rate conversion * * Copyright (C) 2011-2014 Free Software Foundation, Inc. * Copyright (C) 2015 Ettus Research LLC * * Author: Tom Tsou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include #include #include "Resampler.h" extern "C" { #include "convert.h" } /* Resampling parameters for 64 MHz clocking */ #define RESAMP_64M_INRATE 65 #define RESAMP_64M_OUTRATE 96 /* Resampling parameters for 100 MHz clocking */ #define RESAMP_100M_INRATE 52 #define RESAMP_100M_OUTRATE 75 /* Universal resampling parameters */ #define NUMCHUNKS 24 /* * Resampling filter bandwidth scaling factor * This narrows the filter cutoff relative to the output bandwidth * of the polyphase resampler. At 4 samples-per-symbol using the * 2 pulse Laurent GMSK approximation gives us below 0.5 degrees * RMS phase error at the resampler output. */ #define RESAMP_TX4_FILTER 0.45 static Resampler *upsampler = NULL; static Resampler *dnsampler = NULL; static size_t resamp_inrate = 0; static size_t resamp_inchunk = 0; static size_t resamp_outrate = 0; static size_t resamp_outchunk = 0; RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio, size_t tx_sps, size_t rx_sps) : RadioInterface(wRadio, tx_sps, rx_sps, 1), outerSendBuffer(NULL), outerRecvBuffer(NULL) { } RadioInterfaceResamp::~RadioInterfaceResamp() { close(); } void RadioInterfaceResamp::close() { delete outerSendBuffer; delete outerRecvBuffer; delete upsampler; delete dnsampler; outerSendBuffer = NULL; outerRecvBuffer = NULL; upsampler = NULL; dnsampler = NULL; if (sendBuffer.size()) sendBuffer[0] = NULL; if (recvBuffer.size()) recvBuffer[0] = NULL; RadioInterface::close(); } /* Initialize I/O specific objects */ bool RadioInterfaceResamp::init(int type) { float cutoff = 1.0f; close(); sendBuffer.resize(1); recvBuffer.resize(1); convertSendBuffer.resize(1); convertRecvBuffer.resize(1); mReceiveFIFO.resize(1); powerScaling.resize(1); switch (type) { case RadioDevice::RESAMP_64M: resamp_inrate = RESAMP_64M_INRATE; resamp_outrate = RESAMP_64M_OUTRATE; break; case RadioDevice::RESAMP_100M: resamp_inrate = RESAMP_100M_INRATE; resamp_outrate = RESAMP_100M_OUTRATE; break; case RadioDevice::NORMAL: default: LOG(ALERT) << "Invalid device configuration"; return false; } resamp_inchunk = resamp_inrate * 4 * mSPSRx; resamp_outchunk = resamp_outrate * 4 * mSPSRx; if (mSPSTx == 4) cutoff = RESAMP_TX4_FILTER; dnsampler = new Resampler(resamp_inrate, resamp_outrate); if (!dnsampler->init()) { LOG(ALERT) << "Rx resampler failed to initialize"; return false; } upsampler = new Resampler(resamp_outrate, resamp_inrate); if (!upsampler->init(cutoff)) { LOG(ALERT) << "Tx resampler failed to initialize"; return false; } /* * Allocate high and low rate buffers. The high rate receive * buffer and low rate transmit vectors feed into the resampler * and requires headroom equivalent to the filter length. Low * rate buffers are allocated in the main radio interface code. */ sendBuffer[0] = new RadioBuffer(NUMCHUNKS, resamp_inchunk, upsampler->len(), true); recvBuffer[0] = new RadioBuffer(NUMCHUNKS * 20, resamp_inchunk, 0, false); outerSendBuffer = new signalVector(NUMCHUNKS * resamp_outchunk); outerRecvBuffer = new signalVector(resamp_outchunk, dnsampler->len()); convertSendBuffer[0] = new short[outerSendBuffer->size() * 2]; convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2]; return true; } /* Receive a timestamped chunk from the device */ void RadioInterfaceResamp::pullBuffer() { bool local_underrun; int rc, num_recv; if (recvBuffer[0]->getFreeSegments() <= 0) return; /* Outer buffer access size is fixed */ num_recv = mRadio->readSamples(convertRecvBuffer, resamp_outchunk, &overrun, readTimestamp, &local_underrun); if (num_recv != (int) resamp_outchunk) { LOG(ALERT) << "Receive error " << num_recv; return; } convert_short_float((float *) outerRecvBuffer->begin(), convertRecvBuffer[0], 2 * resamp_outchunk); underrun |= local_underrun; readTimestamp += (TIMESTAMP) resamp_outchunk; /* Write to the end of the inner receive buffer */ rc = dnsampler->rotate((float *) outerRecvBuffer->begin(), resamp_outchunk, recvBuffer[0]->getWriteSegment(), resamp_inchunk); if (rc < 0) { LOG(ALERT) << "Sample rate upsampling error"; } /* Set history for the next chunk */ outerRecvBuffer->updateHistory(); } /* Send a timestamped chunk to the device */ bool RadioInterfaceResamp::pushBuffer() { int rc; size_t numSent; if (sendBuffer[0]->getAvailSegments() <= 0) return false; /* Always send from the beginning of the buffer */ rc = upsampler->rotate(sendBuffer[0]->getReadSegment(), resamp_inchunk, (float *) outerSendBuffer->begin(), resamp_outchunk); if (rc < 0) { LOG(ALERT) << "Sample rate downsampling error"; } convert_float_short(convertSendBuffer[0], (float *) outerSendBuffer->begin(), powerScaling[0], 2 * resamp_outchunk); numSent = mRadio->writeSamples(convertSendBuffer, resamp_outchunk, &underrun, writeTimestamp); if (numSent != resamp_outchunk) { LOG(ALERT) << "Transmit error " << numSent; } writeTimestamp += resamp_outchunk; return true; } osmo-trx-0.4.0/Transceiver52M/radioVector.h0000644000175000017500000000372313272615542017532 0ustar rubenruben/* * Written by Thomas Tsou * Based on code by Harvind S Samra * * Copyright 2011 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #ifndef RADIOVECTOR_H #define RADIOVECTOR_H #include "sigProcLib.h" #include "GSMCommon.h" #include "Interthread.h" class radioVector { public: radioVector(GSM::Time& wTime, size_t size = 0, size_t start = 0, size_t chans = 1); radioVector(GSM::Time& wTime, signalVector *vector); ~radioVector(); GSM::Time getTime() const; void setTime(const GSM::Time& wTime); bool operator>(const radioVector& other) const; signalVector *getVector(size_t chan = 0) const; bool setVector(signalVector *vector, size_t chan = 0); size_t chans() const { return vectors.size(); } private: std::vector vectors; GSM::Time mTime; }; class noiseVector : std::vector { public: noiseVector(size_t size = 0); bool insert(float val); float avg() const; private: size_t itr; }; class VectorFIFO : public InterthreadQueue { }; class VectorQueue : public InterthreadPriorityQueue { public: GSM::Time nextTime() const; radioVector* getStaleBurst(const GSM::Time& targTime); radioVector* getCurrentBurst(const GSM::Time& targTime); }; #endif /* RADIOVECTOR_H */ osmo-trx-0.4.0/Transceiver52M/Channelizer.h0000644000175000017500000000203013272615542017501 0ustar rubenruben#ifndef _CHANNELIZER_RX_H_ #define _CHANNELIZER_RX_H_ #include "ChannelizerBase.h" class Channelizer : public ChannelizerBase { public: /** Constructor for channelizing filter bank @param m number of physical channels @param blockLen number of samples per output of each iteration @param hLen number of taps in each constituent filter path */ Channelizer(size_t m, size_t blockLen, size_t hLen = 16); ~Channelizer(); /* Return required input and output buffer lengths */ size_t inputLen() const; size_t outputLen() const; /** Rotate "input commutator" and drive samples through filterbank @param in complex input vector @param iLen number of samples in buffer (must match block length) @return false on error and true otherwise */ bool rotate(const float *in, size_t iLen); /** Get buffer for an output path @param chan channel number of filterbank @return NULL on error and pointer to buffer otherwise */ float *outputBuffer(size_t chan) const; }; #endif /* _CHANNELIZER_RX_H_ */ osmo-trx-0.4.0/Transceiver52M/radioVector.cpp0000644000175000017500000000572513272615542020071 0ustar rubenruben/* * Written by Thomas Tsou * Based on code by Harvind S Samra * * Copyright 2011 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * See the COPYING file in the main directory for details. */ #include "radioVector.h" radioVector::radioVector(GSM::Time &time, size_t size, size_t start, size_t chans) : vectors(chans), mTime(time) { for (size_t i = 0; i < vectors.size(); i++) vectors[i] = new signalVector(size, start); } radioVector::radioVector(GSM::Time& wTime, signalVector *vector) : vectors(1), mTime(wTime) { vectors[0] = vector; } radioVector::~radioVector() { for (size_t i = 0; i < vectors.size(); i++) delete vectors[i]; } GSM::Time radioVector::getTime() const { return mTime; } void radioVector::setTime(const GSM::Time& wTime) { mTime = wTime; } bool radioVector::operator>(const radioVector& other) const { return mTime > other.mTime; } signalVector *radioVector::getVector(size_t chan) const { if (chan >= vectors.size()) return NULL; return vectors[chan]; } bool radioVector::setVector(signalVector *vector, size_t chan) { if (chan >= vectors.size()) return false; vectors[chan] = vector; return true; } noiseVector::noiseVector(size_t size) : std::vector(size), itr(0) { } float noiseVector::avg() const { float val = 0.0; for (size_t i = 0; i < size(); i++) val += (*this)[i]; return val / (float) size(); } bool noiseVector::insert(float val) { if (!size()) return false; if (itr >= this->size()) itr = 0; (*this)[itr++] = val; return true; } GSM::Time VectorQueue::nextTime() const { GSM::Time retVal; mLock.lock(); while (mQ.size()==0) mWriteSignal.wait(mLock); retVal = mQ.top()->getTime(); mLock.unlock(); return retVal; } radioVector* VectorQueue::getStaleBurst(const GSM::Time& targTime) { mLock.lock(); if ((mQ.size()==0)) { mLock.unlock(); return NULL; } if (mQ.top()->getTime() < targTime) { radioVector* retVal = mQ.top(); mQ.pop(); mLock.unlock(); return retVal; } mLock.unlock(); return NULL; } radioVector* VectorQueue::getCurrentBurst(const GSM::Time& targTime) { mLock.lock(); if ((mQ.size()==0)) { mLock.unlock(); return NULL; } if (mQ.top()->getTime() == targTime) { radioVector* retVal = mQ.top(); mQ.pop(); mLock.unlock(); return retVal; } mLock.unlock(); return NULL; } osmo-trx-0.4.0/Transceiver52M/laurent.m0000644000175000017500000000413613272615542016727 0ustar rubenruben% % Laurent decomposition of GMSK signals % Generates C0, C1, and C2 pulse shapes % % Pierre Laurent, "Exact and Approximate Construction of Digital Phase % Modulations by Superposition of Amplitude Modulated Pulses", IEEE % Transactions of Communications, Vol. 34, No. 2, Feb 1986. % % Author: Thomas Tsou % % Modulation parameters oversamp = 16; L = 3; f = 270.83333e3; T = 1/f; h = 0.5; BT = 0.30; B = BT / T; % Generate sampling points for L symbol periods t = -(L*T/2):T/oversamp:(L*T/2); t = t(1:end-1) + (T/oversamp/2); % Generate Gaussian pulse g = qfunc(2*pi*B*(t - T/2)/(log(2)^.5)) - qfunc(2*pi*B*(t + T/2)/(log(2)^.5)); g = g / sum(g) * pi/2; g = [0 g]; % Integrate phase q = 0; for i = 1:size(g,2); q(i) = sum(g(1:i)); end % Compute two sided "generalized phase pulse" function s = 0; for i = 1:size(g,2); s(i) = sin(q(i)) / sin(pi*h); end for i = (size(g,2) + 1):(2 * size(g,2) - 1); s(i) = sin(pi*h - q(i - (size(g,2) - 1))) / sin(pi*h); end % Compute C0 pulse: valid for all L values c0 = s(1:end-(oversamp*(L-1))); for i = 1:L-1; c0 = c0 .* s((1 + i*oversamp):end-(oversamp*(L - 1 - i))); end % Compute C1 pulse: valid for L = 3 only! % C1 = S0 * S4 * S2 c1 = s(1:end-(oversamp*(4))); c1 = c1 .* s((1 + 4*oversamp):end-(oversamp*(4 - 1 - 3))); c1 = c1 .* s((1 + 2*oversamp):end-(oversamp*(4 - 1 - 1))); % Compute C2 pulse: valid for L = 3 only! % C2 = S0 * S1 * S5 c2 = s(1:end-(oversamp*(5))); c2 = c2 .* s((1 + 1*oversamp):end-(oversamp*(5 - 1 - 0))); c2 = c2 .* s((1 + 5*oversamp):end-(oversamp*(5 - 1 - 4))); % Plot C0, C1, C2 Laurent pulse series figure(1); hold off; plot((0:size(c0,2)-1)/oversamp - 2,c0, 'b'); hold on; plot((0:size(c1,2)-1)/oversamp - 2,c1, 'r'); plot((0:size(c2,2)-1)/oversamp - 2,c2, 'g'); % Generate OpenBTS pulse numSamples = size(c0,2); centerPoint = (numSamples - 1)/2; i = ((0:numSamples) - centerPoint) / oversamp; xP = .96*exp(-1.1380*i.^2 - 0.527*i.^4); xP = xP / max(xP) * max(c0); % Plot C0 pulse compared to OpenBTS pulse figure(2); hold off; plot((0:size(c0,2)-1)/oversamp, c0, 'b'); hold on; plot((0:size(xP,2)-1)/oversamp, xP, 'r'); osmo-trx-0.4.0/contrib/0000755000175000017500000000000013272615542013762 5ustar rubenrubenosmo-trx-0.4.0/contrib/jenkins.sh0000755000175000017500000000723113272615542015765 0ustar rubenruben#!/bin/sh set -ex substr() { [ -z "${2##*$1*}" ]; } #apt-get install qemu qemu-user-static qemu-system-arm debootstrap fakeroot proot mychroot_nocwd() { # LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container # PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH. # PROOT_NO_SECCOMP is requried due to proot bug #106 LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@" } mychroot() { mychroot_nocwd -w / "$@" } base="$PWD" deps="$base/deps" inst="$deps/install" export deps inst if [ -z "${INSIDE_CHROOT}" ]; then osmo-clean-workspace.sh # Only use ARM chroot if host is not ARM and the target is ARM: if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then OSMOTRX_DIR="$PWD" # we assume we are called as contrib/jenkins.sh ROOTFS_PREFIX="${ROOTFS_PREFIX:-$HOME}" ROOTFS="${ROOTFS_PREFIX}/qemu-img" mkdir -p "${ROOTFS_PREFIX}" # Prepare chroot: if [ ! -d "$ROOTFS" ]; then mkdir -p "$ROOTFS" if [ "x${USE_DEBOOTSTRAP}" = "x1" ]; then fakeroot qemu-debootstrap --foreign --include="linux-image-armmp-lpae" --arch=armhf stretch "$ROOTFS" http://ftp.de.debian.org/debian/ # Hack to avoid debootstrap trying to mount /proc, as it will fail with "no permissions" and anyway proot takes care of it: sed -i "s/setup_proc//g" "$ROOTFS/debootstrap/suite-script" mychroot /debootstrap/debootstrap --second-stage --verbose http://ftp.de.debian.org/debian/ else YESTERDAY=$(python -c 'import datetime ; print((datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y%m%d"))') wget -nc -q "https://uk.images.linuxcontainers.org/images/debian/stretch/armhf/default/${YESTERDAY}_22:42/rootfs.tar.xz" tar -xf rootfs.tar.xz -C "$ROOTFS/" || true echo "nameserver 8.8.8.8" > "$ROOTFS/etc/resolv.conf" fi mychroot -b /dev apt-get update mychroot apt-get -y install build-essential dh-autoreconf pkg-config libuhd-dev libusb-1.0-0-dev libusb-dev git libtalloc-dev libgnutls28-dev stow fi # Run jenkins.sh inside the chroot: INSIDE_CHROOT=1 mychroot_nocwd \ -w /osmo-trx \ -b "$OSMOTRX_DIR:/osmo-trx" \ -b "$(which osmo-clean-workspace.sh):/usr/bin/osmo-clean-workspace.sh" \ -b "$(which osmo-build-dep.sh):/usr/bin/osmo-build-dep.sh" \ -b "$(which osmo-deps.sh):/usr/bin/osmo-deps.sh" \ ./contrib/jenkins.sh exit 0 fi fi mkdir "$deps" || true osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc" osmo-build-dep.sh libusrp export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export LD_LIBRARY_PATH="$inst/lib" set +x echo echo echo echo " =============================== osmo-trx ===============================" echo set -x cd "$base" autoreconf --install --force ./configure --enable-sanitize --with-uhd --with-usrp1 $INSTR $MAKE $PARALLEL_MAKE $MAKE check \ || cat-testlogs.sh osmo-clean-workspace.sh osmo-trx-0.4.0/INSTALLATION0000644000175000017500000000077013272615542014152 0ustar rubenrubenInstallation Requirements osmo-trx compiles to a simple Unix binary and does not require special installation. One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to running configure. To run osmo-trx, the following should be installed: libuhd (https://gnuradio.org). This is part of the GNURadio installation. For information on specific executables, see tests/README.tests and apps/README.apps. See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more information. osmo-trx-0.4.0/AUTHORS0000644000175000017500000000675113272615542013403 0ustar rubenruben# # Copyright 2008, 2009 Free Software Foundation, Inc. # # This file is part of GNU Radio # # GNU Radio is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. # # GNU Radio is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # David A. Burgess, dburgess@kestrelsp.com: CLI/CLI.cpp CLI/CLI.h CommonLibs/Assert.h CommonLibs/BitVector.cpp CommonLibs/Interthread.h CommonLibs/LinkedLists.cpp CommonLibs/LinkedLists.h CommonLibs/Regexp.h CommonLibs/Sockets.cpp CommonLibs/Sockets.h CommonLibs/Threads.cpp CommonLibs/Threads.h CommonLibs/Timeval.cpp CommonLibs/Timeval.h CommonLibs/Vector.h GSM/GSM610Tables.cpp GSM/GSM610Tables.h GSM/GSMCommon.cpp GSM/GSMCommon.h GSM/GSMConfig.h GSM/GSML1FEC.cpp GSM/GSML1FEC.h GSM/GSML2LAPDm.cpp GSM/GSML2LAPDm.h GSM/GSML3CCElements.cpp GSM/GSML3CCElements.h GSM/GSML3CCMessages.cpp GSM/GSML3CCMessages.h GSM/GSML3CommonElements.cpp GSM/GSML3CommonElements.h GSM/GSML3MMElements.cpp GSM/GSML3MMElements.h GSM/GSML3MMMessages.cpp GSM/GSML3MMMessages.h GSM/GSML3Message.cpp GSM/GSML3Message.h GSM/GSML3RRElements.cpp GSM/GSML3RRElements.h GSM/GSML3RRMessages.cpp GSM/GSML3RRMessages.h GSM/GSMLogicalChannel.h GSM/GSMTDMA.cpp GSM/GSMTDMA.h GSM/GSMTransfer.cpp GSM/GSMTransfer.h LICENSEBLOCK TRXManager/TRXManager.cpp Transceiver/Complex.h tests/CommonLibs/BitVectorTest.cpp tests/CommonLibs/InterthreadTest.cpp tests/CommonLibs/SocketsTest.cpp tests/CommonLibs/TimevalTest.cpp tests/CommonLibs/VectorTest.cpp Harvind S. Samra, hssamra@kestrelsp.com: GSM/GSMConfig.h GSM/GSMTransfer.h LICENSEBLOCK Transceiver/ComplexTest.cpp Transceiver/Transceiver.cpp Transceiver/Transceiver.h Transceiver/USRPDevice.cpp Transceiver/USRPDevice.h Transceiver/USRPping.cpp Transceiver/radioInterface.cpp Transceiver/radioInterface.h Transceiver/rcvLPF_651.h Transceiver/runTransceiver.cpp Transceiver/sendLPF_961.h Transceiver/sigProcLib.cpp Transceiver/sigProcLib.h Transceiver/sigProcLibTest.cpp Transceiver/sweepGenerator.cpp Transceiver/testRadio.cpp Raffi Sevlian, raffisev@gmail.com: GSM/GSMCommon.h GSM/GSMConfig.h GSM/GSML1FEC.h GSM/GSML3CCElements.cpp GSM/GSML3CCElements.h GSM/GSML3CCMessages.cpp GSM/GSML3CCMessages.h GSM/GSML3CommonElements.cpp GSM/GSML3CommonElements.h GSM/GSML3MMElements.cpp GSM/GSML3MMElements.h GSM/GSML3MMMessages.cpp GSM/GSML3MMMessages.h GSM/GSML3Message.cpp GSM/GSML3Message.h GSM/GSML3RRElements.cpp GSM/GSML3RRElements.h GSM/GSML3RRMessages.cpp GSM/GSML3RRMessages.h GSM/GSMLogicalChannel.h GSM/GSMSAPMux.cpp GSM/GSMSAPMux.h GSM/GSMTransfer.h LICENSEBLOCK TRXManager/TRXManager.h Alon Levy, alonlevy1@gmail.com RRLPMessages.cpp RRLPMessages.h RRLPTest.cpp osmo-trx-0.4.0/tests/0000755000175000017500000000000013272615542013464 5ustar rubenrubenosmo-trx-0.4.0/tests/Transceiver52M/0000755000175000017500000000000013272615542016235 5ustar rubenrubenosmo-trx-0.4.0/tests/Transceiver52M/convolve_test.c0000644000175000017500000000545013272615542021277 0ustar rubenruben#include #include #include #include "convolve.h" #define TESTVEC_LEN 1000 #define DO_INIT 1 float x_vect[TESTVEC_LEN]; float y_vect[TESTVEC_LEN]; float h_vect[TESTVEC_LEN]; float *x; float *h; float *y; /* Generate some random values for testing */ void gen_floats(float *vect, int len) { int i; for(i=0;i expout AT_CHECK([$abs_top_builddir/tests/CommonLibs/BitVectorTest], [], [expout], []) AT_CLEANUP AT_SETUP([InterthreadTest]) AT_KEYWORDS([InterthreadTest]) cat $abs_srcdir/CommonLibs/InterthreadTest.ok > expout AT_CHECK([$abs_top_builddir/tests/CommonLibs/InterthreadTest], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([LogTest]) AT_KEYWORDS([LogTest]) cat $abs_srcdir/CommonLibs/LogTest.ok > expout cat $abs_srcdir/CommonLibs/LogTest.err > experr AT_CHECK([$abs_top_builddir/tests/CommonLibs/LogTest], [], [expout], [experr]) AT_CLEANUP AT_SETUP([PRBSTest]) AT_KEYWORDS([PRBSTest]) cat $abs_srcdir/CommonLibs/PRBSTest.ok > expout AT_CHECK([$abs_top_builddir/tests/CommonLibs/PRBSTest], [], [expout], []) AT_CLEANUP AT_SETUP([SocketsTest]) AT_KEYWORDS([SocketsTest]) cat $abs_srcdir/CommonLibs/SocketsTest.ok > expout AT_CHECK([$abs_top_builddir/tests/CommonLibs/SocketsTest], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([TimevalTest]) AT_KEYWORDS([TimevalTest]) cat $abs_srcdir/CommonLibs/TimevalTest.ok > expout AT_CHECK([$abs_top_builddir/tests/CommonLibs/TimevalTest], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([VectorTest]) AT_KEYWORDS([VectorTest]) cat $abs_srcdir/CommonLibs/VectorTest.ok > expout AT_CHECK([$abs_top_builddir/tests/CommonLibs/VectorTest], [], [expout], []) AT_CLEANUP AT_SETUP([convolve_test]) AT_KEYWORDS([convolve_test]) # Different results for i686, x86_64 and ARM. see OS#2826, OS#2828, and https://lists.osmocom.org/pipermail/openbsc/2018-January/011655.html AT_SKIP_IF(true) cat $abs_srcdir/Transceiver52M/convolve_test.ok > expout AT_CHECK([$abs_top_builddir/tests/Transceiver52M/convolve_test], [], [expout], []) AT_CLEANUP osmo-trx-0.4.0/tests/Makefile.am0000644000175000017500000000300313272615542015514 0ustar rubenrubenSUBDIRS = \ CommonLibs \ Transceiver52M \ $(NULL) # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac :;{ \ echo '# Signature of the current package.' && \ echo 'm4_define([AT_PACKAGE_NAME],' && \ echo ' [$(PACKAGE_NAME)])' && \ echo 'm4_define([AT_PACKAGE_TARNAME],' && \ echo ' [$(PACKAGE_TARNAME)])' && \ echo 'm4_define([AT_PACKAGE_VERSION],' && \ echo ' [$(PACKAGE_VERSION)])' && \ echo 'm4_define([AT_PACKAGE_STRING],' && \ echo ' [$(PACKAGE_STRING)])' && \ echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \ echo ' [$(PACKAGE_BUGREPORT)])'; \ echo 'm4_define([AT_PACKAGE_URL],' && \ echo ' [$(PACKAGE_URL)])'; \ } >'$(srcdir)/package.m4' EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) TESTSUITE = $(srcdir)/testsuite DISTCLEANFILES = atconfig $(NULL) check-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) installcheck-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' $(TESTSUITEFLAGS) clean-local: test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' --clean AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te AUTOTEST = $(AUTOM4TE) --language=autotest $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at mv $@.tmp $@ osmo-trx-0.4.0/tests/CommonLibs/0000755000175000017500000000000013272615542015526 5ustar rubenrubenosmo-trx-0.4.0/tests/CommonLibs/LogTest.ok0000644000175000017500000000000013272615542017430 0ustar rubenrubenosmo-trx-0.4.0/tests/CommonLibs/TimevalTest.ok0000644000175000017500000000000513272615542020315 0ustar rubenrubenDone osmo-trx-0.4.0/tests/CommonLibs/PRBSTest.ok0000644000175000017500000010103213272615542017464 0ustar rubenruben1000010001100001001110010101011000011011110100110111001000101000010101101001111110110010010010110111111001001101010011001100000001100011001010001101001011111110100010110001110101100101100111100011111011101000001101011011011101100000101101011111010101010000001010010101111001011101110000001110011101001001111010111010100010010000110011100001011110110110011010000111011110000111111111000001111011111000101110011001000001001010011101101000111100111110011011000101010010001110001101101010111000100110001000100000000 Period: 511 1000000000000011000000000000101000000000001111000000000010001000000000110011000000001010101000000011111111000000100000001000001100000011000010100000101000111100001111001000100010001011001100110011101010101010100111111111111101000000000000111000000000001001000000000011011000000000101101000000001110111000000010011001000000110101011000001011111101000011100000111000100100001001001101100011011010110100101101111011101110110001100110011010010101010101110111111111110011000000000010101000000000111111000000001000001000000011000011000000101000101000001111001111000010001010001000110011110011001010100010101011111100111111100000101000000100001111000001100010001000010100110011000111101010101001000111111111011001000000001101011000000010111101000000111000111000001001001001000011011011011000101101101101001110110110111010011011011001110101101101010011110110111110100011011000011100101101000100101110111001101110011001010110010101011111010111111100001111000000100010001000001100110011000010101010101000111111111111001000000000001011000000000011101000000000100111000000001101001000000010111011000000111001101000001001010111000011011111001000101100001011001110100011101010011100100111110100101101000011101110111000100110011001001101010101011010111111111101111000000000110001000000001010011000000011110101000000100011111000001100100001000010101100011000111110100101001000011101111011000100110001101001101010010111010111110111001111000011001010001000101011110011001111100010101010000100111111110001101000000010010111000000110111001000001011001011000011101011101000100111100111001101000101001010111001111011111001010001100001011110010100011100010111100100100111000101101101001001110110111011010011011001101110101101010110011110111111010100011000001111100101000010000101111000110001110001001010010010011011110110110101100011011011110100101101100011101110110100100110011011101101010101100110111111110101011000000011111101000000100000111000001100001001000010100011011000111100101101001000101110111011001110011001101010010101010111110111111111000011000000001000101000000011001111000000101010001000001111110011000010000010101000110000111111001010001000001011110011000011100010101000100100111111001101101000001010110111000011111011001000100001101011001100010111101010100111000111111101001001000000111011011000001001101101000011010110111000101111011001001110001101011010010010111101110110111000110011011001001010101101011011111110111101100000011000110100000101001011100001111011100100010001100101100110010101110101010111110011111111000010100000001000111100000011001000100000101011001100001111101010100010000111111100110001000000101010011000001111110101000010000011111000110000100001001010001100011011110010100101100010111101110100111000110011101001001010100111011011111101001101100000111010110100001001111011100011010001100100101110010101101110010111110110010111000011010111001000101111001011001110001011101010010011100111110110100101000011011101111000101100110001001110101010011010011111110101110100000011110011100000100010100100001100111101100010101000110100111111001011101000001011100111000011100101001000100101111011001101110001101010110010010111111010110111000001111011001000010001101011000110010111101001010111000111011111001001001100001011011010100011101101111100100110110000101101011010001110111101110010011000110010110101001010111011111011111001100001100001010100010100011111100111100100000101000101100001111001110100010001010011100110011110100101010100011101111111100100110000000101101010000001110111110000010011000010000110101000110001011111001010011100001011110100100011100011101100100100100110101101101101011110110110111100011011011000100101101101001101110110111010110011011001111010101101010001111110111110010000011000010110000101000111010001111001001110010001011010010110011101110111010100110011001111101010101010000111111111110001000000000010011000000000110101000000001011111000000011100001000000100100011000001101100101000010110101111000111011110001001001100010011011010100110101101111101011110110000111100011010001000100101110011001101110010101010110010111111111010111000000001111001000000010001011000000110011101000001010100111000011111101001000100000111011001100001001101010100011010111111100101111000000101110001000001110010011000010010110101000110111011111001011001100001011101010100011100111111100100101000000101101111000001110110001000010011010011000110101110101001011110011111011100010100001100100111100010101101000100111110111001101000011001010111000101011111001001111100001011010000100011101110001100100110010010101101010110111110111111011000011000001101000101000010111001111000111001010001001001011110011011011100010101101100100111110110101101000011011110111000101100011001001110100101011010011101111101110100110000110011101010001010100111110011111101000010100000111000111100001001001000100011011011001100101101101010101110110111111110011011000000010101101000000111110111000001000011001000011000101011000101001111101001111010000111010001110001001110010010011010010110110101110111011011110011001101100010101010110100111111111011101000000001100111000000010101001000000111111011000001000001101000011000010111000101000111001001111001001011010001011011101110011101100110010100110101010111101011111111000111100000001001000100000011011001100000101101010100001110111111100010011000000100110101000001101011111000010111100001000111000100011001001001100101011011010101111101101111110000110110000010001011010000110011101110001010100110010011111101010110100000111111011100001000001100100011000010101100101000111110101111001000011110001011000100010011101001100110100111010101011101001111111100111010000000101001110000001111010010000010001110110000110010011010001010110101110011111011110010100001100010111100010100111000100111101001001101000111011010111001001101111001011010110001011101111010011100110001110100101010010011101111110110100110000011011101010000101100111110001110101000010010011111000110110100001001011011100011011101100100101100110101101110101011110110011111100011010100000100101111100001101110000100010110010001100111010110010101001111010111111010001111000001110010001000010010110011000110111010101001011001111111011101010000001100111110000010101000010000111111000110001000001001010011000011011110101000101100011111001110100100001010011101100011110100110100100011101011101100100111100110101101000101011110111001111100011001010000100101011110001101111100010010110000100110111010001101011001110010111101010010111000111110111001001000011001011011000101011101101001111100110111010000101011001110001111101010010010000111110110110001000011011010011000101101110101001110110011111010011010100001110101111100010011110000100110100010001101011100110010111100101010111000101111111001001110000001011010010000011101110110000100110011010001101010101110010111111110010111000000010111001000000111001011000001001011101000011011100111000101100101001001110101111011010011110001101110100010010110011100110111010100101011001111101111101010000110000111110001010001000010011110011000110100010101001011100111111011100101000001100101111000010101110001000111110010011001000010110101011000111011111101001001100000111011010100001001101111100011010110000100101111010001101110001110010110010010010111010110110111001111011011001010001101101011110010110111100010111011000100111001101001101001010111010111011111001111001100001010001010100011110011111100100010100000101100111100001110101000100010011111001100110100001010101011100011111111100100100000000101101100000001110110100000010011011100000110101100100001011110101100011100011110100100100100011101101101100100110110110101101011011011110111101101100011000110110100101001011011101111011101100110001100110101010010101011111110111111100000011000000100000101000001100001111000010100010001000111100110011001000101010101011001111111111101010000000000111110000000001000010000000011000110000000101001010000001111011110000010001100010000110010100110001010111101010011111000111110100001001000011100011011000100100101101001101101110111010110110011001111011010101010001101111111110010110000000010111010000000111001110000001001010010000011011110110000101100011010001110100101110010011101110010110100110010111011101010111001100111111001010101000001011111111000011100000001000100100000011001101100000101010110100001111111011100010000001100100110000010101101010000111110111110001000011000010011000101000110101001111001011111010001011100001110011100100010010100101100110111101110101011000110011111101001010100000111011111100001001100000100011010100001100101111100010101110000100111110010001101000010110010111000111010111001001001111001011011010001011101101110011100110110010100101011010111101111101111000110000110001001010001010011011110011110101100010100011110100111100100011101000101100100111001110101101001010011110111011110100011001100011100101010100100101111111101101110000000110110010000001011010110000011101111010000100110001110001101010010010010111110110110111000011011011001000101101101011001110110111101010011011000111110101101001000011110111011000100011001101001100101010111010101111111001111110000001010000010000011110000110000100010001010001100110011110010101010100010111111111100111000000000101001000000001111011000000010001101000000110010111000001010111001000011111001011000100001011101001100011100111010100100101001111101101111010000110110001110001011010010010011101110110110100110011011011101010101101100111111110110101000000011011111000000101100001000001110100011000010011100101000110100101111001011101110001011100110010011100101010110100101111111011101110000001100110010000010101010110000111111111010001000000001110011000000010010101000000110111111000001011000001000011101000011000100111000101001101001001111010111011010001111001101110010001010110010110011111010111010100001111001111100010001010000100110011110001101010100010010111111100110111000000101011001000001111101011000010000111101000110001000111001010011001001011110101011011100011111101100100100000110101101100001011110110100011100011011100100100101100101101101110101110110110011110011011010100010101101111100111110110000101000011010001111000101110010001001110010110011010010111010101110111001111110011001010000010101011110000111111100010001000000100110011000001101010101000010111111111000111000000001001001000000011011011000000101101101000001110110111000010011011001000110101101011001011110111101011100011000111100100101001000101101111011001110110001101010011010010111110101110111000011110011001000100010101011001100111111101010101000000111111111000001000000001000011000000011000101000000101001111000001111010001000010001110011000110010010101001010110111111011111011000001100001101000010100010111000111100111001001000101001011011001111011101101010001100110111110010101011000010111111101000111000000111001001000001001011011000011011101101000101100110111001110101011001010011111101011110100000111100011100001000100100100011001101101100101010110110101111111011011110000001101100010000010110100110000111011101010001001100111110011010101000010101111111000111110000001001000010000011011000110000101101001010001110111011110010011001100010110101010100111011111111101001100000000111010100000001001111100000011010000100000101110001100001110010010100010010110111100110111011000101011001101001111101010111010000111111001110001000001010010011000011110110101000100011011111001100101100001010101110100011111110011100100000010100101100000111101110100001000110011100011001010100100101011111101101111100000110110000100001011010001100011101110010100100110010111101101010111000110111111001001011000001011011101000011101100111000100110101001001101011111011010111100001101111000100010110001001100111010011010101001110101111111010011110000001110100010000010011100110000110100101010001011101111110011100110000010100101010000111101111110001000110000010011001010000110101011110001011111100010011100000100110100100001101011101100010111100110100111000101011101001001111100111011010000101001101110001111010110010010001111010110110010001111011010110010001101111010110010110001111010111010010001111001110110010001010011010110011110101111010100011110001111100100010010000101100110110001110101011010010011111101110110100000110011011100001010101100100011111110101100100000011110101100000100011110100001100100011100010101100100100111110101101101000011110110111000100011011001001100101101011010101110111101111110011000110000010101001010000111111011110001000001100010011000010100110101000111101011111001000111100001011001000100011101011001100100111101010101101000111111110111001000000011001011000000101011101000001111100111000010000101001000110001111011001010010001101011110110010111100011010111000100101111001001101110001011010110010011101111010110100110001111011101010010001100111110110010101000011010111111000101111000001001110001000011010010011000101110110101001110011011111010010101100001110111110100010011000011100110101000100101011111001101111100001010110000100011111010001100100001110010101100010010111110100110111000011101011001000100111101011001101000111101010111001000111111001011001000001011101011000011100111101000100101000111001101111001001010110001011011111010011101100001110100110100010011101011100110100111100101011101000101111100111001110000101001010010001111011110110010001100011010110010100101111010111101110001111000110010010001001010110110011011111011010101100001101111110100010110000011100111010000100101001110001101111010010010110001110110111010010011011001110110101101010011011110111110101100011000011110100101000100011101111001100100110001010101101010011111110111110100000011000011100000101000100100001111001101100010001010110100110011111011101010100001100111111100010101000000100111111000001101000001000010111000011000111001000101001001011001111011011101010001101100111110010110101000010111011111000111001100001001001010100011011011111100101101100000101110110100001110011011100010010101100100110111110101101011000011110111101000100011000111001100101001001010101111011011111110001101100000010010110100000110111011100001011001100100011101010101100100111111110101101000000011110111000000100011001000001100101011000010101111101000111110000111001000010001001011000110011011101001010101100111011111110101001100000011111010100000100001111100001100010000100010100110001100111101010010101000111110111111001000011000001011000101000011101001111000100111010001001101001110011010111010010101111001110111110001010011000010011110101000110100011111001011100100001011100101100011100101110100100101110011101101110010100110110010111101011010111000111101111001001000110001011011001010011101101011110100110111100011101011000100100111101001101101000111010110111001001111011001011010001101011101110010111100110010111000101010111001001111111001011010000001011101110000011100110010000100101010110001101111111010010110000001110111010000010011001110000110101010010001011111110110011100000011010100100000101111101100001110000110100010010001011100110110011100101011010100101111101111101110000110000110010001010001010110011110011111010100010100001111100111100010000101000100110001111001101010010001010111110110011111000011010100001000101111100011001110000100101010010001101111110110010110000011010111010000101111001110001110001010010010010011110110110110100011011011011100101101101100101110110110101110011011011110010101101100010111110110100111000011011101001000101100111011001110101001101010011111010111110100001111000011100010001000100100110011001101101010101010110111111111111011000000000001101000000000010111000000000111001000000001001011000000011011101000000101100111000001110101001000010011111011000110100001101001011100010111011100100111001100101101001010101110111011111110011001100000010101010100000111111111100001000000000100011000000001100101000000010101111000000111110001000001000010011000011000110101000101001011111001111011100001010001100100011110010101100100010111110101100111000011110101001000100011111011001100100001101010101100010111111110100111000000011101001000000100111011000001101001101000010111010111000111001111001001001010001011011011110011101101100010100110110100111101011011101000111101100111001000110101001011001011111011101011100001100111100100010101000101100111111001110101000001010011111000011110100001000100011100011001100100100101010101101101111111110110110000000011011010000000101101110000001110110010000010011010110000110101111010001011110001110011100010010010100100110110111101101011011000110111101101001011000110111011101001011001100111011101010101001100111111111010101000000001111111000000010000001000000110000011000001010000101000011110001111000100010010001001100110110011010101011010101111111101111110000000110000010000001010000110000011110001010000100010011110001100110100010010101011100110111111100101011000000101111101000001110000111000010010001001000110110011011001011010101101011101111110111100110000011000101010000101001111110001111010000010010001110000110110010010001011010110110011101111011010100110001101111101010010110000111110111010001000011001110011000101010010101001111110111111010000011000001110000101000010010001111000110110010001001011010110011011101111010101100110001111110101010010000011111110110000100000011010001100000101110010100001110010111100010010111000100110111001001101011001011010111101011101111000111100110001001000101010011011001111110101101010000011110111110000100011000010001100101000110010101111001010111110001011111000010011100001000110100100011001011101100101011100110101111100101011110000101111100010001110000100110010010001101010110110010111111011010111000001101111001000010110001011000111010011101001001110100111011010011101001101110100111010110011101001111010100111010001111101001110010000111010010110001001110111010011010011001110101110101010011110011111110100010100000011100111100000100101000100001101111001100010110001010100111010011111101001110100000111010011100001001110100100011010011101100101110100110101110011101011110010100111100010111101000100111000111001101001001001010111011011011111001101101100001010110110100011111011011100100001101100101100010110101110100111011110011101001100010100111010100111101001111101000111010000111001001110001001011010010011011101110110101100110011011110101010101100011111111110100100000000011101100000000100110100000001101011100000010111100100000111000101100001001001110100011011010011100101101110100101110110011101110011010100110010101111101010111110000111111000010001000001000110011000011001010101000101011111111001111100000001010000100000011110001100000100010010100001100110111100010101011000100111111101001101000000111010111000001001111001000011010001011000101110011101001110010100111010010111101001110111000111010011001001001110101011011010011111101101110100000110110011100001011010100100011101111101100100110000110101101010001011110111110011100011000010100100101000111101101111001000110110001011001011010011101011101110100111100110011101000101010100111001111111101001010000000111011110000001001100010000011010100110000101111101010001110000111110010010001000010110110011000111011010101001001101111111011010110000001101111010000010110001110000111010010010001001110110110011010011011010101110101101111110011110110000010100011010000111100101110001000101110010011001110010110101010010111011111110111001100000011001010100000101011111100001111100000100010000100001100110001100010101010010100111111110111101000000011000111000000101001001000001111011011000010001101101000110010110111001010111011001011111001101011100001010111100100011111000101100100001001110101100011010011110100101110100011101110011100100110010100101101010111101110111111000110011000001001010101000011011111111000101100000001001110100000011010011100000101110100100001110011101100010010100110100110111101011101011000111100111101001000101000111011001111001001101010001011010111110011101111000010100110001000111101010011001000111110101011001000011111101011000100000111101001100001000111010100011001001111100101011010000101111101110001110000110010010010001010110110110011111011011010100001101101111100010110110000100111011010001101001101110010111010110010111001111010111001010001111001011110010001011100010110011100100111010100101101001111101110111010000110011001110001010101010010011111111110110100000000011011100000000101100100000001110101100000010011110100000110100011100001011100100100011100101101100100101110110101101110011011110110010101100011010111110100101111000011101110001000100110010011001101010110101010111111011111111000001100000001000010100000011000111100000101001000100001111011001100010001101010100110010111111101010111000000111111001000001000001011000011000011101000101000100111001111001101001010001010111011110011111001100010100001010100111100011111101000100100000111001101100001001010110100011011111011100101100001100101110100010101110011100111110010100101000010111101111000111000110001001001001010011011011011110101101101100011110110110100100011011011101100101101100110101110110101011110011011111100010101100000100111110100001101000011100010111000100100111001001101101001011010110111011101111011001100110001101010101010010111111111110111000000000011001000000000101011000000001111101000000010000111000000110001001000001010011011000011110101101000100011110111001100100011001010101100101011111110101111100000011110000100000100010001100001100110010100010101010111100111111111000101000000001001111000000011010001000000101110011000001110010101000010010111111000110111000001001011001000011011101011000101100111101001110101000111010011111001001110100001011010011100011101110100100100110011101101101010100110110111111101011011000000111101101000001000110111000011001011001000101011101011001111100111101010000101000111110001111001000010010001011000110110011101001011010100111011101111101001100110000111010101010001001111111110011010000000010101110000000111110010000001000010110000011000111010000101001001110001111011010010010001101110110110010110011011010111010101101111001111110110001010000011010011110000101110100010001110011100110010010100101010110111101111111011000110000001101001010000010111011110000111001100010001001010100110011011111101010101100000111111110100001000000011100011000000100100101000001101101111000010110110001000111011010011001001101110101011010110011111101111010100000110001111100001010010000100011110110001100100011010010101100101110111110101110011000011110010101000100010111111001100111000001010101001000011111111011000100000001101001100000010111010100000111001111100001001010000100011011110001100101100010010101110100110111110011101011000010100111101000111101000111001000111001001011001001011011101011011101100111101100110101000110101011111001011111100001011100000100011100100001100100101100010101101110100111110110011101000011010100111000101111101001001110000111011010010001001101110110011010110011010101111010101111110001111110000010010000010000110110000110001011010001010011101110011110100110010100011101010111100100111111000101101000001001110111000011010011001000101110101011001110011111101010010100000111110111100001000011000100011000101001100101001111010101111010001111110001110010000010010010110000110110111010001011011001110011101101010010100110111110111101011000011000111101000101001000111001111011001001010001101011011110010111101100010111000110100111001001011101001011011100111011101100101001100110101111010101011110001111111100010010000000100110110000001101011010000010111101110000111000110010001001001010110011011011111010101101100001111110110100010000011011100110000101100101010001110101111110010011110000010110100010000111011100110001001100101010011010101111110101111110000011110000010000100010000110001100110001010010101010011110111111110100011000000011100101000000100101111000001101110001000010110010011000111010110101001001111011111011010001100001101110010100010110010111100111010111000101001111001001111010001011010001110011101110010010100110010110111101010111011000111111001101001000001010111011000011111001101000100001010111001100011111001010100100001011111101100011100000110100100100001011101101100011100110110100100101011011101101111101100110110000110101011010001011111101110011100000110010100100001010111101100011111000110100100001001011101100011011100110100101100101011101110101111100110011110000101010100010001111111100110010000000101010110000001111111010000010000001110000110000010010001010000110110011110001011010100010011101111100110100110000101011101010001111100111110010000101000010110001111000111010010001001001110110011011010011010101101110101111110110011110000011010100010000101111100110001110000101010010010001111110110110010000011011010110000101101111010001110110001110010011010010010110101110110111011110011011001100010101101010100111110111111101000011000000111000101000001001001111000011011010001000101101110011001110110010101010011010111111110101111000000011110001000000100010011000001100110101000010101011111000111111100001001000000100011011000001100101101000010101110111000111110011001001000010101011011000111111101101001000000110111011000001011001101000011101010111000100111111001001101000001011010111000011101111001000100110001011001101010011101010111110100111111000011101000001000100111000011001101001000101010111011001111111001101010000001010111110000011111000010000100001000110001100011001010010100101011110111101111100011000110000100101001010001101111011110010110001100010111010010100111001110111101001010011000111011110101001001100011111011010100100001101111101100010110000110100111010001011101001110011100111010010100101001110111101111010011000110001110101001010010011111011110110100001100011011100010100101100100111101110101101000110011110111001010100011001011111100101011100000101111100100001110000101100010010001110100110110010011101011010110100111101111011101000110001100111001010010101001011110111111011100011000001100100101000010101101111000111110110001001000011010011011000101110101101001110011110111010010100011001110111100101010011000101111110101001110000011111010010000100001110110001100010011010010100110101110111101011110011000111100010101001000100111111011001101000001101010111000010111111001000111000001011001001000011101011011000100111101101001101000110111010111001011001111001011101010001011100111110011100101000010100101111000111101110001001000110010011011001010110101101011111011110111100001100011000100010100101001100111101111010101000110001111111001010010000001011110110000011100011010000100100101110001101101110010010110110010110111011010111011001101111001101010110001010111111010011111000001110100001000010011100011000110100100101001011101101111011100110110001100101011010010101111101110111110000110011000010001010101000110011111111001010100000001011111100000011100000100000100100001100001101100010100010110100111100111011101000101001100111001111010101001010001111111011110010000001100010110000010100111010000111101001110001000111010010011001001110110101011010011011111101110101100000110011110100001010100011100011111100100100100000101101101100001110110110100010011011011100110101101100101011110110101111100011011110000100101100010001101110100110010110011101010111010100111111001111101000001010000111000011110001001000100010011011001100110101101010101011110111111111100011000000000100101000000001101111000000010110001000000111010011000001001110101000011010011111000101110100001001110011100011010010100100101110111101101110011000110110010101001011010111111011101111000001100110001000010101010011000111111110101001000000011111011000000100001101000001100010111000010100111001000111101001011001000111011101011001001100111101011010101000111101111111001000110000001011001010000011101011110000100111100010001101000100110010111001101010111001010111111001011111000001011100001000011100100011000100101100101001101110101111010110011110001111010100010010001111100110110010000101011010110001111101111010010000110001110110001010010011010011110110101110100011011110011100101100010100101110100111101110011101000110010100111001010111101001011111000111011100001001001100100011011010101100101101111110101110110000011110011010000100010101110001100111110010010101000010110111111000111011000001001001101000011011010111000101101111001001110110001011010011010011101110101110100110011110011101010100010100111111100111101000000101000111000001111001001000010001011011000110011101101001010100110111011111101011001100000111101010100001000111111100011001000000100101011000001101111101000010110000111000111010001001001001110011011011010010101101101110111110110110011000011011010101000101101111111001110110000001010011010000011110101110000100011110010001100100010110010101100111010111110101001111000011111010001000100001110011001100010010101010100110111111111101011000000000111101000000001000111000000011001001000000101011011000001111101101000010000110111000110001011001001010011101011011110100111101100011101000110100100111001011101101001011100110111011100101011001100101111101010101110000111111110010001000000010110011000000111010101000001001111111000011010000001000101110000011001110010000101010010110001111110111010010000011001110110000101010011010001111110101110010000011110010110000100010111010001100111001110010101001010010111111011110111000001100011001000010100101011000111101111101001000110000111011001010001001101011110011010111100010101111000100111110001001101000010011010111000110101111001001011110001011011100010011101100100110100110101101011101011110111100111100011000101000100101001111001101111010001010110001110011111010010010100001110110111100010011011000100110101101001101011110111010111100011001111000100101010001001101111110011010110000010101111010000111110001110001000010010010011000110110110101001011011011111011101101100001100110110100010101011011100111111101100101000000110101111000001011110001000011100010011000100100110101001101101011111010110111100001111011000100010001101001100110010111010101010111001111111111001010000000001011110000000011100010000000100100110000001101101010000010110111110000111011000010001001101000110011010111001010101111001011111110001011100000010011100100000110100101100001011101110100011100110011100100101010100101101111111101110110000000110011010000001010101110000011111110010000100000010110001100000111010010100001001110111100011010011000100101110101001101110011111010110010100001111010111100010001111000100110010001001101010110011010111111010101111000001111110001000010000010011000110000110101001010001011111011110011100001100010100100010100111101100111101000110101000111001011111001001011100001011011100100011101100101100100110101110101101011110011110111100010100011000100111100101001101000101111010111001110001111001010010010001011110110110011100011011010100100101101111101101110110000110110011010001011010101110011101111110010100110000010111101010000111000111110001001001000010011011011000110101101101001011110110111011100011011001100100101101010101101110111111110110011000000011010101000000101111111000001110000001000010010000011000110110000101001011010001111011101110010001100110010110010101010111010111111111001111000000001010001000000011110011000000100010101000001100111111000010101000001000111111000011001000001000101011000011001111101000101010000111001111110001001010000010011011110000110101100010001011110100110011100011101010100100100111111101101101000000110110111000001011011001000011101101011000100110111101001101011000111010111101001001111000111011010001001001101110011011010110010101101111010111110110001111000011010010001000101110110011001110011010101010010101111111110111110000000011000010000000101000110000001111001010000010001011110000110011100010001010100100110011111101101010100000110111111100001011000000100011101000001100100111000010101101001000111110111011001000011001101011000101010111101001111111000111010000001001001110000011011010010000101101110110001110110011010010011010101110110101111110011011110000010101100010000111110100110001000011101010011000100111110101001101000011111010111000100001111001001100010001011010100110011101111101010100110000111111101010001000000111110011000001000010101000011000111111000101001000001001111011000011010001101000101110010111001110010111001010010111001011110111001011100011001011100100101011100101101111100101110110000101110011010001110010101110010010111110010110111000010111011001000111001101011001001010111101011011111000111101100001001000110100011011001011100101101011100101110111100101110011000101110010101001110010111111010010111000001110111001000010011001011000110101011101001011111100111011100000101001100100001111010101100010001111110100110010000011101010110000100111111010001101000001110010111000010010111001000110111001011001011001011101011101011100111100111100101000101000101111001111001110001010001010010011110011110110100010100011011100111100101100101000101110101111001110011110001010010100010011110111100110100011000101011100101001111100101111010000101110001110001110010010010010010110110110110111011011011011001101101101101010110110110111111011011011000001101101101000010110110111000111011011001001001101101011011010110111101101111011000110110001101001011010010111011101110111001100110011001010101010101011111111111111100000000000000 Period: 32767 osmo-trx-0.4.0/tests/CommonLibs/InterthreadTest.ok0000644000175000017500000000000513272615542021165 0ustar rubenrubenDone osmo-trx-0.4.0/tests/CommonLibs/SocketsTest.cpp0000644000175000017500000000455413272615542020515 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "Sockets.h" #include "Threads.h" #include #include #include #include static const int gNumToSend = 10; static void sigalarm_handler(int foo) { printf("FAIL: test did not run successfully\n"); exit(EXIT_FAILURE); } void *testReaderIP(void *param) { UDPSocket *readSocket = (UDPSocket *)param; readSocket->nonblocking(); int rc = 0; while (rcread(buf, MAX_UDP_LENGTH); if (count>0) { CERR("read: " << buf); rc++; } else { sleep(2); } } return NULL; } int main(int argc, char * argv[] ) { int count; if (signal(SIGALRM, sigalarm_handler) == SIG_ERR) { perror("signal"); exit(EXIT_FAILURE); } /* If the test takes longer than 2*gNumToSend seconds, abort it */ alarm(2* gNumToSend); UDPSocket readSocket("127.0.0.1", 0); UDPSocket socket1("127.0.0.1", 0, "localhost", readSocket.port()); CERR("socket1: " << socket1.port() << ", readSocket: " << readSocket.port()); Thread readerThreadIP; readerThreadIP.start(testReaderIP, &readSocket); // give the readers time to open sleep(1); for (int i=0; i. */ #include #include #include "Logger.h" extern "C" { #include #include #include #include #include "debug.h" } #define MYCAT 0 int main(int argc, char *argv[]) { struct log_info_cat categories[1]; struct log_info linfo; categories[MYCAT] = { "MYCAT", NULL, "Whatever", LOGL_NOTICE, 1, }; linfo.cat = categories; linfo.num_cat = ARRAY_SIZE(categories); void *tall_ctx = talloc_named_const(NULL, 1, "OsmoTRX context"); msgb_talloc_ctx_init(tall_ctx, 0); osmo_init_logging2(tall_ctx, &linfo); log_set_use_color(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); log_set_print_level(osmo_stderr_target, 1); Log(MYCAT, LOGL_FATAL, __BASE_FILE__, __LINE__).get() << "testing the logger."; Log(MYCAT, LOGL_ERROR, __BASE_FILE__, __LINE__).get() << "testing the logger."; Log(MYCAT, LOGL_NOTICE, __BASE_FILE__, __LINE__).get() << "testing the logger."; Log(MYCAT, LOGL_INFO, __BASE_FILE__, __LINE__).get() << "testing the logger."; Log(MYCAT, LOGL_DEBUG, __BASE_FILE__, __LINE__).get() << "testing the logger."; } osmo-trx-0.4.0/tests/CommonLibs/VectorTest.ok0000644000175000017500000000023513272615542020163 0ustar rubenruben0 1 2 3 4 10 11 12 13 14 0 1 2 3 4 10 11 12 13 14 0 1 2 3 4 10 11 12 13 14 8 8 8 8 8 8 8 8 8 8 8 8 8 0 1 2 3 4 8 8 1 2 3 8 8 8 0 9 9 9 4 8 8 9 9 9 osmo-trx-0.4.0/tests/CommonLibs/BitVectorTest.cpp0000644000175000017500000000307313272615542020776 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "BitVector.h" #include #include using namespace std; int main(int argc, char *argv[]) { BitVector v5("000011110000"); int r1 = v5.peekField(0,8); int r2 = v5.peekField(4,4); int r3 = v5.peekField(4,8); cout << r1 << ' ' << r2 << ' ' << r3 << endl; cout << v5 << endl; v5.fillField(0,0xa,4); int r4 = v5.peekField(0,8); cout << v5 << endl; cout << r4 << endl; v5.reverse8(); cout << v5 << endl; unsigned char ts[9] = "abcdefgh"; BitVector tp(70); cout << "ts=" << ts << endl; tp.unpack(ts); cout << "tp=" << tp << endl; tp.pack(ts); cout << "ts=" << ts << endl; } osmo-trx-0.4.0/tests/CommonLibs/TimevalTest.cpp0000644000175000017500000000403113272615542020471 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "Timeval.h" #include #include #include using namespace std; int main(int argc, char *argv[]) { long last_remaining = 10000; Timeval then(last_remaining); assert(then.elapsed() == -last_remaining); cerr << then << " elapsed: " << then.elapsed() << endl; /* Check that last_remaining parameter affects setting time in the future */ usleep(10000); double increased_time_secs = Timeval().seconds(); assert(increased_time_secs <= then.seconds()); struct timespec invariant_time = then.timespec(); int loops = 0; while (!then.passed()) { struct timespec tspecnow = then.timespec(); cerr << "now: " << Timeval().seconds() << " then: " << then << " remaining: " << then.remaining() << endl; assert(last_remaining >= then.remaining()); assert(tspecnow.tv_sec == invariant_time.tv_sec && tspecnow.tv_nsec == invariant_time.tv_nsec); usleep(500000); loops++; } cerr << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl; assert(then.remaining() <= 0); assert(loops >= 18); printf("Done\n"); } osmo-trx-0.4.0/tests/CommonLibs/InterthreadTest.cpp0000644000175000017500000000527013272615542021347 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "Threads.h" #include "Interthread.h" #include using namespace std; InterthreadQueue gQ; InterthreadMap gMap; int q_last_read_val = -1; int q_last_write_val; int m_last_read_val; int m_last_write_val; void* qWriter(void*) { int *p; for (int i=0; i<20; i++) { p = new int; *p = i; CERR("queue write " << *p); gQ.write(p); q_last_write_val = i; if (random()%2) sleep(1); } p = new int; *p = -1; gQ.write(p); return NULL; } void* qReader(void*) { bool done = false; while (!done) { int *p = gQ.read(); CERR("queue read " << *p); if (*p<0) { assert(q_last_read_val == 19 && *p == -1); done = true; } else { assert(q_last_read_val == *p - 1); q_last_read_val = *p; } delete p; } return NULL; } void* mapWriter(void*) { int *p; for (int i=0; i<20; i++) { p = new int; *p = i; CERR("map write " << *p); gMap.write(i,p); m_last_write_val = i; if (random()%2) sleep(1); } return NULL; } void* mapReader(void*) { for (int i=0; i<20; i++) { int *p = gMap.read(i); CERR("map read " << *p); assert(*p == i); m_last_read_val = *p; // InterthreadMap will delete the pointers // delete p; } return NULL; } int main(int argc, char *argv[]) { Thread qReaderThread; qReaderThread.start(qReader,NULL); Thread mapReaderThread; mapReaderThread.start(mapReader,NULL); Thread qWriterThread; qWriterThread.start(qWriter,NULL); Thread mapWriterThread; mapWriterThread.start(mapWriter,NULL); qReaderThread.join(); qWriterThread.join(); mapReaderThread.join(); mapWriterThread.join(); assert(q_last_write_val == 19); assert(q_last_read_val == 19); assert(m_last_write_val == 19); assert(m_last_read_val == 19); printf("Done\n"); } // vim: ts=4 sw=4 osmo-trx-0.4.0/tests/CommonLibs/PRBSTest.cpp0000644000175000017500000000245513272615542017646 0ustar rubenruben/* * Copyright (C) 2017 Alexander Chemeris * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "PRBS.h" #include #include #include void testPrbs(PRBS &prbs, uint64_t expectedPeriod) { uint64_t period = 0; do { std::cout << prbs.generateBit(); period++; } while (!prbs.isFinished()); std::cout << std::endl; std::cout << "Period: " << period << std::endl; assert(period == expectedPeriod); } int main(int argc, char *argv[]) { PRBS9 prbs9(0x01); testPrbs(prbs9, (1<<9)-1); PRBS15 prbs15(0x01); testPrbs(prbs15, (1<<15)-1); } osmo-trx-0.4.0/tests/CommonLibs/BitVectorTest.ok0000644000175000017500000000022713272615542020623 0ustar rubenruben15 15 240 000011110000 101011110000 175 111101010000 ts=abcdefgh tp=0110000101100010011000110110010001100101011001100110011101101000000000 ts=abcdefgh osmo-trx-0.4.0/tests/CommonLibs/VectorTest.cpp0000644000175000017500000000316413272615542020340 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "Vector.h" #include using namespace std; typedef Vector TestVector; int main(int argc, char *argv[]) { TestVector test1(5); for (int i=0; i<5; i++) test1[i]=i; TestVector test2(5); for (int i=0; i<5; i++) test2[i]=10+i; cout << test1 << endl; cout << test2 << endl; { TestVector testC(test1,test2); cout << testC << endl; cout << testC.head(3) << endl; cout << testC.tail(3) << endl; testC.fill(8); cout << testC << endl; test1.copyToSegment(testC,3); cout << testC << endl; TestVector testD(testC.segment(4,3)); cout << testD << endl; testD.fill(9); cout << testC << endl; cout << testD << endl; } return 0; } osmo-trx-0.4.0/Makefile.am0000644000175000017500000000242613272615542014362 0ustar rubenruben# # Copyright 2008 Free Software Foundation, Inc. # # This software is distributed under the terms of the GNU Public License. # See the COPYING file in the main directory for details. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # include $(top_srcdir)/Makefile.common ACLOCAL_AMFLAGS = -I config AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES) AM_CXXFLAGS = -Wall -pthread #AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread #AM_CFLAGS = -Wall -O2 -NDEBUG -pthread # Order must be preserved SUBDIRS = \ CommonLibs \ GSM \ Transceiver52M \ tests EXTRA_DIST = \ autogen.sh \ INSTALLATION \ LEGAL \ COPYING \ README .PHONY: release @RELMAKE@ dox: FORCE doxygen doxconfig FORCE: osmo-trx-0.4.0/LEGAL0000644000175000017500000000447013272615542013076 0ustar rubenrubenOpenBTS The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details. The related copyrights: Most parts copyright 2008-2011 Free Software Foundation. Some parts copyright 2010 Kestrel Signal Processing, Inc. Some parts copyright 2011 Range Networks, Inc. Patent Laws The use of this software to provide GSM services may result in the use of patented technologies. The user of this software is required to take whatever actions are necessary to avoid patent infringement. Telecom and Radio Spectrum Laws The primary function of OsmoTRX is the provision of telecommunications service over a radio link. This activity is heavily regulated nearly everywhere in the world. Users of this software are expected to comply with local and national regulations in the jurisdictions where this sortware is used with radio equipment. Legal Summary The user of this software is expected to comply with all applicable laws and regulations, including patent laws, copyright laws, and telecommunications regulations. The legal restrictions listed here are not necessarily exhaustive. Note to US Government Users The OsmoTRX software applications and associated documentation are "Commercial Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of "Commercial Computer Software" and "Commercial Computer Software Documentation," as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as applicable. Consistent with 48 C.F.R. 12.212 or 48 C.F.R. Sections 227.7202-1 through 227.7202-4, as applicable, the Commercial Computer Software and Commercial Computer Software Documentation are being licensed to U.S. Government end users (a) only as Commercial Items and (b) with only those rights as are granted to all other end users pursuant to the terms and conditions of GPLv3 and AGPLv3. Note to US Government Contractors GPL is not compatible with "government purpose rights" (GPR). If you receive OsmoTRX software under a GPL and deliver it under GPR, you will be in violation of GPL and possibly subject to enforcement actions by the original authors and copyright holders, including the Free Software Foundation, Inc. Software Licensing and Distribution The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file for more information on the license for this distribution. osmo-trx-0.4.0/README0000644000175000017500000000766013272615542013213 0ustar rubenrubenThis is the interface to the transcevier. Each TRX Manager UDP socket interface represents a single ARFCN. Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data. Give a base port B (5700), the master clock interface is at port P=B. The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2. The corresponding core-side interface for every socket is at P+100. For any given build, the number of ARFCN interfaces can be fixed. Indications on the Master Clock Interface The master clock interface is output only (from the radio). Messages are "indications". CLOCK gives the current value of the transceiver clock to be used by the core. This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times. IND CLOCK Commands on the Per-ARFCN Control Interface The per-ARFCN control interface uses a command-reponse protocol. Commands are NULL-terminated ASCII strings, one per UDP socket. Each command has a corresponding response. Every command is of the form: CMD [params] The is the actual command. Parameters are optional depending on the commands type. Every response is of the form: RSP [result] The is 0 for success and a non-zero error code for failure. Successful responses may include results, depending on the command type. Power Control POWEROFF shuts off transmitter power and stops the demodulator. CMD POWEROFF RSP POWEROFF POWERON starts the transmitter and starts the demodulator. Initial power level is very low. This command fails if the transmitter and receiver are not yet tuned. This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng. If the transceiver is already on, it response with success to this command. CMD POWERON RSP POWERON SETPOWER sets output power in dB wrt full scale. This command fails if the transmitter and receiver are not running. CMD SETPOWER RSP SETPOWER ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale. This command fails if the transmitter and receiver are not running. CMD ADJPOWER RSP ADJPOWER Tuning Control RXTUNE tunes the receiver to a given frequency in kHz. This command fails if the receiver is already running. (To re-tune you stop the radio, re-tune, and restart.) This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng. CMD RXTUNE RSP RXTUNE TXTUNE tunes the transmitter to a given frequency in kHz. This command fails if the transmitter is already running. (To re-tune you stop the radio, re-tune, and restart.) This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng. CMD TXTUNE RSP TXTUNE Timeslot Control SETSLOT sets the format of the uplink timeslots in the ARFCN. The indicates the timeslot of interest. The indicates the type of channel that occupies the timeslot. A chantype of zero indicates the timeslot is off. CMD SETSLOT RSP SETSLOT Messages on the per-ARFCN Data Interface Messages on the data interface carry one radio burst per UDP message. Received Data Burst 1 byte timeslot index 4 bytes GSM frame number, big endian 1 byte RSSI in -dBm 2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian 148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1" Transmit Data Burst 1 byte timeslot index 4 bytes GSM frame number, big endian 1 byte transmit level wrt ARFCN max, -dB (attenuation) 148 bytes output symbol values, 0 & 1 osmo-trx-0.4.0/COPYING0000644000175000017500000010707713272615542013371 0ustar rubenruben GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . ========================================================================= This marks the end of the AGPLv3 text. The following text is appended to the same file for convience but constituting a distinct document, not part of the actual AGPL text and not part of an attempt to create a deriviative work based on the AGPLv3 text. ========================================================================= ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX Permissive Terms Supplementing the License 1. Remote Interaction Through IP Networks. OsmoTRX is an implementation of the GSM network cellular air interface, as well as other interfaces to IP networks. The interaction of cellular handsets with the OsmoTRX software is considered "remote network interaction" for the purposes of the Affero General Public License and cellular users are subject to the source code access requirements of Section 13 of AGPLv3 ("Remote Network Interaction; Use with the GNU General Public License"). Remote interactions through interfaces other than the GSM air interface are, at your option, exempted from the requirements of Section 13 ("Remote Network Interaction; Use with the GNU General Public License"). This exemption of interfaces other than the GSM air interface from the requirements of Section 13 is an additional permission granted to you. END OF ADDITIONAL TERMS How to comply with Section 13 of the AGPLv3 license. The recommended method for compliance with Section 13 of the AGPLv3 license is to deliver a text message to each handset that attaches to the cellular network which uses OsmoTRX. At a minimum, that text message should include the string "OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This message need not be delivered to handsets that are denied registration with the network, since those handsets have been denied service. osmo-trx-0.4.0/CommonLibs/0000755000175000017500000000000013272615542014364 5ustar rubenrubenosmo-trx-0.4.0/CommonLibs/config_defs.h0000644000175000017500000000047313272615542017007 0ustar rubenruben#pragma once /* * This file contains structures used by both VTY (C, dir CommonLibs) and * osmo-trx (CXX, dir Transceiver52) */ enum FillerType { FILLER_DUMMY, FILLER_ZERO, FILLER_NORM_RAND, FILLER_EDGE_RAND, FILLER_ACCESS_RAND, }; enum ReferenceType { REF_INTERNAL, REF_EXTERNAL, REF_GPS, }; osmo-trx-0.4.0/CommonLibs/Sockets.cpp0000644000175000017500000001543113272615542016507 0ustar rubenruben/* * Copyright 2008, 2010 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include #include #include #include #include #include "Threads.h" #include "Sockets.h" #include #include #include #include #include bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort) { assert(address); assert(hostAndPort); char *copy = strdup(hostAndPort); char *colon = strchr(copy,':'); if (!colon) return false; *colon = '\0'; char *host = copy; unsigned port = strtol(colon+1,NULL,10); bool retVal = resolveAddress(address,host,port); free(copy); return retVal; } bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port) { assert(address); assert(host); // FIXME -- Need to ignore leading/trailing spaces in hostname. struct hostent *hp; int h_errno_local; #ifdef HAVE_GETHOSTBYNAME2_R struct hostent hostData; char tmpBuffer[2048]; // There are different flavors of gethostbyname_r(), but // latest Linux use the following form: if (gethostbyname2_r(host, AF_INET, &hostData, tmpBuffer, sizeof(tmpBuffer), &hp, &h_errno_local)!=0) { CERR("WARNING -- gethostbyname2_r() failed for " << host << ", " << hstrerror(h_errno_local)); return false; } #else static Mutex sGethostbynameMutex; // gethostbyname() is NOT thread-safe, so we should use a mutex here. // Ideally it should be a global mutex for all non thread-safe socket // operations and it should protect access to variables such as // global h_errno. sGethostbynameMutex.lock(); hp = gethostbyname(host); h_errno_local = h_errno; sGethostbynameMutex.unlock(); #endif if (hp==NULL) { CERR("WARNING -- gethostbyname() failed for " << host << ", " << hstrerror(h_errno_local)); return false; } if (hp->h_addrtype != AF_INET) { CERR("WARNING -- gethostbyname() resolved " << host << " to something other then AF_INET"); return false; } address->sin_family = hp->h_addrtype; assert(sizeof(address->sin_addr) == hp->h_length); memcpy(&(address->sin_addr), hp->h_addr_list[0], hp->h_length); address->sin_port = htons(port); return true; } DatagramSocket::DatagramSocket() { memset(mDestination, 0, sizeof(mDestination)); } void DatagramSocket::nonblocking() { fcntl(mSocketFD,F_SETFL,O_NONBLOCK); } void DatagramSocket::blocking() { fcntl(mSocketFD,F_SETFL,0); } void DatagramSocket::close() { ::close(mSocketFD); } DatagramSocket::~DatagramSocket() { close(); } int DatagramSocket::write( const char * message, size_t length ) { assert(length<=MAX_UDP_LENGTH); int retVal = sendto(mSocketFD, message, length, 0, (struct sockaddr *)mDestination, addressSize()); if (retVal == -1 ) perror("DatagramSocket::write() failed"); return retVal; } int DatagramSocket::writeBack( const char * message, size_t length ) { assert(length<=MAX_UDP_LENGTH); int retVal = sendto(mSocketFD, message, length, 0, (struct sockaddr *)mSource, addressSize()); if (retVal == -1 ) perror("DatagramSocket::write() failed"); return retVal; } int DatagramSocket::write( const char * message) { size_t length=strlen(message)+1; return write(message,length); } int DatagramSocket::writeBack( const char * message) { size_t length=strlen(message)+1; return writeBack(message,length); } int DatagramSocket::send(const struct sockaddr* dest, const char * message, size_t length ) { assert(length<=MAX_UDP_LENGTH); int retVal = sendto(mSocketFD, message, length, 0, dest, addressSize()); if (retVal == -1 ) perror("DatagramSocket::send() failed"); return retVal; } int DatagramSocket::send(const struct sockaddr* dest, const char * message) { size_t length=strlen(message)+1; return send(dest,message,length); } int DatagramSocket::read(char* buffer, size_t length) { socklen_t addr_len = sizeof(mSource); int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0, (struct sockaddr*) &mSource, &addr_len); if ((rd_length==-1) && (errno!=EAGAIN)) { perror("DatagramSocket::read() failed"); throw SocketError(); } return rd_length; } int DatagramSocket::read(char* buffer, size_t length, unsigned timeout) { fd_set fds; FD_ZERO(&fds); FD_SET(mSocketFD,&fds); struct timeval tv; tv.tv_sec = timeout/1000; tv.tv_usec = (timeout%1000)*1000; int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv); if (sel<0) { perror("DatagramSocket::read() select() failed"); throw SocketError(); } if (sel==0) return -1; if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length); return -1; } UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort) :DatagramSocket() { open(wSrcPort, wSrcIP); } UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort, const char *wDestIP, unsigned short wDestPort) :DatagramSocket() { open(wSrcPort, wSrcIP); destination(wDestPort, wDestIP); } void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP ) { resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort ); } void UDPSocket::open(unsigned short localPort, const char *wlocalIP) { // create mSocketFD = socket(AF_INET,SOCK_DGRAM,0); if (mSocketFD<0) { perror("socket() failed"); throw SocketError(); } // pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes. int on = 1; setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); // bind struct sockaddr_in address; size_t length = sizeof(address); bzero(&address,length); address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr(wlocalIP); address.sin_port = htons(localPort); if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { perror("bind() failed"); throw SocketError(); } } unsigned short UDPSocket::port() const { struct sockaddr_in name; socklen_t nameSize = sizeof(name); int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize); if (retVal==-1) throw SocketError(); return ntohs(name.sin_port); } // vim:ts=4:sw=4 osmo-trx-0.4.0/CommonLibs/BitVector.h0000644000175000017500000001505213272615542016441 0ustar rubenruben/* * Copyright 2008, 2009 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef FECVECTORS_H #define FECVECTORS_H #include "Vector.h" #include class BitVector : public Vector { public: /**@name Constructors. */ //@{ /**@name Casts of Vector constructors. */ //@{ BitVector(char* wData, char* wStart, char* wEnd) :Vector(wData,wStart,wEnd) { } BitVector(size_t len=0):Vector(len) {} BitVector(const Vector& source):Vector(source) {} BitVector(Vector& source):Vector(source) {} BitVector(const Vector& source1, const Vector source2):Vector(source1,source2) {} //@} /** Construct from a string of "0" and "1". */ BitVector(const char* valString); //@} /** Index a single bit. */ bool bit(size_t index) const { // We put this code in .h for fast inlining. const char *dp = mStart+index; assert(dp::segment(start,span)); } BitVector head(size_t span) { return segment(0,span); } const BitVector head(size_t span) const { return segment(0,span); } BitVector tail(size_t start) { return segment(start,size()-start); } const BitVector tail(size_t start) const { return segment(start,size()-start); } //@} void zero() { fill(0); } /** Invert 0<->1. */ void invert(); /**@name Byte-wise operations. */ //@{ /** Reverse an 8-bit vector. */ void reverse8(); /** Reverse groups of 8 within the vector (byte reversal). */ void LSB8MSB(); //@} /**@name Serialization and deserialization. */ //@{ uint64_t peekField(size_t readIndex, unsigned length) const; uint64_t peekFieldReversed(size_t readIndex, unsigned length) const; uint64_t readField(size_t& readIndex, unsigned length) const; uint64_t readFieldReversed(size_t& readIndex, unsigned length) const; void fillField(size_t writeIndex, uint64_t value, unsigned length); void fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length); void writeField(size_t& writeIndex, uint64_t value, unsigned length); void writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length); void write0(size_t& writeIndex) { writeField(writeIndex,0,1); } void write1(size_t& writeIndex) { writeField(writeIndex,1,1); } //@} /** Sum of bits. */ unsigned sum() const; /** Reorder bits, dest[i] = this[map[i]]. */ void map(const unsigned *map, size_t mapSize, BitVector& dest) const; /** Reorder bits, dest[map[i]] = this[i]. */ void unmap(const unsigned *map, size_t mapSize, BitVector& dest) const; /** Pack into a char array. */ void pack(unsigned char*) const; /** Unpack from a char array. */ void unpack(const unsigned char*); /** Make a hexdump string. */ void hex(std::ostream&) const; std::string hexstr() const; /** Unpack from a hexdump string. * @returns true on success, false on error. */ bool unhex(const char*); void set(BitVector other) // That's right. No ampersand. { clear(); mData=other.mData; mStart=other.mStart; mEnd=other.mEnd; other.mData=NULL; } void settfb(int i, int j) const { mStart[i] = j; } }; std::ostream& operator<<(std::ostream&, const BitVector&); /** The SoftVector class is used to represent a soft-decision signal. Values 0..1 represent probabilities that a bit is "true". */ class SoftVector: public Vector { public: /** Build a SoftVector of a given length. */ SoftVector(size_t wSize=0):Vector(wSize) {} /** Construct a SoftVector from a C string of "0", "1", and "X". */ SoftVector(const char* valString); /** Construct a SoftVector from a BitVector. */ SoftVector(const BitVector& source); /** Wrap a SoftVector around a block of floats. The block will be delete[]ed upon desctuction. */ SoftVector(float *wData, unsigned length) :Vector(wData,length) {} SoftVector(float* wData, float* wStart, float* wEnd) :Vector(wData,wStart,wEnd) { } /** Casting from a Vector. Note that this is NOT pass-by-reference. */ SoftVector(Vector source) :Vector(source) {} /**@name Casts and overrides of Vector operators. */ //@{ SoftVector segment(size_t start, size_t span) { float* wStart = mStart + start; float* wEnd = wStart + span; assert(wEnd<=mEnd); return SoftVector(NULL,wStart,wEnd); } SoftVector alias() { return segment(0,size()); } const SoftVector segment(size_t start, size_t span) const { return (SoftVector)(Vector::segment(start,span)); } SoftVector head(size_t span) { return segment(0,span); } const SoftVector head(size_t span) const { return segment(0,span); } SoftVector tail(size_t start) { return segment(start,size()-start); } const SoftVector tail(size_t start) const { return segment(start,size()-start); } //@} // How good is the SoftVector in the sense of the bits being solid? // Result of 1 is perfect and 0 means all the bits were 0.0 // If plow is non-NULL, also return the lowest energy bit. float getEnergy(float *low=0) const; /** Fill with "unknown" values. */ void unknown() { fill(0.0F); } /** Return a hard bit value from a given index by slicing. */ bool bit(size_t index) const { const float *dp = mStart+index; assert(dp0.0F; } /** Slice the whole signal into bits. */ BitVector sliced() const; }; std::ostream& operator<<(std::ostream&, const SoftVector&); #endif // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/Timeval.cpp0000644000175000017500000000437013272615542016475 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "Timeval.h" using namespace std; void Timeval::future(unsigned offset) { now(); unsigned sec = offset/1000; unsigned msec = offset%1000; mTimeval.tv_usec += msec*1000; mTimeval.tv_sec += sec; if (mTimeval.tv_usec>1000000) { mTimeval.tv_usec -= 1000000; mTimeval.tv_sec += 1; } } struct timespec Timeval::timespec() const { struct timespec retVal; retVal.tv_sec = mTimeval.tv_sec; retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec; return retVal; } bool Timeval::passed() const { Timeval nowTime; if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false; if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true; if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true; return false; } double Timeval::seconds() const { return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec); } long Timeval::delta(const Timeval& other) const { // 2^31 milliseconds is just over 4 years. int32_t deltaS = other.sec() - sec(); int32_t deltaUs = other.usec() - usec(); return 1000*deltaS + deltaUs/1000; } ostream& operator<<(ostream& os, const Timeval& tv) { os.setf( ios::fixed, ios::floatfield ); os << tv.seconds(); return os; } ostream& operator<<(ostream& os, const struct timespec& ts) { os << ts.tv_sec << "," << ts.tv_nsec; return os; } // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/debug.h0000644000175000017500000000014613272615542015624 0ustar rubenruben#pragma once extern const struct log_info log_info; /* Debug Areas of the code */ enum { DMAIN, }; osmo-trx-0.4.0/CommonLibs/Threads.cpp0000644000175000017500000000472013272615542016465 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "Threads.h" #include "Timeval.h" using namespace std; Mutex gStreamLock; ///< Global lock to control access to cout and cerr. void lockCout() { gStreamLock.lock(); Timeval entryTime; cout << entryTime << " " << pthread_self() << ": "; } void unlockCout() { cout << dec << endl << flush; gStreamLock.unlock(); } void lockCerr() { gStreamLock.lock(); Timeval entryTime; cerr << entryTime << " " << pthread_self() << ": "; } void unlockCerr() { cerr << dec << endl << flush; gStreamLock.unlock(); } Mutex::Mutex() { bool res; res = pthread_mutexattr_init(&mAttribs); assert(!res); res = pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE); assert(!res); res = pthread_mutex_init(&mMutex,&mAttribs); assert(!res); } Mutex::~Mutex() { pthread_mutex_destroy(&mMutex); bool res = pthread_mutexattr_destroy(&mAttribs); assert(!res); } /** Block for the signal up to the cancellation timeout. */ void Signal::wait(Mutex& wMutex, unsigned timeout) const { Timeval then(timeout); struct timespec waitTime = then.timespec(); pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime); } void Thread::start(void *(*task)(void*), void *arg) { assert(mThread==((pthread_t)0)); bool res; // (pat) Moved initialization to constructor to avoid crash in destructor. //res = pthread_attr_init(&mAttrib); //assert(!res); res = pthread_attr_setstacksize(&mAttrib, mStackSize); assert(!res); res = pthread_create(&mThread, &mAttrib, task, arg); assert(!res); } // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/Makefile.am0000644000175000017500000000272413272615542016425 0ustar rubenruben# # Copyright 2008, 2009 Free Software Foundation, Inc. # Copyright 2011, 2012 Range Networks, Inc. # # This software is distributed under the terms of the GNU Public License. # See the COPYING file in the main directory for details. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # include $(top_srcdir)/Makefile.common AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) noinst_LTLIBRARIES = libcommon.la libcommon_la_SOURCES = \ BitVector.cpp \ LinkedLists.cpp \ Sockets.cpp \ Threads.cpp \ Timeval.cpp \ Logger.cpp \ trx_vty.c \ debug.c noinst_HEADERS = \ BitVector.h \ PRBS.h \ Interthread.h \ LinkedLists.h \ Sockets.h \ Threads.h \ Timeval.h \ Vector.h \ Logger.h \ trx_vty.h \ debug.h \ config_defs.h osmo-trx-0.4.0/CommonLibs/Threads.h0000644000175000017500000001022513272615542016127 0ustar rubenruben/* * Copyright 2008, 2011 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef THREADS_H #define THREADS_H #include #include #include #include class Mutex; /**@name Multithreaded access for standard streams. */ //@{ /**@name Functions for gStreamLock. */ //@{ extern Mutex gStreamLock; ///< global lock for cout and cerr void lockCerr(); ///< call prior to writing cerr void unlockCerr(); ///< call after writing cerr void lockCout(); ///< call prior to writing cout void unlockCout(); ///< call after writing cout //@} /**@name Macros for standard messages. */ //@{ #define COUT(text) { lockCout(); std::cout << text; unlockCout(); } #define CERR(text) { lockCerr(); std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; unlockCerr(); } #ifdef NDEBUG #define DCOUT(text) {} #define OBJDCOUT(text) {} #else #define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); } #define OBJDCOUT(text) { DCOUT(this << " " << text); } #endif //@} //@} /**@defgroup C++ wrappers for pthread mechanisms. */ //@{ /** A class for recursive mutexes based on pthread_mutex. */ class Mutex { private: pthread_mutex_t mMutex; pthread_mutexattr_t mAttribs; public: Mutex(); ~Mutex(); void lock() { pthread_mutex_lock(&mMutex); } bool trylock() { return pthread_mutex_trylock(&mMutex)==0; } void unlock() { pthread_mutex_unlock(&mMutex); } friend class Signal; }; class ScopedLock { private: Mutex& mMutex; public: ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); } ~ScopedLock() { mMutex.unlock(); } }; /** A C++ interthread signal based on pthread condition variables. */ class Signal { private: mutable pthread_cond_t mSignal; public: Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); } ~Signal() { pthread_cond_destroy(&mSignal); } /** Block for the signal up to the cancellation timeout. Under Linux, spurious returns are possible. */ void wait(Mutex& wMutex, unsigned timeout) const; /** Block for the signal. Under Linux, spurious returns are possible. */ void wait(Mutex& wMutex) const { pthread_cond_wait(&mSignal,&wMutex.mMutex); } void signal() { pthread_cond_signal(&mSignal); } void broadcast() { pthread_cond_broadcast(&mSignal); } }; #define START_THREAD(thread,function,argument) \ thread.start((void *(*)(void*))function, (void*)argument); /** A C++ wrapper for pthread threads. */ class Thread { private: pthread_t mThread; pthread_attr_t mAttrib; // FIXME -- Can this be reduced now? size_t mStackSize; public: /** Create a thread in a non-running state. */ Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) { pthread_attr_init(&mAttrib); // (pat) moved this here. mStackSize=wStackSize; } /** Destroy the Thread. It should be stopped and joined. */ // (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops. ~Thread() { pthread_attr_destroy(&mAttrib); } /** Start the thread on a task. */ void start(void *(*task)(void*), void *arg); /** Join a thread that will stop on its own. */ void join() { if (mThread) { int s = pthread_join(mThread, NULL); assert(!s); } } /** Send cancelation to thread */ void cancel() { pthread_cancel(mThread); } }; #endif // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/BitVector.cpp0000644000175000017500000001546213272615542017001 0ustar rubenruben/* * Copyright 2008, 2009 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "BitVector.h" #include #include #include #include using namespace std; /** Apply a Galois polymonial to a binary seqeunce. @param val The input sequence. @param poly The polynomial. @param order The order of the polynomial. @return Single-bit result. */ unsigned applyPoly(uint64_t val, uint64_t poly, unsigned order) { uint64_t prod = val & poly; unsigned sum = prod; for (unsigned i=1; i>i; return sum & 0x01; } BitVector::BitVector(const char *valString) :Vector(strlen(valString)) { uint32_t accum = 0; for (size_t i=0; i=0; i--) { accum = (accum<<1) | ((*dp--) & 0x01); } return accum; } uint64_t BitVector::readField(size_t& readIndex, unsigned length) const { const uint64_t retVal = peekField(readIndex,length); readIndex += length; return retVal; } uint64_t BitVector::readFieldReversed(size_t& readIndex, unsigned length) const { const uint64_t retVal = peekFieldReversed(readIndex,length); readIndex += length; return retVal; } void BitVector::fillField(size_t writeIndex, uint64_t value, unsigned length) { char *dpBase = mStart + writeIndex; char *dp = dpBase + length - 1; assert(dp < mEnd); while (dp>=dpBase) { *dp-- = value & 0x01; value >>= 1; } } void BitVector::fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length) { char *dp = mStart + writeIndex; char *dpEnd = dp + length - 1; assert(dpEnd < mEnd); while (dp<=dpEnd) { *dp++ = value & 0x01; value >>= 1; } } void BitVector::writeField(size_t& writeIndex, uint64_t value, unsigned length) { fillField(writeIndex,value,length); writeIndex += length; } void BitVector::writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length) { fillFieldReversed(writeIndex,value,length); writeIndex += length; } void BitVector::invert() { for (size_t i=0; i=8); char tmp0 = mStart[0]; mStart[0] = mStart[7]; mStart[7] = tmp0; char tmp1 = mStart[1]; mStart[1] = mStart[6]; mStart[6] = tmp1; char tmp2 = mStart[2]; mStart[2] = mStart[5]; mStart[5] = tmp2; char tmp3 = mStart[3]; mStart[3] = mStart[4]; mStart[4] = tmp3; } void BitVector::LSB8MSB() { if (size()<8) return; size_t size8 = 8*(size()/8); size_t iTop = size8 - 8; for (size_t i=0; i<=iTop; i+=8) segment(i,8).reverse8(); } unsigned BitVector::sum() const { unsigned sum = 0; for (size_t i=0; i0.0F) newSig[i]=1; else newSig[i] = 0; } return newSig; } float SoftVector::getEnergy(float *plow) const { const SoftVector &vec = *this; int len = vec.size(); float avg = 0; float low = 1; for (int i = 0; i < len; i++) { float energy = fabsf(vec[i]); if (energy < low) low = energy; avg += energy/len; } if (plow) { *plow = low; } return avg; } ostream& operator<<(ostream& os, const SoftVector& sv) { for (size_t i=0; i0.5) os << "1"; else if (sv[i]>0.25) os << "|"; else if (sv[i]>0.0) os << "'"; else os << "-"; } return os; } void BitVector::pack(unsigned char* targ) const { // Assumes MSB-first packing. unsigned bytes = size()/8; for (unsigned i=0; i> (8-rem),rem); } void BitVector::hex(ostream& os) const { os << std::hex; unsigned digits = size()/4; size_t wp=0; for (unsigned i=0; i0) { if (sscanf(src+digits, "%1x", &val) < 1) { return false; } fillField(whole,val,rem); } return true; } // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/Logger.h0000644000175000017500000000476013272615542015763 0ustar rubenruben/* * Copyright 2009, 2010 Free Software Foundation, Inc. * Copyright 2010 Kestrel Signal Processing, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef LOGGER_H #define LOGGER_H #include #include #include #include extern "C" { #include #include "debug.h" } /* Translation for old log statements */ #ifndef LOGL_ALERT #define LOGL_ALERT LOGL_FATAL #endif #ifndef LOGL_ERR #define LOGL_ERR LOGL_ERROR #endif #ifndef LOGL_WARNING #define LOGL_WARNING LOGL_NOTICE #endif #define LOG(level) \ Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] " #define LOGC(category, level) \ Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] " /** A C++ stream-based thread-safe logger. This object is NOT the global logger; every log record is an object of this class. */ class Log { public: protected: std::ostringstream mStream; ///< This is where we buffer up the log entry. int mCategory; ///< Priority of current report. int mPriority; ///< Category of current report. const char *filename; ///< Source File Name of current report. int line; ///< Line number in source file of current report. public: Log(int wCategory, int wPriority, const char* filename, int line) : mCategory(wCategory), mPriority(wPriority), filename(filename), line(line) { } // Most of the work is in the destructor. /** The destructor actually generates the log entry. */ ~Log(); std::ostringstream& get(); }; std::ostream& operator<<(std::ostream& os, std::ostringstream& ss); #endif // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/Logger.cpp0000644000175000017500000000333213272615542016310 0ustar rubenruben/* * Copyright (C) 2018 sysmocom - s.f.m.c. GmbH * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include #include #include #include #include #include // For gettimeofday #include "Logger.h" #include "Threads.h" // pat added using namespace std; Mutex gLogToLock; std::ostream& operator<<(std::ostream& os, std::ostringstream& ss) { return os << ss.str(); } Log::~Log() { int mlen = mStream.str().size(); int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n'); const char *fmt = neednl ? "%s\n" : "%s"; ScopedLock lock(gLogToLock); // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers, // so just use std::cout. LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str()); } ostringstream& Log::get() { return mStream; } // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/Sockets.h0000644000175000017500000001153013272615542016150 0ustar rubenruben/* * Copyright 2008, 2010 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef SOCKETS_H #define SOCKETS_H #include #include #include #include #include #include #include #include #include #include #define MAX_UDP_LENGTH 1500 /** A function to resolve IP host names. */ bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port); /** Resolve an address of the form ":". */ bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort); /** An exception to throw when a critical socket operation fails. */ class SocketError {}; #define SOCKET_ERROR {throw SocketError(); } /** Abstract class for connectionless sockets. */ class DatagramSocket { protected: int mSocketFD; ///< underlying file descriptor char mDestination[256]; ///< address to which packets are sent char mSource[256]; ///< return address of most recent received packet public: /** An almost-does-nothing constructor. */ DatagramSocket(); virtual ~DatagramSocket(); /** Return the address structure size for this socket type. */ virtual size_t addressSize() const = 0; /** Send a binary packet. @param buffer The data bytes to send to mDestination. @param length Number of bytes to send, or strlen(buffer) if defaulted to -1. @return number of bytes written, or -1 on error. */ int write( const char * buffer, size_t length); /** Send a C-style string packet. @param buffer The data bytes to send to mDestination. @return number of bytes written, or -1 on error. */ int write( const char * buffer); /** Send a binary packet. @param buffer The data bytes to send to mSource. @param length Number of bytes to send, or strlen(buffer) if defaulted to -1. @return number of bytes written, or -1 on error. */ int writeBack(const char * buffer, size_t length); /** Send a C-style string packet. @param buffer The data bytes to send to mSource. @return number of bytes written, or -1 on error. */ int writeBack(const char * buffer); /** Receive a packet. @param buffer A char[MAX_UDP_LENGTH] procured by the caller. @return The number of bytes received or -1 on non-blocking pass. */ int read(char* buffer, size_t length); /** Receive a packet with a timeout. @param buffer A char[MAX_UDP_LENGTH] procured by the caller. @param maximum wait time in milliseconds @return The number of bytes received or -1 on timeout. */ int read(char* buffer, size_t length, unsigned timeout); /** Send a packet to a given destination, other than the default. */ int send(const struct sockaddr *dest, const char * buffer, size_t length); /** Send a C-style string to a given destination, other than the default. */ int send(const struct sockaddr *dest, const char * buffer); /** Make the socket non-blocking. */ void nonblocking(); /** Make the socket blocking (the default). */ void blocking(); /** Close the socket. */ void close(); }; /** UDP/IP User Datagram Socket */ class UDPSocket : public DatagramSocket { public: /** Open a USP socket with an OS-assigned port and no default destination. */ UDPSocket(const char *localIP, unsigned short localPort); /** Given a full specification, open the socket and set the dest address. */ UDPSocket(const char *localIP, unsigned short localPort, const char *remoteIP, unsigned short remotePort); /** Set the destination port. */ void destination( unsigned short wDestPort, const char * wDestIP ); /** Return the actual port number in use. */ unsigned short port() const; /** Open and bind the UDP socket to a local port. */ void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1"); /** Give the return address of the most recently received packet. */ const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; } size_t addressSize() const { return sizeof(struct sockaddr_in); } }; #endif // vim:ts=4:sw=4 osmo-trx-0.4.0/CommonLibs/LinkedLists.h0000644000175000017500000001064613272615542016771 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef LINKEDLISTS_H #define LINKEDLISTS_H #include #include /** This node class is used to build singly-linked lists. */ class ListNode { private: ListNode* mNext; void* mData; public: ListNode* next() { return mNext; } void next(ListNode* wNext) { mNext=wNext; } void* data() { return mData; } void data(void* wData) { mData=wData; } }; /** A fast FIFO for pointer-based storage. */ class PointerFIFO { protected: ListNode* mHead; ///< points to next item out ListNode* mTail; ///< points to last item in ListNode* mFreeList; ///< pool of previously-allocated nodes unsigned mSize; ///< number of items in the FIFO public: PointerFIFO() :mHead(NULL),mTail(NULL),mFreeList(NULL), mSize(0) {} unsigned size() const { return mSize; } unsigned totalSize() const { return 0; } // Not used in this version. /** Put an item into the FIFO at the back of the queue. */ void put(void* val); /** Push an item on the front of the FIFO. */ void push_front(void*val); // pat added. /** Take an item from the FIFO. Returns NULL for empty list. */ void* get(); /** Peek at front item without removal. */ void *front() { return mHead ? mHead->data() : 0; } // pat added private: /** Allocate a new node to extend the FIFO. */ ListNode *allocate(); /** Release a node to the free pool after removal from the FIFO. */ void release(ListNode* wNode); }; // This is the default type for SingleLinkList Node element; // You can derive your class directly from this, but then you must add type casts // all over the place. class SingleLinkListNode { public: SingleLinkListNode *mNext; SingleLinkListNode *next() {return mNext;} void setNext(SingleLinkListNode *item) {mNext=item;} SingleLinkListNode() : mNext(0) {} virtual unsigned size() { return 0; } }; // A single-linked lists of elements with internal pointers. // The methods must match those from SingleLinkListNode. // This class also assumes the Node has a size() method, and accumulates // the total size of elements in the list in totalSize(). template class SingleLinkList { Node *mHead, *mTail; unsigned mSize; // Number of elements in list. unsigned mTotalSize; // Total of size() method of elements in list. public: SingleLinkList() : mHead(0), mTail(0), mSize(0), mTotalSize(0) {} unsigned size() const { return mSize; } unsigned totalSize() const { return mTotalSize; } Node *pop_back() { assert(0); } // Not efficient with this type of list. Node *pop_front() { if (!mHead) return NULL; Node *result = mHead; mHead = mHead->next(); if (mTail == result) { mTail = NULL; assert(mHead == NULL); } result->setNext(NULL); // be neat mSize--; mTotalSize -= result->size(); return result; } void push_front(Node *item) { item->setNext(mHead); mHead = item; if (!mTail) { mTail = item; } mSize++; mTotalSize += item->size(); } void push_back(Node *item) { item->setNext(NULL); if (mTail) { mTail->setNext(item); } mTail = item; if (!mHead) mHead = item; mSize++; mTotalSize += item->size(); } Node *front() { return mHead; } Node *back() { return mTail; } // Interface to InterthreadQueue so it can used SingleLinkList as the Fifo. void put(void *val) { push_back((Node*)val); } void *get() { return pop_front(); } }; #endif // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/Timeval.h0000644000175000017500000000511513272615542016140 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef TIMEVAL_H #define TIMEVAL_H #include #include "sys/time.h" #include #include /** A wrapper on usleep to sleep for milliseconds. */ inline void msleep(long v) { usleep(v*1000); } /** A C++ wrapper for struct timeval. */ class Timeval { private: struct timeval mTimeval; public: /** Set the value to gettimeofday. */ void now() { gettimeofday(&mTimeval,NULL); } /** Set the value to gettimeofday plus an offset. */ void future(unsigned ms); //@{ Timeval(unsigned sec, unsigned usec) { mTimeval.tv_sec = sec; mTimeval.tv_usec = usec; } Timeval(const struct timeval& wTimeval) :mTimeval(wTimeval) {} /** Create a Timeval offset into the future. @param offset milliseconds */ Timeval(unsigned offset=0) { future(offset); } //@} /** Convert to a struct timespec. */ struct timespec timespec() const; /** Return total seconds. */ double seconds() const; uint32_t sec() const { return mTimeval.tv_sec; } uint32_t usec() const { return mTimeval.tv_usec; } /** Return differnce from other (other-self), in ms. */ long delta(const Timeval& other) const; /** Elapsed time in ms. */ long elapsed() const { return delta(Timeval()); } /** Remaining time in ms. */ long remaining() const { return -elapsed(); } /** Return true if the time has passed, as per gettimeofday. */ bool passed() const; /** Add a given number of minutes to the time. */ void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; } }; std::ostream& operator<<(std::ostream& os, const Timeval&); std::ostream& operator<<(std::ostream& os, const struct timespec&); #endif // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/Vector.h0000644000175000017500000001615213272615542016004 0ustar rubenruben/**@file Simplified Vector template with aliases. */ /* * Copyright 2008 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef VECTOR_H #define VECTOR_H #include #include #include // We cant use Logger.h in this file... extern int gVectorDebug; #define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;} /** A simplified Vector template with aliases. Unlike std::vector, this class does not support dynamic resizing. Unlike std::vector, this class does support "aliases" and subvectors. */ template class Vector { // TODO -- Replace memcpy calls with for-loops. public: /**@name Iterator types. */ //@{ typedef T* iterator; typedef const T* const_iterator; //@} protected: T* mData; ///< allocated data block, if any T* mStart; ///< start of useful data T* mEnd; ///< end of useful data + 1 public: /**** char *inspect() { static char buf[100]; sprintf(buf," mData=%p mStart=%p mEnd=%p ",mData,mStart,mEnd); return buf; } ***/ /** Return the size of the Vector. */ size_t size() const { assert(mStart>=mData); assert(mEnd>=mStart); return mEnd - mStart; } /** Return size in bytes. */ size_t bytes() const { return size()*sizeof(T); } /** Change the size of the Vector, discarding content. */ void resize(size_t newSize) { if (mData!=NULL) delete[] mData; if (newSize==0) mData=NULL; else mData = new T[newSize]; mStart = mData; mEnd = mStart + newSize; } /** Reduce addressable size of the Vector, keeping content. */ void shrink(size_t newSize) { assert(newSize <= mEnd - mStart); mEnd = mStart + newSize; } /** Release memory and clear pointers. */ void clear() { resize(0); } /** Copy data from another vector. */ void clone(const Vector& other) { resize(other.size()); memcpy(mData,other.mStart,other.bytes()); } //@{ /** Build an empty Vector of a given size. */ Vector(size_t wSize=0):mData(NULL) { resize(wSize); } /** Build a Vector by moving another. */ Vector(Vector&& other) :mData(other.mData),mStart(other.mStart),mEnd(other.mEnd) { other.mData=NULL; } /** Build a Vector by copying another. */ Vector(const Vector& other):mData(NULL) { clone(other); } /** Build a Vector with explicit values. */ Vector(T* wData, T* wStart, T* wEnd) :mData(wData),mStart(wStart),mEnd(wEnd) { } /** Build a vector from an existing block, NOT to be deleted upon destruction. */ Vector(T* wStart, size_t span) :mData(NULL),mStart(wStart),mEnd(wStart+span) { } /** Build a Vector by concatenation. */ Vector(const Vector& other1, const Vector& other2) :mData(NULL) { resize(other1.size()+other2.size()); memcpy(mStart, other1.mStart, other1.bytes()); memcpy(mStart+other1.size(), other2.mStart, other2.bytes()); } //@} /** Destroy a Vector, deleting held memory. */ ~Vector() { clear(); } //@{ /** Assign from another Vector, shifting ownership. */ void operator=(Vector& other) { clear(); mData=other.mData; mStart=other.mStart; mEnd=other.mEnd; other.mData=NULL; } /** Assign from another Vector, copying. */ void operator=(const Vector& other) { clone(other); } //@} //@{ /** Return an alias to a segment of this Vector. */ Vector segment(size_t start, size_t span) { T* wStart = mStart + start; T* wEnd = wStart + span; assert(wEnd<=mEnd); return Vector(NULL,wStart,wEnd); } /** Return an alias to a segment of this Vector. */ const Vector segment(size_t start, size_t span) const { T* wStart = mStart + start; T* wEnd = wStart + span; assert(wEnd<=mEnd); return Vector(NULL,wStart,wEnd); } Vector head(size_t span) { return segment(0,span); } const Vector head(size_t span) const { return segment(0,span); } Vector tail(size_t start) { return segment(start,size()-start); } const Vector tail(size_t start) const { return segment(start,size()-start); } /** Copy part of this Vector to a segment of another Vector. @param other The other vector. @param start The start point in the other vector. @param span The number of elements to copy. */ void copyToSegment(Vector& other, size_t start, size_t span) const { T* base = other.mStart + start; assert(base+span<=other.mEnd); assert(mStart+span<=mEnd); memcpy(base,mStart,span*sizeof(T)); } /** Copy all of this Vector to a segment of another Vector. */ void copyToSegment(Vector& other, size_t start=0) const { copyToSegment(other,start,size()); } void copyTo(Vector& other) const { copyToSegment(other,0,size()); } /** Copy a segment of this vector into another. @param other The other vector (to copt into starting at 0.) @param start The start point in this vector. @param span The number of elements to copy. */ void segmentCopyTo(Vector& other, size_t start, size_t span) const { const T* base = mStart + start; assert(base+span<=mEnd); assert(other.mStart+span<=other.mEnd); memcpy(other.mStart,base,span*sizeof(T)); } /** Move (copy) a segment of this vector into a different position in the vector @param from Start point from which to copy. @param to Start point to which to copy. @param span The number of elements to copy. */ void segmentMove(size_t from, size_t to, size_t span) { const T* baseFrom = mStart + from; T* baseTo = mStart + to; assert(baseFrom+span<=mEnd); assert(baseTo+span<=mEnd); memmove(baseTo,baseFrom,span*sizeof(T)); } void fill(const T& val) { T* dp=mStart; while (dp std::ostream& operator<<(std::ostream& os, const Vector& v) { for (unsigned i=0; i * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PRBS_H #define PRBS_H #include #include /** Pseudo-random binary sequence (PRBS) generator (a Galois LFSR implementation). */ class PRBS { public: PRBS(unsigned wLen, uint64_t wCoeff, uint64_t wState = 0x01) : mCoeff(wCoeff), mStartState(wState), mState(wState), mLen(wLen) { assert(wLen<=64); } /**@name Accessors */ //@{ uint64_t coeff() const { return mCoeff; } uint64_t state() const { return mState; } void state(uint64_t state) { mState = state & mask(); } unsigned size() const { return mLen; } //@} /** Calculate one bit of a PRBS */ unsigned generateBit() { const unsigned result = mState & 0x01; processBit(result); return result; } /** Update the generator state by one bit. If you want to synchronize your PRBS to a known state, call this function size() times passing your PRBS to it bit by bit. */ void processBit(unsigned inBit) { mState >>= 1; if (inBit) mState ^= mCoeff; } /** Return true when PRBS is wrapping through initial state */ bool isFinished() const { return mStartState == mState; } protected: uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent. uint64_t mStartState; ///< initial shift register state. uint64_t mState; ///< shift register state. unsigned mLen; ///< number of bits used in shift register /** Return mask for the state register */ uint64_t mask() const { return (mLen==64)?0xFFFFFFFFFFFFFFFFUL:((1< #include #include "debug.h" /* default categories */ static const struct log_info_cat default_categories[] = { [DMAIN] = { .name = "DMAIN", .description = "Main generic category", .color = NULL, .enabled = 1, .loglevel = LOGL_NOTICE, }, }; const struct log_info log_info = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; osmo-trx-0.4.0/CommonLibs/trx_vty.c0000644000175000017500000003631313272615542016255 0ustar rubenruben/* * Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include "trx_vty.h" #include "../config.h" static struct trx_ctx* g_trx_ctx; static const struct value_string clock_ref_names[] = { { REF_INTERNAL, "internal" }, { REF_EXTERNAL, "external" }, { REF_GPS, "gspdo" }, { 0, NULL } }; static const struct value_string filler_names[] = { { FILLER_DUMMY, "Dummy bursts" }, { FILLER_ZERO, "Disabled" }, { FILLER_NORM_RAND, "Normal bursts with random payload" }, { FILLER_EDGE_RAND, "EDGE bursts with random payload" }, { FILLER_ACCESS_RAND, "Access bursts with random payload" }, { 0, NULL } }; struct trx_ctx *trx_from_vty(struct vty *v) { /* It can't hurt to force callers to continue to pass the vty instance * to this function, in case we'd like to retrieve the global * trx instance from the vty at some point in the future. But * until then, just return the global pointer, which should have been * initialized by trx_vty_init(). */ OSMO_ASSERT(g_trx_ctx); return g_trx_ctx; } enum trx_vty_node { TRX_NODE = _LAST_OSMOVTY_NODE + 1, CHAN_NODE, }; static struct cmd_node trx_node = { TRX_NODE, "%s(config-trx)# ", 1, }; static struct cmd_node chan_node = { CHAN_NODE, "%s(config-trx-chan)# ", 1, }; DEFUN(cfg_trx, cfg_trx_cmd, "trx", "Configure the TRX\n") { struct trx_ctx *trx = trx_from_vty(vty); if (!trx) return CMD_WARNING; vty->node = TRX_NODE; return CMD_SUCCESS; } DEFUN(cfg_bind_ip, cfg_bind_ip_cmd, "bind-ip A.B.C.D", "Set the IP address for the local bind\n" "IPv4 Address\n") { struct trx_ctx *trx = trx_from_vty(vty); osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_remote_ip, cfg_remote_ip_cmd, "remote-ip A.B.C.D", "Set the IP address for the remote BTS\n" "IPv4 Address\n") { struct trx_ctx *trx = trx_from_vty(vty); osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_base_port, cfg_base_port_cmd, "base-port <1-65535>", "Set the TRX Base Port\n" "TRX Base Port\n") { struct trx_ctx *trx = trx_from_vty(vty); trx->cfg.base_port = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_dev_args, cfg_dev_args_cmd, "dev-args DESC", "Set the device-specific arguments to pass to the device\n" "Device-specific arguments\n") { struct trx_ctx *trx = trx_from_vty(vty); osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_tx_sps, cfg_tx_sps_cmd, "tx-sps (1|4)", "Set the Tx Samples-per-Symbol\n" "Tx Samples-per-Symbol\n") { struct trx_ctx *trx = trx_from_vty(vty); trx->cfg.tx_sps = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_rx_sps, cfg_rx_sps_cmd, "rx-sps (1|4)", "Set the Rx Samples-per-Symbol\n" "Rx Samples-per-Symbol\n") { struct trx_ctx *trx = trx_from_vty(vty); trx->cfg.rx_sps = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd, "test rtsc <0-7>", "Set the Random Normal Burst test mode with TSC\n" "TSC\n") { struct trx_ctx *trx = trx_from_vty(vty); if (trx->cfg.rach_delay_set) { vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s", VTY_NEWLINE); return CMD_WARNING; } trx->cfg.rtsc_set = true; trx->cfg.rtsc = atoi(argv[0]); if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */ trx->cfg.filler = FILLER_NORM_RAND; return CMD_SUCCESS; } DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd, "test rach-delay <0-68>", "Set the Random Access Burst test mode with delay\n" "RACH delay\n") { struct trx_ctx *trx = trx_from_vty(vty); if (trx->cfg.rtsc_set) { vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s", VTY_NEWLINE); return CMD_WARNING; } if (trx->cfg.egprs) { vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s", VTY_NEWLINE); return CMD_WARNING; } trx->cfg.rach_delay_set = true; trx->cfg.rach_delay = atoi(argv[0]); trx->cfg.filler = FILLER_ACCESS_RAND; return CMD_SUCCESS; } DEFUN(cfg_clock_ref, cfg_clock_ref_cmd, "clock-ref (internal|external|gpsdo)", "Set the Reference Clock\n" "Enable internal referece (default)\n" "Enable external 10 MHz reference\n" "Enable GPSDO reference\n") { struct trx_ctx *trx = trx_from_vty(vty); trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd, "multi-arfcn (disable|enable)", "Enable multi-ARFCN transceiver (default=disable)\n") { struct trx_ctx *trx = trx_from_vty(vty); if (strcmp("disable", argv[0]) == 0) { trx->cfg.multi_arfcn = false; } else if (strcmp("enable", argv[0]) == 0) { trx->cfg.multi_arfcn = true; } else { return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_offset, cfg_offset_cmd, "offset FLOAT", "Set the baseband frequency offset (default=0, auto)\n" "Baseband Frequency Offset\n") { struct trx_ctx *trx = trx_from_vty(vty); trx->cfg.offset = atof(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd, "rssi-offset FLOAT", "Set the RSSI to dBm offset in dB (default=0)\n" "RSSI to dBm offset in dB\n") { struct trx_ctx *trx = trx_from_vty(vty); trx->cfg.rssi_offset = atof(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_swap_channels, cfg_swap_channels_cmd, "swap-channels (disable|enable)", "Swap channels (default=disable)\n") { struct trx_ctx *trx = trx_from_vty(vty); if (strcmp("disable", argv[0]) == 0) { trx->cfg.swap_channels = false; } else if (strcmp("enable", argv[0]) == 0) { trx->cfg.swap_channels = true; } else { return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_egprs, cfg_egprs_cmd, "egprs (disable|enable)", "Enable EDGE receiver (default=disable)\n") { struct trx_ctx *trx = trx_from_vty(vty); if (strcmp("disable", argv[0]) == 0) { trx->cfg.egprs = false; } else if (strcmp("enable", argv[0]) == 0) { trx->cfg.egprs = true; trx->cfg.filler = FILLER_EDGE_RAND; } else { return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_rt_prio, cfg_rt_prio_cmd, "rt-prio <1-32>", "Set the SCHED_RR real-time priority\n" "Real time priority\n") { struct trx_ctx *trx = trx_from_vty(vty); trx->cfg.sched_rr = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_filler, cfg_filler_cmd, "filler dummy", "Enable C0 filler table\n" "Dummy method\n") { struct trx_ctx *trx = trx_from_vty(vty); trx->cfg.filler = FILLER_DUMMY; return CMD_SUCCESS; } DEFUN(cfg_chan, cfg_chan_cmd, "chan <0-100>", "Select a channel to configure\n" "Channel index\n") { struct trx_ctx *trx = trx_from_vty(vty); int idx = atoi(argv[0]); if (idx >= TRX_CHAN_MAX) { vty_out(vty, "Chan list full.%s", VTY_NEWLINE); return CMD_WARNING; } if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */ vty_out(vty, "Non-existent or non-consecutive chan %d.%s", idx, VTY_NEWLINE); return CMD_WARNING; } else if (trx->cfg.num_chans == idx) { /* creating it */ trx->cfg.num_chans++; trx->cfg.chans[idx].trx = trx; trx->cfg.chans[idx].idx = idx; } vty->node = CHAN_NODE; vty->index = &trx->cfg.chans[idx]; return CMD_SUCCESS; } DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd, "rx-path NAME", "Set the Rx Path\n" "Rx Path name\n") { struct trx_chan *chan = vty->index; osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd, "tx-path NAME", "Set the Tx Path\n" "Tx Path name\n") { struct trx_chan *chan = vty->index; osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]); return CMD_SUCCESS; } static int dummy_config_write(struct vty *v) { return CMD_SUCCESS; } static int config_write_trx(struct vty *vty) { struct trx_chan *chan; int i; struct trx_ctx *trx = trx_from_vty(vty); vty_out(vty, "trx%s", VTY_NEWLINE); if (trx->cfg.bind_addr) vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE); if (trx->cfg.remote_addr) vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE); if (trx->cfg.base_port != DEFAULT_TRX_PORT) vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE); if (trx->cfg.dev_args) vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE); if (trx->cfg.tx_sps != DEFAULT_TX_SPS) vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE); if (trx->cfg.rx_sps != DEFAULT_RX_SPS) vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE); if (trx->cfg.rtsc_set) vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE); if (trx->cfg.rach_delay_set) vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE); if (trx->cfg.clock_ref != REF_INTERNAL) vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE); vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE); if (trx->cfg.offset != 0) vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE); if (trx->cfg.rssi_offset != 0) vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE); vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE); vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE); if (trx->cfg.sched_rr != 0) vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE); for (i = 0; i < trx->cfg.num_chans; i++) { chan = &trx->cfg.chans[i]; vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE); if (chan->rx_path) vty_out(vty, " rx-path %s%s", chan->rx_path, VTY_NEWLINE); if (chan->tx_path) vty_out(vty, " tx-path %s%s", chan->tx_path, VTY_NEWLINE); } return CMD_SUCCESS; } static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx) { struct trx_chan *chan; int i; vty_out(vty, "TRX Config:%s", VTY_NEWLINE); vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE); vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE); vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE); vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE); vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE); vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE); vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc, trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE); vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay, trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE); vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE); vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE); vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE); vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE); vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE); vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE); vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE); vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr, trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE); vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE); for (i = 0; i < trx->cfg.num_chans; i++) { chan = &trx->cfg.chans[i]; vty_out(vty, " Channel %u:%s", chan->idx, VTY_NEWLINE); if (chan->rx_path) vty_out(vty, " Rx Path: %s%s", chan->rx_path, VTY_NEWLINE); if (chan->tx_path) vty_out(vty, " Tx Path: %s%s", chan->tx_path, VTY_NEWLINE); } } DEFUN(show_trx, show_trx_cmd, "show trx", SHOW_STR "Display information on the TRX\n") { struct trx_ctx *trx = trx_from_vty(vty); trx_dump_vty(vty, trx); return CMD_SUCCESS; } static int trx_vty_is_config_node(struct vty *vty, int node) { switch (node) { case TRX_NODE: case CHAN_NODE: return 1; default: return 0; } } static int trx_vty_go_parent(struct vty *vty) { switch (vty->node) { case TRX_NODE: vty->node = CONFIG_NODE; vty->index = NULL; vty->index_sub = NULL; break; case CHAN_NODE: vty->node = TRX_NODE; vty->index = NULL; vty->index_sub = NULL; break; default: OSMO_ASSERT(0); } return vty->node; } static const char trx_copyright[] = "Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n" "Copyright (C) 2013 Thomas Tsou \r\n" "Copyright (C) 2015 Ettus Research LLC\r\n" "Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH \r\n" "License AGPLv3+: GNU AGPL version 3 or later \r\n" "This is free software: you are free to change and redistribute it.\r\n" "There is NO WARRANTY, to the extent permitted by law.\r\n"; struct vty_app_info g_vty_info = { .name = "OsmoTRX", .version = PACKAGE_VERSION, .copyright = trx_copyright, .go_parent_cb = trx_vty_go_parent, .is_config_node = trx_vty_is_config_node, }; struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx) { struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx); trx->cfg.bind_addr = talloc_strdup(trx, DEFAULT_TRX_IP); trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP); trx->cfg.base_port = DEFAULT_TRX_PORT; trx->cfg.tx_sps = DEFAULT_TX_SPS; trx->cfg.rx_sps = DEFAULT_RX_SPS; trx->cfg.filler = FILLER_ZERO; return trx; } int trx_vty_init(struct trx_ctx* trx) { g_trx_ctx = trx; install_element_ve(&show_trx_cmd); install_element(CONFIG_NODE, &cfg_trx_cmd); install_node(&trx_node, config_write_trx); install_element(TRX_NODE, &cfg_bind_ip_cmd); install_element(TRX_NODE, &cfg_remote_ip_cmd); install_element(TRX_NODE, &cfg_base_port_cmd); install_element(TRX_NODE, &cfg_dev_args_cmd); install_element(TRX_NODE, &cfg_tx_sps_cmd); install_element(TRX_NODE, &cfg_rx_sps_cmd); install_element(TRX_NODE, &cfg_test_rtsc_cmd); install_element(TRX_NODE, &cfg_test_rach_delay_cmd); install_element(TRX_NODE, &cfg_clock_ref_cmd); install_element(TRX_NODE, &cfg_multi_arfcn_cmd); install_element(TRX_NODE, &cfg_offset_cmd); install_element(TRX_NODE, &cfg_rssi_offset_cmd); install_element(TRX_NODE, &cfg_swap_channels_cmd); install_element(TRX_NODE, &cfg_egprs_cmd); install_element(TRX_NODE, &cfg_rt_prio_cmd); install_element(TRX_NODE, &cfg_filler_cmd); install_element(TRX_NODE, &cfg_chan_cmd); install_node(&chan_node, dummy_config_write); install_element(CHAN_NODE, &cfg_chan_rx_path_cmd); install_element(CHAN_NODE, &cfg_chan_tx_path_cmd); return 0; } osmo-trx-0.4.0/CommonLibs/trx_vty.h0000644000175000017500000000313013272615542016251 0ustar rubenruben#pragma once #include #include "config_defs.h" extern struct vty_app_info g_vty_info; #define TRX_CHAN_MAX 8 /* Samples-per-symbol for downlink path * 4 - Uses precision modulator (more computation, less distortion) * 1 - Uses minimized modulator (less computation, more distortion) * * Other values are invalid. Receive path (uplink) is always * downsampled to 1 sps. Default to 4 sps for all cases. */ #define DEFAULT_TX_SPS 4 /* * Samples-per-symbol for uplink (receiver) path * Do not modify this value. EDGE configures 4 sps automatically on * B200/B210 devices only. Use of 4 sps on the receive path for other * configurations is not supported. */ #define DEFAULT_RX_SPS 1 /* Default configuration parameters */ #define DEFAULT_TRX_PORT 5700 #define DEFAULT_TRX_IP "127.0.0.1" #define DEFAULT_CHANS 1 struct trx_ctx; struct trx_chan { struct trx_ctx *trx; /* backpointer */ unsigned int idx; /* channel index */ char *rx_path; char *tx_path; }; struct trx_ctx { struct { char *bind_addr; char *remote_addr; char *dev_args; unsigned int base_port; unsigned int tx_sps; unsigned int rx_sps; unsigned int rtsc; bool rtsc_set; unsigned int rach_delay; bool rach_delay_set; enum ReferenceType clock_ref; enum FillerType filler; bool multi_arfcn; double offset; double rssi_offset; bool swap_channels; bool egprs; unsigned int sched_rr; unsigned int num_chans; struct trx_chan chans[TRX_CHAN_MAX]; } cfg; }; int trx_vty_init(struct trx_ctx* trx); struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx); osmo-trx-0.4.0/CommonLibs/Interthread.h0000644000175000017500000003337713272615542017023 0ustar rubenruben/* * Copyright 2008, 2011 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef INTERTHREAD_H #define INTERTHREAD_H #include "Timeval.h" #include "Threads.h" #include "LinkedLists.h" #include #include #include /**@defgroup Templates for interthread mechanisms. */ //@{ /** Pointer FIFO for interthread operations. */ // (pat) The elements in the queue are type T*, and // the Fifo class implements the underlying queue. // The default is class PointerFIFO, which does not place any restrictions on the type of T, // and is implemented by allocating auxilliary structures for the queue, // or SingleLinkedList, which implements the queue using an internal pointer in type T, // which must implement the functional interface of class SingleLinkListNode, // namely: functions T*next() and void setNext(T*). template class InterthreadQueue { protected: Fifo mQ; mutable Mutex mLock; mutable Signal mWriteSignal; public: /** Delete contents. */ void clear() { ScopedLock lock(mLock); while (mQ.size()>0) delete (T*)mQ.get(); } /** Empty the queue, but don't delete. */ void flushNoDelete() { ScopedLock lock(mLock); while (mQ.size()>0) mQ.get(); } ~InterthreadQueue() { clear(); } size_t size() const { ScopedLock lock(mLock); return mQ.size(); } size_t totalSize() const // pat added { ScopedLock lock(mLock); return mQ.totalSize(); } /** Blocking read. @return Pointer to object (will not be NULL). */ T* read() { ScopedLock lock(mLock); T* retVal = (T*)mQ.get(); while (retVal==NULL) { mWriteSignal.wait(mLock); retVal = (T*)mQ.get(); } return retVal; } /** Non-blocking peek at the first element; returns NULL if empty. */ T* front() { ScopedLock lock(mLock); return (T*) mQ.front(); } /** Blocking read with a timeout. @param timeout The read timeout in ms. @return Pointer to object or NULL on timeout. */ T* read(unsigned timeout) { if (timeout==0) return readNoBlock(); Timeval waitTime(timeout); ScopedLock lock(mLock); while ((mQ.size()==0) && (!waitTime.passed())) mWriteSignal.wait(mLock,waitTime.remaining()); T* retVal = (T*)mQ.get(); return retVal; } /** Non-blocking read. @return Pointer to object or NULL if FIFO is empty. */ T* readNoBlock() { ScopedLock lock(mLock); return (T*)mQ.get(); } /** Non-blocking write. */ void write(T* val) { ScopedLock lock(mLock); mQ.put(val); mWriteSignal.signal(); } /** Non-block write to the front of the queue. */ void write_front(T* val) // pat added { ScopedLock lock(mLock); mQ.push_front(val); mWriteSignal.signal(); } }; // (pat) Identical to above but with the threading problem fixed. template class InterthreadQueue2 { protected: Fifo mQ; mutable Mutex mLock; mutable Signal mWriteSignal; public: /** Delete contents. */ void clear() { ScopedLock lock(mLock); while (mQ.size()>0) delete (T*)mQ.get(); } /** Empty the queue, but don't delete. */ void flushNoDelete() { ScopedLock lock(mLock); while (mQ.size()>0) mQ.get(); } ~InterthreadQueue2() { clear(); } size_t size() const { ScopedLock lock(mLock); return mQ.size(); } size_t totalSize() const // pat added { ScopedLock lock(mLock); return mQ.totalSize(); } /** Blocking read. @return Pointer to object (will not be NULL). */ T* read() { ScopedLock lock(mLock); T* retVal = (T*)mQ.get(); while (retVal==NULL) { mWriteSignal.wait(mLock); retVal = (T*)mQ.get(); } return retVal; } /** Non-blocking peek at the first element; returns NULL if empty. */ T* front() { ScopedLock lock(mLock); return (T*) mQ.front(); } /** Blocking read with a timeout. @param timeout The read timeout in ms. @return Pointer to object or NULL on timeout. */ T* read(unsigned timeout) { if (timeout==0) return readNoBlock(); Timeval waitTime(timeout); ScopedLock lock(mLock); while ((mQ.size()==0) && (!waitTime.passed())) mWriteSignal.wait(mLock,waitTime.remaining()); T* retVal = (T*)mQ.get(); return retVal; } /** Non-blocking read. @return Pointer to object or NULL if FIFO is empty. */ T* readNoBlock() { ScopedLock lock(mLock); return (T*)mQ.get(); } /** Non-blocking write. */ void write(T* val) { // (pat) The Mutex mLock must be released before signaling the mWriteSignal condition. // This is an implicit requirement of pthread_cond_wait() called from signal(). // If you do not do that, the InterthreadQueue read() function cannot start // because the mutex is still locked by the thread calling the write(), // so the read() thread yields its immediate execution opportunity. // This recurs (and the InterthreadQueue fills up with data) // until the read thread's accumulated temporary priority causes it to // get a second pre-emptive activation over the writing thread, // resulting in bursts of activity by the read thread. { ScopedLock lock(mLock); mQ.put(val); } mWriteSignal.signal(); } /** Non-block write to the front of the queue. */ void write_front(T* val) // pat added { // (pat) See comments above. { ScopedLock lock(mLock); mQ.push_front(val); } mWriteSignal.signal(); } }; /** Pointer FIFO for interthread operations. */ template class InterthreadQueueWithWait { protected: PointerFIFO mQ; mutable Mutex mLock; mutable Signal mWriteSignal; mutable Signal mReadSignal; virtual void freeElement(T* element) const { delete element; }; public: /** Delete contents. */ void clear() { ScopedLock lock(mLock); while (mQ.size()>0) freeElement((T*)mQ.get()); mReadSignal.signal(); } virtual ~InterthreadQueueWithWait() { clear(); } size_t size() const { ScopedLock lock(mLock); return mQ.size(); } /** Blocking read. @return Pointer to object (will not be NULL). */ T* read() { ScopedLock lock(mLock); T* retVal = (T*)mQ.get(); while (retVal==NULL) { mWriteSignal.wait(mLock); retVal = (T*)mQ.get(); } mReadSignal.signal(); return retVal; } /** Blocking read with a timeout. @param timeout The read timeout in ms. @return Pointer to object or NULL on timeout. */ T* read(unsigned timeout) { if (timeout==0) return readNoBlock(); Timeval waitTime(timeout); ScopedLock lock(mLock); while ((mQ.size()==0) && (!waitTime.passed())) mWriteSignal.wait(mLock,waitTime.remaining()); T* retVal = (T*)mQ.get(); if (retVal!=NULL) mReadSignal.signal(); return retVal; } /** Non-blocking read. @return Pointer to object or NULL if FIFO is empty. */ T* readNoBlock() { ScopedLock lock(mLock); T* retVal = (T*)mQ.get(); if (retVal!=NULL) mReadSignal.signal(); return retVal; } /** Non-blocking write. */ void write(T* val) { // (pat) 8-14: Taking out the threading problem fix temporarily for David to use in the field. ScopedLock lock(mLock); mQ.put(val); mWriteSignal.signal(); } /** Wait until the queue falls below a low water mark. */ // (pat) This function suffers from the same problem as documented // at InterthreadQueue.write(), but I am not fixing it because I cannot test it. // The caller of this function will eventually get to run, just not immediately // after the mReadSignal condition is fulfilled. void wait(size_t sz=0) { ScopedLock lock(mLock); while (mQ.size()>sz) mReadSignal.wait(mLock); } }; /** Thread-safe map of pointers to class D, keyed by class K. */ template class InterthreadMap { protected: typedef std::map Map; Map mMap; mutable Mutex mLock; Signal mWriteSignal; public: void clear() { // Delete everything in the map. ScopedLock lock(mLock); typename Map::iterator iter = mMap.begin(); while (iter != mMap.end()) { delete iter->second; ++iter; } mMap.clear(); } ~InterthreadMap() { clear(); } /** Non-blocking write. @param key The index to write to. @param wData Pointer to data, not to be deleted until removed from the map. */ void write(const K &key, D * wData) { ScopedLock lock(mLock); typename Map::iterator iter = mMap.find(key); if (iter!=mMap.end()) { delete iter->second; iter->second = wData; } else { mMap[key] = wData; } mWriteSignal.broadcast(); } /** Non-blocking read with element removal. @param key Key to read from. @return Pointer at key or NULL if key not found, to be deleted by caller. */ D* getNoBlock(const K& key) { ScopedLock lock(mLock); typename Map::iterator iter = mMap.find(key); if (iter==mMap.end()) return NULL; D* retVal = iter->second; mMap.erase(iter); return retVal; } /** Blocking read with a timeout and element removal. @param key The key to read from. @param timeout The blocking timeout in ms. @return Pointer at key or NULL on timeout, to be deleted by caller. */ D* get(const K &key, unsigned timeout) { if (timeout==0) return getNoBlock(key); Timeval waitTime(timeout); ScopedLock lock(mLock); typename Map::iterator iter = mMap.find(key); while ((iter==mMap.end()) && (!waitTime.passed())) { mWriteSignal.wait(mLock,waitTime.remaining()); iter = mMap.find(key); } if (iter==mMap.end()) return NULL; D* retVal = iter->second; mMap.erase(iter); return retVal; } /** Blocking read with and element removal. @param key The key to read from. @return Pointer at key, to be deleted by caller. */ D* get(const K &key) { ScopedLock lock(mLock); typename Map::iterator iter = mMap.find(key); while (iter==mMap.end()) { mWriteSignal.wait(mLock); iter = mMap.find(key); } D* retVal = iter->second; mMap.erase(iter); return retVal; } /** Remove an entry and delete it. @param key The key of the entry to delete. @return True if it was actually found and deleted. */ bool remove(const K &key ) { D* val = getNoBlock(key); if (!val) return false; delete val; return true; } /** Non-blocking read. @param key Key to read from. @return Pointer at key or NULL if key not found. */ D* readNoBlock(const K& key) const { D* retVal=NULL; ScopedLock lock(mLock); typename Map::const_iterator iter = mMap.find(key); if (iter!=mMap.end()) retVal = iter->second; return retVal; } /** Blocking read with a timeout. @param key The key to read from. @param timeout The blocking timeout in ms. @return Pointer at key or NULL on timeout. */ D* read(const K &key, unsigned timeout) const { if (timeout==0) return readNoBlock(key); ScopedLock lock(mLock); Timeval waitTime(timeout); typename Map::const_iterator iter = mMap.find(key); while ((iter==mMap.end()) && (!waitTime.passed())) { mWriteSignal.wait(mLock,waitTime.remaining()); iter = mMap.find(key); } if (iter==mMap.end()) return NULL; D* retVal = iter->second; return retVal; } /** Blocking read. @param key The key to read from. @return Pointer at key. */ D* read(const K &key) const { ScopedLock lock(mLock); typename Map::const_iterator iter = mMap.find(key); while (iter==mMap.end()) { mWriteSignal.wait(mLock); iter = mMap.find(key); } D* retVal = iter->second; return retVal; } }; /** This class is used to provide pointer-based comparison in priority_queues. */ template class PointerCompare { public: /** Compare the objects pointed to, not the pointers themselves. */ bool operator()(const T *v1, const T *v2) { return (*v1)>(*v2); } }; /** Priority queue for interthread operations. Passes pointers to objects. */ template , class Cmp = PointerCompare > class InterthreadPriorityQueue { protected: std::priority_queue mQ; mutable Mutex mLock; mutable Signal mWriteSignal; public: /** Clear the FIFO. */ void clear() { ScopedLock lock(mLock); while (mQ.size()>0) { T* ptr = mQ.top(); mQ.pop(); delete ptr; } } ~InterthreadPriorityQueue() { clear(); } size_t size() const { ScopedLock lock(mLock); return mQ.size(); } /** Non-blocking read. */ T* readNoBlock() { ScopedLock lock(mLock); T* retVal = NULL; if (mQ.size()!=0) { retVal = mQ.top(); mQ.pop(); } return retVal; } /** Blocking read. */ T* read() { ScopedLock lock(mLock); T* retVal; while (mQ.size()==0) mWriteSignal.wait(mLock); retVal = mQ.top(); mQ.pop(); return retVal; } /** Non-blocking write. */ void write(T* val) { // (pat) 8-14: Taking out the threading problem fix temporarily for David to use in the field. ScopedLock lock(mLock); mQ.push(val); mWriteSignal.signal(); } }; class Semaphore { private: bool mFlag; Signal mSignal; mutable Mutex mLock; public: Semaphore() :mFlag(false) { } void post() { ScopedLock lock(mLock); mFlag=true; mSignal.signal(); } void get() { ScopedLock lock(mLock); while (!mFlag) mSignal.wait(mLock); mFlag=false; } bool semtry() { ScopedLock lock(mLock); bool retVal = mFlag; mFlag = false; return retVal; } }; //@} #endif // vim: ts=4 sw=4 osmo-trx-0.4.0/CommonLibs/LinkedLists.cpp0000644000175000017500000000377213272615542017326 0ustar rubenruben/* * Copyright 2008 Free Software Foundation, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include "LinkedLists.h" void PointerFIFO::push_front(void* val) // by pat { // Pat added this routine for completeness, but never used or tested. // The first person to use this routine should remove this assert. ListNode *node = allocate(); node->data(val); node->next(mHead); mHead = node; if (!mTail) mTail=node; mSize++; } void PointerFIFO::put(void* val) { ListNode *node = allocate(); node->data(val); node->next(NULL); if (mTail!=NULL) mTail->next(node); mTail=node; if (mHead==NULL) mHead=node; mSize++; } /** Take an item from the FIFO. */ void* PointerFIFO::get() { // empty list? if (mHead==NULL) return NULL; // normal case ListNode* next = mHead->next(); void* retVal = mHead->data(); release(mHead); mHead = next; if (next==NULL) mTail=NULL; mSize--; return retVal; } ListNode *PointerFIFO::allocate() { if (mFreeList==NULL) return new ListNode; ListNode* retVal = mFreeList; mFreeList = mFreeList->next(); return retVal; } void PointerFIFO::release(ListNode* wNode) { wNode->next(mFreeList); mFreeList = wNode; } osmo-trx-0.4.0/autogen.sh0000755000175000017500000013443113272615542014331 0ustar rubenruben#!/bin/sh # a u t o g e n . s h # # Copyright (c) 2005-2009 United States Government as represented by # the U.S. Army Research Laboratory. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ### # # Script for automatically preparing the sources for compilation by # performing the myrid of necessary steps. The script attempts to # detect proper version support, and outputs warnings about particular # systems that have autotool peculiarities. # # Basically, if everything is set up and installed correctly, the # script will validate that minimum versions of the GNU Build System # tools are installed, account for several common configuration # issues, and then simply run autoreconf for you. # # If autoreconf fails, which can happen for many valid configurations, # this script proceeds to run manual preparation steps effectively # providing a POSIX shell script (mostly complete) reimplementation of # autoreconf. # # The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER # environment variables and corresponding _OPTIONS variables (e.g. # AUTORECONF_OPTIONS) may be used to override the default automatic # detection behaviors. Similarly the _VERSION variables will override # the minimum required version numbers. # # Examples: # # To obtain help on usage: # ./autogen.sh --help # # To obtain verbose output: # ./autogen.sh --verbose # # To skip autoreconf and prepare manually: # AUTORECONF=false ./autogen.sh # # To verbosely try running with an older (unsupported) autoconf: # AUTOCONF_VERSION=2.50 ./autogen.sh --verbose # # Author: # Christopher Sean Morrison # # Patches: # Sebastian Pipping # ###################################################################### # set to minimum acceptible version of autoconf if [ "x$AUTOCONF_VERSION" = "x" ] ; then AUTOCONF_VERSION=2.52 fi # set to minimum acceptible version of automake if [ "x$AUTOMAKE_VERSION" = "x" ] ; then AUTOMAKE_VERSION=1.6.0 fi # set to minimum acceptible version of libtool if [ "x$LIBTOOL_VERSION" = "x" ] ; then LIBTOOL_VERSION=1.4.2 fi ################## # ident function # ################## ident ( ) { # extract copyright from header __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`" if [ "x$__copyright" = "x" ] ; then __copyright="`date +%Y`" fi # extract version from CVS Id string __id="$Id: autogen.sh 33925 2009-03-01 23:27:06Z brlcad $" __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`" if [ "x$__version" = "x" ] ; then __version="" fi echo "autogen.sh build preparation script by Christopher Sean Morrison" echo " + config.guess download patch by Sebastian Pipping (2008-12-03)" echo "revised 3-clause BSD-style license, copyright (c) $__copyright" echo "script version $__version, ISO/IEC 9945 POSIX shell script" } ################## # USAGE FUNCTION # ################## usage ( ) { echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]" echo " --help Help on $NAME_OF_AUTOGEN usage" echo " --verbose Verbose progress output" echo " --quiet Quiet suppressed progress output" echo " --download Download the latest config.guess from gnulib" echo " --version Only perform GNU Build System version checks" echo echo "Description: This script will validate that minimum versions of the" echo "GNU Build System tools are installed and then run autoreconf for you." echo "Should autoreconf fail, manual preparation steps will be run" echo "potentially accounting for several common preparation issues. The" echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER," echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS" echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the" echo "default automatic detection behavior." echo ident return 0 } ########################## # VERSION_ERROR FUNCTION # ########################## version_error ( ) { if [ "x$1" = "x" ] ; then echo "INTERNAL ERROR: version_error was not provided a version" exit 1 fi if [ "x$2" = "x" ] ; then echo "INTERNAL ERROR: version_error was not provided an application name" exit 1 fi $ECHO $ECHO "ERROR: To prepare the ${PROJECT} build system from scratch," $ECHO " at least version $1 of $2 must be installed." $ECHO $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will" $ECHO "run configure or make. Either the GNU Autotools will need to be installed" $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source" $ECHO "code on another system and then transferred to here. -- Cheers!" $ECHO } ########################## # VERSION_CHECK FUNCTION # ########################## version_check ( ) { if [ "x$1" = "x" ] ; then echo "INTERNAL ERROR: version_check was not provided a minimum version" exit 1 fi _min="$1" if [ "x$2" = "x" ] ; then echo "INTERNAL ERROR: version check was not provided a comparison version" exit 1 fi _cur="$2" # needed to handle versions like 1.10 and 1.4-p6 _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`" _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`" _min_major="`echo $_min | cut -d. -f1`" _min_minor="`echo $_min | cut -d. -f2`" _min_patch="`echo $_min | cut -d. -f3`" _cur_major="`echo $_cur | cut -d. -f1`" _cur_minor="`echo $_cur | cut -d. -f2`" _cur_patch="`echo $_cur | cut -d. -f3`" if [ "x$_min_major" = "x" ] ; then _min_major=0 fi if [ "x$_min_minor" = "x" ] ; then _min_minor=0 fi if [ "x$_min_patch" = "x" ] ; then _min_patch=0 fi if [ "x$_cur_minor" = "x" ] ; then _cur_major=0 fi if [ "x$_cur_minor" = "x" ] ; then _cur_minor=0 fi if [ "x$_cur_patch" = "x" ] ; then _cur_patch=0 fi $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}" if [ $_min_major -lt $_cur_major ] ; then return 0 elif [ $_min_major -eq $_cur_major ] ; then if [ $_min_minor -lt $_cur_minor ] ; then return 0 elif [ $_min_minor -eq $_cur_minor ] ; then if [ $_min_patch -lt $_cur_patch ] ; then return 0 elif [ $_min_patch -eq $_cur_patch ] ; then return 0 fi fi fi return 1 } ###################################### # LOCATE_CONFIGURE_TEMPLATE FUNCTION # ###################################### locate_configure_template ( ) { _pwd="`pwd`" if test -f "./configure.ac" ; then echo "./configure.ac" elif test -f "./configure.in" ; then echo "./configure.in" elif test -f "$_pwd/configure.ac" ; then echo "$_pwd/configure.ac" elif test -f "$_pwd/configure.in" ; then echo "$_pwd/configure.in" elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then echo "$PATH_TO_AUTOGEN/configure.ac" elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then echo "$PATH_TO_AUTOGEN/configure.in" fi } ################## # argument check # ################## ARGS="$*" PATH_TO_AUTOGEN="`dirname $0`" NAME_OF_AUTOGEN="`basename $0`" AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN" LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4" if [ "x$HELP" = "x" ] ; then HELP=no fi if [ "x$QUIET" = "x" ] ; then QUIET=no fi if [ "x$VERBOSE" = "x" ] ; then VERBOSE=no fi if [ "x$VERSION_ONLY" = "x" ] ; then VERSION_ONLY=no fi if [ "x$DOWNLOAD" = "x" ] ; then DOWNLOAD=no fi if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then AUTORECONF_OPTIONS="-i -f" fi if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then AUTOCONF_OPTIONS="-f" fi if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then AUTOMAKE_OPTIONS="-a -c -f" fi ALT_AUTOMAKE_OPTIONS="-a -c" if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then LIBTOOLIZE_OPTIONS="--automake -c -f" fi ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force" if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then ACLOCAL_OPTIONS="" fi if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then AUTOHEADER_OPTIONS="" fi if [ "x$CONFIG_GUESS_URL" = "x" ] ; then CONFIG_GUESS_URL="http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD" fi for arg in $ARGS ; do case "x$arg" in x--help) HELP=yes ;; x-[hH]) HELP=yes ;; x--quiet) QUIET=yes ;; x-[qQ]) QUIET=yes ;; x--verbose) VERBOSE=yes ;; x-[dD]) DOWNLOAD=yes ;; x--download) DOWNLOAD=yes ;; x-[vV]) VERBOSE=yes ;; x--version) VERSION_ONLY=yes ;; *) echo "Unknown option: $arg" echo usage exit 1 ;; esac done ##################### # environment check # ##################### # sanity check before recursions potentially begin if [ ! -f "$AUTOGEN_SH" ] ; then echo "INTERNAL ERROR: $AUTOGEN_SH does not exist" if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH" fi exit 1 fi # force locale setting to C so things like date output as expected LC_ALL=C # commands that this script expects for __cmd in echo head tail pwd ; do echo "test" | $__cmd > /dev/null 2>&1 if [ $? != 0 ] ; then echo "INTERNAL ERROR: '${__cmd}' command is required" exit 2 fi done echo "test" | grep "test" > /dev/null 2>&1 if test ! x$? = x0 ; then echo "INTERNAL ERROR: grep command is required" exit 1 fi echo "test" | sed "s/test/test/" > /dev/null 2>&1 if test ! x$? = x0 ; then echo "INTERNAL ERROR: sed command is required" exit 1 fi # determine the behavior of echo case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac # determine the behavior of head case "x`echo 'head' | head -n 1 2>&1`" in *xhead*) HEAD_N="n " ;; *) HEAD_N="" ;; esac # determine the behavior of tail case "x`echo 'tail' | tail -n 1 2>&1`" in *xtail*) TAIL_N="n " ;; *) TAIL_N="" ;; esac VERBOSE_ECHO=: ECHO=: if [ "x$QUIET" = "xyes" ] ; then if [ "x$VERBOSE" = "xyes" ] ; then echo "Verbose output quelled by quiet option. Further output disabled." fi else ECHO=echo if [ "x$VERBOSE" = "xyes" ] ; then echo "Verbose output enabled" VERBOSE_ECHO=echo fi fi # allow a recursive run to disable further recursions if [ "x$RUN_RECURSIVE" = "x" ] ; then RUN_RECURSIVE=yes fi ################################################ # check for help arg and bypass version checks # ################################################ if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then HELP=yes fi if [ "x$HELP" = "xyes" ] ; then usage $ECHO "---" $ECHO "Help was requested. No preparation or configuration will be performed." exit 0 fi ####################### # set up signal traps # ####################### untrap_abnormal ( ) { for sig in 1 2 13 15; do trap - $sig done } # do this cleanup whenever we exit. trap ' # start from the root if test -d "$START_PATH" ; then cd "$START_PATH" fi # restore/delete backup files if test "x$PFC_INIT" = "x1" ; then recursive_restore fi ' 0 # trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15) for sig in 1 2 13 15; do trap ' $ECHO "" $ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'" # start from the root if test -d "$START_PATH" ; then cd "$START_PATH" fi # clean up on abnormal exit $VERBOSE_ECHO "rm -rf autom4te.cache" rm -rf autom4te.cache if test -f "acinclude.m4.$$.backup" ; then $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4" chmod u+w acinclude.m4 cat acinclude.m4.$$.backup > acinclude.m4 $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup" rm -f acinclude.m4.$$.backup fi { (exit 1); exit 1; } ' $sig done ############################# # look for a configure file # ############################# if [ "x$CONFIGURE" = "x" ] ; then CONFIGURE="`locate_configure_template`" if [ ! "x$CONFIGURE" = "x" ] ; then $VERBOSE_ECHO "Found a configure template: $CONFIGURE" fi else $ECHO "Using CONFIGURE environment variable override: $CONFIGURE" fi if [ "x$CONFIGURE" = "x" ] ; then if [ "x$VERSION_ONLY" = "xyes" ] ; then CONFIGURE=/dev/null else $ECHO $ECHO "A configure.ac or configure.in file could not be located implying" $ECHO "that the GNU Build System is at least not used in this directory. In" $ECHO "any case, there is nothing to do here without one of those files." $ECHO $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`" exit 1 fi fi #################### # get project name # #################### if [ "x$PROJECT" = "x" ] ; then PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" if [ "x$PROJECT" = "xAC_INIT" ] ; then # projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ ]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" fi if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then PROJECT="project" fi if [ "x$PROJECT" = "x" ] ; then PROJECT="project" fi else $ECHO "Using PROJECT environment variable override: $PROJECT" fi $ECHO "Preparing the $PROJECT build system...please wait" $ECHO ######################## # check for autoreconf # ######################## HAVE_AUTORECONF=no if [ "x$AUTORECONF" = "x" ] ; then for AUTORECONF in autoreconf ; do $VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version" $AUTORECONF --version > /dev/null 2>&1 if [ $? = 0 ] ; then HAVE_AUTORECONF=yes break fi done else HAVE_AUTORECONF=yes $ECHO "Using AUTORECONF environment variable override: $AUTORECONF" fi ########################## # autoconf version check # ########################## _acfound=no if [ "x$AUTOCONF" = "x" ] ; then for AUTOCONF in autoconf ; do $VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version" $AUTOCONF --version > /dev/null 2>&1 if [ $? = 0 ] ; then _acfound=yes break fi done else _acfound=yes $ECHO "Using AUTOCONF environment variable override: $AUTOCONF" fi _report_error=no if [ ! "x$_acfound" = "xyes" ] ; then $ECHO "ERROR: Unable to locate GNU Autoconf." _report_error=yes else _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" if [ "x$_version" = "x" ] ; then _version="0.0.0" fi $ECHO "Found GNU Autoconf version $_version" version_check "$AUTOCONF_VERSION" "$_version" if [ $? -ne 0 ] ; then _report_error=yes fi fi if [ "x$_report_error" = "xyes" ] ; then version_error "$AUTOCONF_VERSION" "GNU Autoconf" exit 1 fi ########################## # automake version check # ########################## _amfound=no if [ "x$AUTOMAKE" = "x" ] ; then for AUTOMAKE in automake ; do $VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version" $AUTOMAKE --version > /dev/null 2>&1 if [ $? = 0 ] ; then _amfound=yes break fi done else _amfound=yes $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE" fi _report_error=no if [ ! "x$_amfound" = "xyes" ] ; then $ECHO $ECHO "ERROR: Unable to locate GNU Automake." _report_error=yes else _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" if [ "x$_version" = "x" ] ; then _version="0.0.0" fi $ECHO "Found GNU Automake version $_version" version_check "$AUTOMAKE_VERSION" "$_version" if [ $? -ne 0 ] ; then _report_error=yes fi fi if [ "x$_report_error" = "xyes" ] ; then version_error "$AUTOMAKE_VERSION" "GNU Automake" exit 1 fi ######################## # check for libtoolize # ######################## HAVE_LIBTOOLIZE=yes HAVE_ALT_LIBTOOLIZE=no _ltfound=no if [ "x$LIBTOOLIZE" = "x" ] ; then LIBTOOLIZE=libtoolize $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version" $LIBTOOLIZE --version > /dev/null 2>&1 if [ ! $? = 0 ] ; then HAVE_LIBTOOLIZE=no $ECHO if [ "x$HAVE_AUTORECONF" = "xno" ] ; then $ECHO "Warning: libtoolize does not appear to be available." else $ECHO "Warning: libtoolize does not appear to be available. This means that" $ECHO "the automatic build preparation via autoreconf will probably not work." $ECHO "Preparing the build by running each step individually, however, should" $ECHO "work and will be done automatically for you if autoreconf fails." fi # look for some alternates for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version" _glibtoolize="`$tool --version > /dev/null 2>&1`" if [ $? = 0 ] ; then $VERBOSE_ECHO "Found $tool --version" _glti="`which $tool`" if [ "x$_glti" = "x" ] ; then $VERBOSE_ECHO "Cannot find $tool with which" continue; fi if test ! -f "$_glti" ; then $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file" continue; fi _gltidir="`dirname $_glti`" if [ "x$_gltidir" = "x" ] ; then $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti" continue; fi if test ! -d "$_gltidir" ; then $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory" continue; fi HAVE_ALT_LIBTOOLIZE=yes LIBTOOLIZE="$tool" $ECHO $ECHO "Fortunately, $tool was found which means that your system may simply" $ECHO "have a non-standard or incomplete GNU Autotools install. If you have" $ECHO "sufficient system access, it may be possible to quell this warning by" $ECHO "running:" $ECHO sudo -V > /dev/null 2>&1 if [ $? = 0 ] ; then $ECHO " sudo ln -s $_glti $_gltidir/libtoolize" $ECHO else $ECHO " ln -s $_glti $_gltidir/libtoolize" $ECHO $ECHO "Run that as root or with proper permissions to the $_gltidir directory" $ECHO fi _ltfound=yes break fi done else _ltfound=yes fi else _ltfound=yes $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE" fi ############################ # libtoolize version check # ############################ _report_error=no if [ ! "x$_ltfound" = "xyes" ] ; then $ECHO $ECHO "ERROR: Unable to locate GNU Libtool." _report_error=yes else _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" if [ "x$_version" = "x" ] ; then _version="0.0.0" fi $ECHO "Found GNU Libtool version $_version" version_check "$LIBTOOL_VERSION" "$_version" if [ $? -ne 0 ] ; then _report_error=yes fi fi if [ "x$_report_error" = "xyes" ] ; then version_error "$LIBTOOL_VERSION" "GNU Libtool" exit 1 fi ##################### # check for aclocal # ##################### if [ "x$ACLOCAL" = "x" ] ; then for ACLOCAL in aclocal ; do $VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version" $ACLOCAL --version > /dev/null 2>&1 if [ $? = 0 ] ; then break fi done else $ECHO "Using ACLOCAL environment variable override: $ACLOCAL" fi ######################## # check for autoheader # ######################## if [ "x$AUTOHEADER" = "x" ] ; then for AUTOHEADER in autoheader ; do $VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version" $AUTOHEADER --version > /dev/null 2>&1 if [ $? = 0 ] ; then break fi done else $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER" fi ######################### # check if version only # ######################### $VERBOSE_ECHO "Checking whether to only output version information" if [ "x$VERSION_ONLY" = "xyes" ] ; then $ECHO ident $ECHO "---" $ECHO "Version requested. No preparation or configuration will be performed." exit 0 fi ################################# # PROTECT_FROM_CLOBBER FUNCTION # ################################# protect_from_clobber ( ) { PFC_INIT=1 # protect COPYING & INSTALL from overwrite by automake. the # automake force option will (inappropriately) ignore the existing # contents of a COPYING and/or INSTALL files (depending on the # version) instead of just forcing *missing* files like it does # for AUTHORS, NEWS, and README. this is broken but extremely # prevalent behavior, so we protect against it by keeping a backup # of the file that can later be restored. for file in COPYING INSTALL ; do if test -f ${file} ; then if test -f ${file}.$$.protect_from_automake.backup ; then $VERBOSE_ECHO "Already backed up ${file} in `pwd`" else $VERBOSE_ECHO "Backing up ${file} in `pwd`" $VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup" cp -p ${file} ${file}.$$.protect_from_automake.backup fi fi done } ############################## # RECURSIVE_PROTECT FUNCTION # ############################## recursive_protect ( ) { # for projects using recursive configure, run the build # preparation steps for the subdirectories. this function assumes # START_PATH was set to pwd before recursion begins so that # relative paths work. # git 'r done, protect COPYING and INSTALL from being clobbered protect_from_clobber if test -d autom4te.cache ; then $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it" $VERBOSE_ECHO "rm -rf autom4te.cache" rm -rf autom4te.cache fi # find configure template _configure="`locate_configure_template`" if [ "x$_configure" = "x" ] ; then return fi # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure" # look for subdirs # $VERBOSE_ECHO "Looking for subdirs in `pwd`" _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" CHECK_DIRS="" for dir in $_det_config_subdirs ; do if test -d "`pwd`/$dir" ; then CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\"" fi done # process subdirs if [ ! "x$CHECK_DIRS" = "x" ] ; then $VERBOSE_ECHO "Recursively scanning the following directories:" $VERBOSE_ECHO " $CHECK_DIRS" for dir in $CHECK_DIRS ; do $VERBOSE_ECHO "Protecting files from automake in $dir" cd "$START_PATH" eval "cd $dir" # recursively git 'r done recursive_protect done fi } # end of recursive_protect ############################# # RESTORE_CLOBBERED FUNCION # ############################# restore_clobbered ( ) { # The automake (and autoreconf by extension) -f/--force-missing # option may overwrite COPYING and INSTALL even if they do exist. # Here we restore the files if necessary. spacer=no for file in COPYING INSTALL ; do if test -f ${file}.$$.protect_from_automake.backup ; then if test -f ${file} ; then # compare entire content, restore if needed if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then if test "x$spacer" = "xno" ; then $VERBOSE_ECHO spacer=yes fi # restore the backup $VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)" $VERBOSE_ECHO "rm -f ${file}" rm -f ${file} $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}" mv ${file}.$$.protect_from_automake.backup ${file} fi # check contents elif test -f ${file}.$$.protect_from_automake.backup ; then $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}" mv ${file}.$$.protect_from_automake.backup ${file} fi # -f ${file} # just in case $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup" rm -f ${file}.$$.protect_from_automake.backup fi # -f ${file}.$$.protect_from_automake.backup done CONFIGURE="`locate_configure_template`" if [ "x$CONFIGURE" = "x" ] ; then return fi _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" if test ! -d "$_aux_dir" ; then _aux_dir=. fi for file in config.guess config.sub ltmain.sh ; do if test -f "${_aux_dir}/${file}" ; then $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\"" rm -f "${_aux_dir}/${file}.backup" fi done } # end of restore_clobbered ############################## # RECURSIVE_RESTORE FUNCTION # ############################## recursive_restore ( ) { # restore COPYING and INSTALL from backup if they were clobbered # for each directory recursively. # git 'r undone restore_clobbered # find configure template _configure="`locate_configure_template`" if [ "x$_configure" = "x" ] ; then return fi # look for subdirs _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" CHECK_DIRS="" for dir in $_det_config_subdirs ; do if test -d "`pwd`/$dir" ; then CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\"" fi done # process subdirs if [ ! "x$CHECK_DIRS" = "x" ] ; then $VERBOSE_ECHO "Recursively scanning the following directories:" $VERBOSE_ECHO " $CHECK_DIRS" for dir in $CHECK_DIRS ; do $VERBOSE_ECHO "Checking files for automake damage in $dir" cd "$START_PATH" eval "cd $dir" # recursively git 'r undone recursive_restore done fi } # end of recursive_restore ####################### # INITIALIZE FUNCTION # ####################### initialize ( ) { # this routine performs a variety of directory-specific # initializations. some are sanity checks, some are preventive, # and some are necessary setup detection. # # this function sets: # CONFIGURE # SEARCH_DIRS # CONFIG_SUBDIRS ################################## # check for a configure template # ################################## CONFIGURE="`locate_configure_template`" if [ "x$CONFIGURE" = "x" ] ; then $ECHO $ECHO "A configure.ac or configure.in file could not be located implying" $ECHO "that the GNU Build System is at least not used in this directory. In" $ECHO "any case, there is nothing to do here without one of those files." $ECHO $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`" exit 1 fi ##################### # detect an aux dir # ##################### _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" if test ! -d "$_aux_dir" ; then _aux_dir=. else $VERBOSE_ECHO "Detected auxillary directory: $_aux_dir" fi ################################ # detect a recursive configure # ################################ CONFIG_SUBDIRS="" _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" for dir in $_det_config_subdirs ; do if test -d "`pwd`/$dir" ; then $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir" CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir" fi done ########################################################### # make sure certain required files exist for GNU projects # ########################################################### _marker_found="" _marker_found_message_intro='Detected non-GNU marker "' _marker_found_message_mid='" in ' for marker in foreign cygnus ; do _marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid} _marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`" if [ ! "x$_marker_found" = "x" ] ; then $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`" break fi if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`" if [ ! "x$_marker_found" = "x" ] ; then $VERBOSE_ECHO "${_marker_found_message}Makefile.am" break fi fi done if [ "x${_marker_found}" = "x" ] ; then _suggest_foreign=no for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do if [ ! -f $file ] ; then $VERBOSE_ECHO "Touching ${file} since it does not exist" _suggest_foreign=yes touch $file fi done if [ "x${_suggest_foreign}" = "xyes" ] ; then $ECHO $ECHO "Warning: Several files expected of projects that conform to the GNU" $ECHO "coding standards were not found. The files were automatically added" $ECHO "for you since you do not have a 'foreign' declaration specified." $ECHO $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`" if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then $ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file." fi $ECHO fi fi ################################################## # make sure certain generated files do not exist # ################################################## for file in config.guess config.sub ltmain.sh ; do if test -f "${_aux_dir}/${file}" ; then $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\"" mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup" fi done ############################ # search alternate m4 dirs # ############################ SEARCH_DIRS="" for dir in m4 ; do if [ -d $dir ] ; then $VERBOSE_ECHO "Found extra aclocal search directory: $dir" SEARCH_DIRS="$SEARCH_DIRS -I $dir" fi done ###################################### # remove any previous build products # ###################################### if test -d autom4te.cache ; then $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it" $VERBOSE_ECHO "rm -rf autom4te.cache" rm -rf autom4te.cache fi # tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it # if test -f aclocal.m4 ; then # $VERBOSE_ECHO "Found an aclocal.m4 file, deleting it" # $VERBOSE_ECHO "rm -f aclocal.m4" # rm -f aclocal.m4 # fi } # end of initialize() ############## # initialize # ############## # stash path START_PATH="`pwd`" # Before running autoreconf or manual steps, some prep detection work # is necessary or useful. Only needs to occur once per directory, but # does need to traverse the entire subconfigure hierarchy to protect # files from being clobbered even by autoreconf. recursive_protect # start from where we started cd "$START_PATH" # get ready to process initialize ######################################### # DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION # ######################################### # TODO - should make sure wget/curl exist and/or work before trying to # use them. download_gnulib_config_guess () { # abuse gitweb to download gnulib's latest config.guess via HTTP config_guess_temp="config.guess.$$.download" ret=1 for __cmd in wget curl fetch ; do $VERBOSE_ECHO "Checking for command ${__cmd}" ${__cmd} --version > /dev/null 2>&1 ret=$? if [ ! $ret = 0 ] ; then continue fi __cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'` $VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}" opts="" case ${__cmd} in wget) opts="-O" ;; curl) opts="-o" ;; fetch) opts="-t 5 -f" ;; esac $VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1 if [ $? = 0 ] ; then mv -f "${config_guess_temp}" ${_aux_dir}/config.guess ret=0 break fi done if [ ! $ret = 0 ] ; then $ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL" rm -f "${config_guess_temp}" fi } ############################## # LIBTOOLIZE_NEEDED FUNCTION # ############################## libtoolize_needed () { ret=1 # means no, don't need libtoolize for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do $VERBOSE_ECHO "Searching for $feature in $CONFIGURE" found="`grep \"^$feature.*\" $CONFIGURE`" if [ ! "x$found" = "x" ] ; then ret=0 # means yes, need to run libtoolize break fi done return ${ret} } ############################################ # prepare build via autoreconf or manually # ############################################ reconfigure_manually=no if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then $ECHO $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C" $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS" autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$autoreconf_output" if [ ! $ret = 0 ] ; then if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then $ECHO $ECHO "Warning: autoreconf failed but due to what is usually a common libtool" $ECHO "misconfiguration issue. This problem is encountered on systems that" $ECHO "have installed libtoolize under a different name without providing a" $ECHO "symbolic link or without setting the LIBTOOLIZE environment variable." $ECHO $ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE" export LIBTOOLIZE RUN_RECURSIVE=no export RUN_RECURSIVE untrap_abnormal $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" exit $? fi fi $ECHO "Warning: $AUTORECONF failed" if test -f ltmain.sh ; then $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should" fi $ECHO "Attempting to run the preparation steps individually" reconfigure_manually=yes else if [ "x$DOWNLOAD" = "xyes" ] ; then if libtoolize_needed ; then download_gnulib_config_guess fi fi fi else reconfigure_manually=yes fi ############################ # LIBTOOL_FAILURE FUNCTION # ############################ libtool_failure ( ) { # libtool is rather error-prone in comparison to the other # autotools and this routine attempts to compensate for some # common failures. the output after a libtoolize failure is # parsed for an error related to AC_PROG_LIBTOOL and if found, we # attempt to inject a project-provided libtool.m4 file. _autoconf_output="$1" if [ "x$RUN_RECURSIVE" = "xno" ] ; then # we already tried the libtool.m4, don't try again return 1 fi if test -f "$LIBTOOL_M4" ; then found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`" if test ! "x$found_libtool" = "x" ; then if test -f acinclude.m4 ; then rm -f acinclude.m4.$$.backup $VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup" cat acinclude.m4 > acinclude.m4.$$.backup fi $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4" chmod u+w acinclude.m4 cat "$LIBTOOL_M4" >> acinclude.m4 # don't keep doing this RUN_RECURSIVE=no export RUN_RECURSIVE untrap_abnormal $ECHO $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4" $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" exit $? fi fi } ########################### # MANUAL_AUTOGEN FUNCTION # ########################### manual_autogen ( ) { ################################################## # Manual preparation steps taken are as follows: # # aclocal [-I m4] # # libtoolize --automake -c -f # # aclocal [-I m4] # # autoconf -f # # autoheader # # automake -a -c -f # ################################################## ########### # aclocal # ########### $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS" aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$aclocal_output" if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi ############## # libtoolize # ############## if libtoolize_needed ; then if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS" libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$libtoolize_output" if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi else if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then $VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS" libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$libtoolize_output" if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi fi fi ########### # aclocal # ########### # re-run again as instructed by libtoolize $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS" aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$aclocal_output" # libtoolize might put ltmain.sh in the wrong place if test -f ltmain.sh ; then if test ! -f "${_aux_dir}/ltmain.sh" ; then $ECHO $ECHO "Warning: $LIBTOOLIZE is creating ltmain.sh in the wrong directory" $ECHO $ECHO "Fortunately, the problem can be worked around by simply copying the" $ECHO "file to the appropriate location (${_aux_dir}/). This has been done for you." $ECHO $VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\"" cp -p ltmain.sh "${_aux_dir}/ltmain.sh" $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C" fi fi # ltmain.sh if [ "x$DOWNLOAD" = "xyes" ] ; then download_gnulib_config_guess fi fi # libtoolize_needed ############ # autoconf # ############ $VERBOSE_ECHO $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS" autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$autoconf_output" if [ ! $ret = 0 ] ; then # retry without the -f and check for usage of macros that are too new ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE" ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE" ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T" macros_to_search="" ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`" ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`" if [ $ac_major -lt 2 ] ; then macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros" else if [ $ac_minor -lt 54 ] ; then macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros" elif [ $ac_minor -lt 55 ] ; then macros_to_search="$ac2_59_macros $ac2_55_macros" elif [ $ac_minor -lt 59 ] ; then macros_to_search="$ac2_59_macros" fi fi configure_ac_macros=__none__ for feature in $macros_to_search ; do $VERBOSE_ECHO "Searching for $feature in $CONFIGURE" found="`grep \"^$feature.*\" $CONFIGURE`" if [ ! "x$found" = "x" ] ; then if [ "x$configure_ac_macros" = "x__none__" ] ; then configure_ac_macros="$feature" else configure_ac_macros="$feature $configure_ac_macros" fi fi done if [ ! "x$configure_ac_macros" = "x__none__" ] ; then $ECHO $ECHO "Warning: Unsupported macros were found in $CONFIGURE" $ECHO $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any" $ECHO "unsupported macros are used that exceed the minimum version" $ECHO "settings specified within this file. As such, the following macros" $ECHO "should be removed from configure.ac or the version numbers in this" $ECHO "file should be increased:" $ECHO $ECHO "$configure_ac_macros" $ECHO $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C" fi ################### # autoconf, retry # ################### $VERBOSE_ECHO $VERBOSE_ECHO "$AUTOCONF" autoconf_output="`$AUTOCONF 2>&1`" ret=$? $VERBOSE_ECHO "$autoconf_output" if [ ! $ret = 0 ] ; then # test if libtool is busted libtool_failure "$autoconf_output" # let the user know what went wrong cat <