diff --git a/CMakeLists.txt b/CMakeLists.txt index e7a2b2836..b9bf61779 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,3 +292,9 @@ include_directories(${PROJECT_SOURCE_DIR}/lib/include/srslte) # Add the subdirectories ######################################################################## add_subdirectory(lib) +if(NOT DISABLE_SRSUE) + message(STATUS "Building with srsUE") + add_subdirectory(srsue) +else(NOT DISABLE_SRSUE) + message(STATUS "Building without srsUE") +endif(NOT DISABLE_SRSUE) diff --git a/srsue/CHANGELOG b/srsue/CHANGELOG new file mode 100644 index 000000000..e1edfcedb --- /dev/null +++ b/srsue/CHANGELOG @@ -0,0 +1,57 @@ +Change Log for Releases +============================== + +## 001.004.000 + * Fixed issues in RLC AM + * Added warning messages for unhandled ASN extensions + * Moved some classes from srsue to srslte namescape + * Improved compatibility with more eNodeBs + +* Known Bugs: + * Found bug in PBR UL schedulign. Disabled PBR. + * Possible problem with UL TA compensation. Disabled TA from MAC commands (from RAR still enabled) + +## 001.003.000 + +* Bugfixes: + * Moved UL signal pregeneration to after attach (causing timeout after ConnectionSetup) + * Fixed issue with incorrect TransactionID + * Fixed bug in PDN attach procedure + * Fixed bugs in RLC AM mode + * Fixed bug in BSR procedure + +* Improvements: + * Added Aperiodic CQI support 3-1 + * Added EIA1 support + * Added RLC AM resegmentation support + + +* Known bugs: + * Tun/tap write failure, seen at high rates with many retx (>70% retx) + * RRC Reestablishment procedure. Incorrect short-MAC computation + * BER performance at 20 MHz for MCS=28. More errors than expected. + +## 001.002.000 + +* Bugfixes: + * UL BLER stdout metric bad formatting + * GW blocking when RLC queue was full + * Memory bug in RLC AM uplink + +* Improvements: + * Management of radio link failure according to the specifications + +* Known bugs: + * Tun/tap write failure, seen at high rates with many retx (>70% retx) + * RRC Reestablishment procedure. Incorrect short-MAC computation + * BER performance at 20 MHz for MCS=28. More errors than expected. + + +## 001.001.000 + +* Added support for BladeRF +* Added paging support, RRC reconnection +* Improved overall stability +* Calibrated RF front-end time advances +* Bugfix: PDCP SN size config bug for AM +* Bugfix: Added RRC Connection setup defaults diff --git a/srsue/CMakeLists.txt b/srsue/CMakeLists.txt new file mode 100644 index 000000000..c3a773ad9 --- /dev/null +++ b/srsue/CMakeLists.txt @@ -0,0 +1,70 @@ +# Copyright 2015 Software Radio Systems Limited +# +# This file is part of srsUE +# +# srsUE 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. +# +# srsUE 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. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + + +######################################################################## +# Find boost +######################################################################## +SET(BOOST_REQUIRED_COMPONENTS + program_options + system +) +if(UNIX AND EXISTS "/usr/lib64") + list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix +endif(UNIX AND EXISTS "/usr/lib64") +set(Boost_ADDITIONAL_VERSIONS + "1.35.0" "1.35" "1.36.0" "1.36" "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39" + "1.40.0" "1.40" "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" + "1.45.0" "1.45" "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49" + "1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53" "1.54.0" "1.54" + "1.55.0" "1.55" "1.56.0" "1.56" "1.57.0" "1.57" "1.58.0" "1.58" "1.59.0" "1.59" + "1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64" + "1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69" +) +find_package(Boost "1.35" COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) + +if(NOT Boost_FOUND) + message(FATAL_ERROR "Boost required to compile srsUE") +endif() + +######################################################################## +# Find dependencies +######################################################################## +find_package(Threads REQUIRED) + + +######################################################################## +# Setup the include and linker paths +######################################################################## +include_directories( + ${Boost_INCLUDE_DIRS} + ${POLAR_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/srsue/hdr +) + +link_directories( + ${Boost_LIBRARY_DIRS} + ${POLAR_LIBRARY_DIRS} +) + +######################################################################## +# Add subdirectories +######################################################################## +add_subdirectory(src) +add_subdirectory(test) diff --git a/srsue/COPYRIGHT b/srsue/COPYRIGHT new file mode 100644 index 000000000..86860b897 --- /dev/null +++ b/srsue/COPYRIGHT @@ -0,0 +1,217 @@ +Copyright (C) 2015-2016 Software Radio Systems Limited. All rights reserved. + +The following software is used within srsUE: + +----------------------------------------------------------- +OpenLTE (/liblte - used under AGPLv3) +----------------------------------------------------------- +Ben Wojtowicz, bwojtowi@gmail.com +Andrew Murphy, andrew.murphy@rd.bbc.co.uk (DCI 1C Unpack, SIB13 unpack/print) + + +----------------------------------------------------------- +PolarSSL (used under Apache 2.0 License below) +----------------------------------------------------------- + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/srsue/LICENSE b/srsue/LICENSE new file mode 100644 index 000000000..a871fcfd0 --- /dev/null +++ b/srsue/LICENSE @@ -0,0 +1,662 @@ + 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 +. + diff --git a/srsue/README.md b/srsue/README.md new file mode 100644 index 000000000..c03e5cdbf --- /dev/null +++ b/srsue/README.md @@ -0,0 +1,99 @@ +srsUE +======== + +[![Coverity Scan Build Status](https://scan.coverity.com/projects/9987/badge.svg)](https://scan.coverity.com/projects/9987) + +srsUE is a software radio LTE UE developed by SRS (www.softwareradiosystems.com). It is written in C++ and builds upon the srsLTE library (https://github.com/srslte/srslte). Running on an Intel Core i7-4790, srsUE achieves up to 60Mbps DL with a 20Mhz bandwidth SISO configuration. +srsUE is released under the AGPLv3 license and uses software from the OpenLTE project (http://sourceforge.net/projects/openlte) for some security functions and for RRC/NAS message parsing. + + +Compatibility +------------- + +srsUE has been fully tested and validated with the following network equipment: + * Amarisoft LTE100 eNodeB and EPC + * Nokia FlexiRadio family FSMF system module with 1800MHz FHED radio module and TravelHawk EPC simulator + * Huawei DBS3900 + * Octasic Flexicell LTE-FDD NIB + + +Features +-------- + +### PHY Layer + + * LTE Release 8 compliant + * FDD configuration + * Tested bandwidths: 1.4, 3, 5, 10, 15 and 20 MHz + * Transmission mode 1 (single antenna) and 2 (transmit diversity) + * Cell search and synchronization procedure for the UE + * Frequency-based ZF and MMSE equalizer + * Highly optimized turbo decoder available in Intel SSE4.1/AVX (+100 Mbps) and standard C (+25 Mbps) + +### Upper Layers + + * LTE Release 8 compliant + * MAC, RLC, PDCP, RRC, NAS and GW layers + * Soft USIM supporting Milenage and XOR authentication + +### User Interfaces + + * Detailed log system with per-layer log levels and hex dumps + * MAC layer wireshark packet capture + * Command-line trace metrics + * Detailed input configuration file + +### Network Interfaces + + * Virtual network interface *tun_srsue* created upon network attach + +Hardware +-------- + +The library currently supports the Ettus Universal Hardware Driver (UHD) and the bladeRF driver. Thus, any hardware supported by UHD or bladeRF can be used. There is no sampling rate conversion, therefore the hardware should support 30.72 MHz clock in order to work correctly with LTE sampling frequencies and decode signals from live LTE base stations. + +We have tested the following hardware: + * USRP B210 + * USRP X300 + * bladeRF + +Download & Install Instructions +------------------------------- + +* Mandatory dependencies: + * srsLTE: https://github.com/srslte/srslte + * Boost: http://www.boost.org + * PolarSSL/mbed TLS https://tls.mbed.org + +* RF front-end driver: + * UHD: https://github.com/EttusResearch/uhd + * BladeRF: https://github.com/Nuand/bladeRF + +Download and build srsUE: +``` +git clone https://github.com/srsLTE/srsUE.git +cd srsUE +mkdir build +cd build +cmake ../ +make +``` + +The ue application can be found in build/ue/src + +Running srsUE +------------- + + * Copy and rename the provided configuration file ue.conf.example + * Check and set configuration parameters + * ```sudo ./ue ue.conf``` + +Disclaimer +---------- + +srsUE is provided with NO WARRANTY OF ANY KIND. Users of this software are expected to comply with all applicable local, national and international telecom and radio spectrum regulations. + +Support +------- + +Mailing list: http://www.softwareradiosystems.com/mailman/listinfo/srslte-users diff --git a/srsue/hdr/mac/demux.h b/srsue/hdr/mac/demux.h new file mode 100644 index 000000000..7dee98419 --- /dev/null +++ b/srsue/hdr/mac/demux.h @@ -0,0 +1,92 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef DEMUX_H +#define DEMUX_H + +#include "common/interfaces.h" +#include "common/phy_interface.h" +#include "common/mac_interface.h" +#include "common/pdu_queue.h" +#include "common/log.h" +#include "common/timers.h" +#include "common/pdu.h" + +/* Logical Channel Demultiplexing and MAC CE dissassemble */ + + +namespace srsue { + +class demux : public srslte::pdu_queue::process_callback +{ +public: + demux(); + void init(phy_interface_mac* phy_h_, rlc_interface_mac *rlc, srslte::log* log_h_, srslte::timers* timers_db_); + + bool process_pdus(); + uint8_t* request_buffer(uint32_t pid, uint32_t len); + void deallocate(uint8_t* payload_buffer_ptr); + + void push_pdu(uint32_t pid, uint8_t *buff, uint32_t nof_bytes); + void push_pdu_temp_crnti(uint8_t *buff, uint32_t nof_bytes); + + void set_uecrid_callback(bool (*callback)(void*, uint64_t), void *arg); + bool get_uecrid_successful(); + + void process_pdu(uint8_t *pdu, uint32_t nof_bytes); + +private: + const static int NOF_HARQ_PID = 8; + const static int MAX_PDU_LEN = 150*1024/8; // ~ 150 Mbps + const static int NOF_BUFFER_PDUS = 64; // Number of PDU buffers per HARQ pid + uint8_t bcch_buffer[1024]; // BCCH PID has a dedicated buffer + + bool (*uecrid_callback) (void*, uint64_t); + void *uecrid_callback_arg; + + srslte::sch_pdu mac_msg; + srslte::sch_pdu pending_mac_msg; + + void process_sch_pdu(srslte::sch_pdu *pdu); + bool process_ce(srslte::sch_subh *subheader); + + bool is_uecrid_successful; + + phy_interface_mac *phy_h; + srslte::log *log_h; + srslte::timers *timers_db; + rlc_interface_mac *rlc; + + // Buffer of PDUs + srslte::pdu_queue pdus; +}; + +} // namespace srsue + +#endif // DEMUX_H + + + diff --git a/srsue/hdr/mac/dl_harq.h b/srsue/hdr/mac/dl_harq.h new file mode 100644 index 000000000..261cd1179 --- /dev/null +++ b/srsue/hdr/mac/dl_harq.h @@ -0,0 +1,120 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef DL_HARQ_H +#define DL_HARQ_H + +#include "common/log.h" +#include "common/timers.h" +#include "mac/demux.h" +#include "mac/dl_sps.h" +#include "common/mac_pcap.h" + +#include "common/mac_interface.h" + +/* Downlink HARQ entity as defined in 5.3.2 of 36.321 */ + + +namespace srsue { + +class dl_harq_entity +{ +public: + + const static uint32_t NOF_HARQ_PROC = 8; + const static uint32_t HARQ_BCCH_PID = NOF_HARQ_PROC; + + dl_harq_entity(); + bool init(srslte::log *log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg, srslte::timers *timers_, demux *demux_unit); + + + /***************** PHY->MAC interface for DL processes **************************/ + void new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t *action); + void tb_decoded(bool ack, srslte_rnti_type_t rnti_type, uint32_t harq_pid); + + + void reset(); + void start_pcap(srslte::mac_pcap* pcap); + int get_current_tbs(uint32_t harq_pid); + + void set_si_window_start(int si_window_start); + + float get_average_retx(); + +private: + + + class dl_harq_process { + public: + dl_harq_process(); + bool init(uint32_t pid, dl_harq_entity *parent); + void reset(); + bool is_sps(); + void new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t *action); + void tb_decoded(bool ack); + int get_current_tbs(); + + private: + bool calc_is_new_transmission(mac_interface_phy::mac_grant_t grant); + + bool is_initiated; + dl_harq_entity *harq_entity; + srslte::log *log_h; + + bool is_new_transmission; + + uint32_t pid; + uint8_t *payload_buffer_ptr; + bool ack; + + uint32_t n_retx; + + mac_interface_phy::mac_grant_t cur_grant; + srslte_softbuffer_rx_t softbuffer; + + }; + static bool generate_ack_callback(void *arg); + + uint32_t get_harq_sps_pid(uint32_t tti); + + dl_sps dl_sps_assig; + + dl_harq_process proc[NOF_HARQ_PROC+1]; + srslte::timers *timers_db; + mac_interface_rrc::mac_cfg_t *mac_cfg; + demux *demux_unit; + srslte::log *log_h; + srslte::mac_pcap *pcap; + uint16_t last_temporal_crnti; + int si_window_start; + + float average_retx; + uint64_t nof_pkts; +}; + +} // namespace srsue + +#endif // DL_HARQ_H diff --git a/srsue/hdr/mac/dl_sps.h b/srsue/hdr/mac/dl_sps.h new file mode 100644 index 000000000..87e21097b --- /dev/null +++ b/srsue/hdr/mac/dl_sps.h @@ -0,0 +1,54 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef DL_SPS_H +#define DL_SPS_H + +#include "common/mac_interface.h" +#include "common/log.h" +#include "common/timers.h" + +/* Downlink Semi-Persistent schedulign (Section 5.10.1) */ + + +namespace srsue { + +class dl_sps +{ +public: + + void clear() {} + void reset() {} + bool get_pending_grant(uint32_t tti, mac_interface_phy::mac_grant_t *grant) { + return false; + } +private: + +}; + +} // namespace srsue + +#endif // DL_SPS_H diff --git a/srsue/hdr/mac/mac.h b/srsue/hdr/mac/mac.h new file mode 100644 index 000000000..48c9d36fd --- /dev/null +++ b/srsue/hdr/mac/mac.h @@ -0,0 +1,208 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef MAC_H +#define MAC_H + +#include "common/log.h" +#include "mac/dl_harq.h" +#include "mac/ul_harq.h" +#include "common/timers.h" +#include "mac/mac_metrics.h" +#include "mac/proc_ra.h" +#include "mac/proc_sr.h" +#include "mac/proc_bsr.h" +#include "mac/proc_phr.h" +#include "mac/mux.h" +#include "mac/demux.h" +#include "common/mac_pcap.h" +#include "common/phy_interface.h" +#include "common/mac_interface.h" +#include "common/tti_sync_cv.h" +#include "common/threads.h" + +namespace srsue { + +class mac + :public mac_interface_phy + ,public mac_interface_rrc + ,public srslte::mac_interface_timers + ,public thread + ,public srslte::timer_callback +{ +public: + mac(); + bool init(phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac* rrc, srslte::log *log_h); + void stop(); + + void get_metrics(mac_metrics_t &m); + + /******** Interface from PHY (PHY -> MAC) ****************/ + /* see mac_interface.h for comments */ + void new_grant_ul(mac_grant_t grant, tb_action_ul_t *action); + void new_grant_ul_ack(mac_grant_t grant, bool ack, tb_action_ul_t *action); + void harq_recv(uint32_t tti, bool ack, tb_action_ul_t *action); + void new_grant_dl(mac_grant_t grant, tb_action_dl_t *action); + void tb_decoded(bool ack, srslte_rnti_type_t rnti_type, uint32_t harq_pid); + void bch_decoded_ok(uint8_t *payload, uint32_t len); + void pch_decoded_ok(uint32_t len); + void tti_clock(uint32_t tti); + + + /******** Interface from RLC (RLC -> MAC) ****************/ + void bcch_start_rx(); + void bcch_stop_rx(); + void bcch_start_rx(int si_window_start, int si_window_length); + void pcch_start_rx(); + void pcch_stop_rx(); + void setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority, int PBR_x_tti, uint32_t BSD); + void reconfiguration(); + void reset(); + + /******** set/get MAC configuration ****************/ + void set_config(mac_cfg_t *mac_cfg); + void get_config(mac_cfg_t *mac_cfg); + void set_config_main(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *main_cfg); + void set_config_rach(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT *rach_cfg, uint32_t prach_config_index); + void set_config_sr(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT *sr_cfg); + void set_contention_id(uint64_t uecri); + + void get_rntis(ue_rnti_t *rntis); + + void timer_expired(uint32_t timer_id); + void start_pcap(srslte::mac_pcap* pcap); + + srslte::timers::timer* get(uint32_t timer_id); + u_int32_t get_unique_id(); + + uint32_t get_current_tti(); + + enum { + HARQ_RTT, + TIME_ALIGNMENT, + CONTENTION_TIMER, + BSR_TIMER_PERIODIC, + BSR_TIMER_RETX, + PHR_TIMER_PERIODIC, + PHR_TIMER_PROHIBIT, + NOF_MAC_TIMERS + } mac_timers_t; + + static const int MAC_NOF_UPPER_TIMERS = 20; + +private: + void run_thread(); + + static const int MAC_MAIN_THREAD_PRIO = 5; + static const int MAC_PDU_THREAD_PRIO = 6; + + // Interaction with PHY + srslte::tti_sync_cv ttisync; + phy_interface_mac *phy_h; + rlc_interface_mac *rlc_h; + rrc_interface_mac *rrc_h; + srslte::log *log_h; + + // MAC configuration + mac_cfg_t config; + + // UE-specific RNTIs + ue_rnti_t uernti; + + uint32_t tti; + bool started; + bool is_synchronized; + uint16_t last_temporal_crnti; + uint16_t phy_rnti; + + /* Multiplexing/Demultiplexing Units */ + mux mux_unit; + demux demux_unit; + + /* DL/UL HARQ */ + dl_harq_entity dl_harq; + ul_harq_entity ul_harq; + + /* MAC Uplink-related Procedures */ + ra_proc ra_procedure; + sr_proc sr_procedure; + bsr_proc bsr_procedure; + phr_proc phr_procedure; + + /* Buffers for PCH reception (not included in DL HARQ) */ + const static uint32_t pch_payload_buffer_sz = 8*1024; + srslte_softbuffer_rx_t pch_softbuffer; + uint8_t pch_payload_buffer[pch_payload_buffer_sz]; + + /* Functions for MAC Timers */ + srslte::timers timers_db; + void setup_timers(); + void timeAlignmentTimerExpire(); + + // pointer to MAC PCAP object + srslte::mac_pcap* pcap; + bool signals_pregenerated; + bool is_first_ul_grant; + + + mac_metrics_t metrics; + + + /* Class to run upper-layer timers with normal priority */ + class upper_timers : public periodic_thread { + public: + upper_timers(); + void reset(); + srslte::timers::timer* get(uint32_t timer_id); + uint32_t get_unique_id(); + private: + void run_period(); + srslte::timers timers_db; + }; + upper_timers upper_timers_thread; + + + + /* Class to process MAC PDUs from DEMUX unit */ + class pdu_process : public thread { + public: + pdu_process(demux *demux_unit); + void notify(); + void stop(); + private: + void run_thread(); + bool running; + bool have_data; + pthread_mutex_t mutex; + pthread_cond_t cvar; + demux* demux_unit; + }; + pdu_process pdu_process_thread; +}; + +} // namespace srsue + +#endif // MAC_H diff --git a/srsue/hdr/mac/mac_metrics.h b/srsue/hdr/mac/mac_metrics.h new file mode 100644 index 000000000..91ce29cab --- /dev/null +++ b/srsue/hdr/mac/mac_metrics.h @@ -0,0 +1,46 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UE_MAC_METRICS_H +#define UE_MAC_METRICS_H + + +namespace srsue { + +struct mac_metrics_t +{ + int tx_pkts; + int tx_errors; + int tx_brate; + int rx_pkts; + int rx_errors; + int rx_brate; + int ul_buffer; +}; + +} // namespace srsue + +#endif // UE_MAC_METRICS_H diff --git a/srsue/hdr/mac/mux.h b/srsue/hdr/mac/mux.h new file mode 100644 index 000000000..8ec186c0b --- /dev/null +++ b/srsue/hdr/mac/mux.h @@ -0,0 +1,116 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef MUX_H +#define MUX_H + +#include + +#include + +#include "common/log.h" +#include "common/mac_interface.h" +#include "common/pdu.h" +#include "mac/proc_bsr.h" +#include "mac/proc_phr.h" + +/* Logical Channel Multiplexing and Prioritization + Msg3 Buffer */ + + +typedef struct { + int id; + int Bj; + int PBR; // -1 sets to infinity + uint32_t BSD; + uint32_t priority; + int sched_len; + int buffer_len; +} lchid_t; + +namespace srsue { + +class mux +{ +public: + mux(); + void reset(); + void init(rlc_interface_mac *rlc, srslte::log *log_h, bsr_proc *bsr_procedure, phr_proc *phr_procedure_); + + bool is_pending_any_sdu(); + bool is_pending_sdu(uint32_t lcid); + + uint8_t* pdu_get(uint8_t *payload, uint32_t pdu_sz, uint32_t tx_tti, uint32_t pid); + uint8_t* msg3_get(uint8_t* payload, uint32_t pdu_sz); + + void msg3_flush(); + bool msg3_is_transmitted(); + + void append_crnti_ce_next_tx(uint16_t crnti); + + void set_priority(uint32_t lcid, uint32_t priority, int PBR_x_tti, uint32_t BSD); + void clear_lch(uint32_t lch_id); + void pusch_retx(uint32_t tx_tti, uint32_t pid); + +private: + int find_lchid(uint32_t lch_id); + bool pdu_move_to_msg3(uint32_t pdu_sz); + bool allocate_sdu(uint32_t lcid, srslte::sch_pdu *pdu, int max_sdu_sz); + bool sched_sdu(lchid_t *ch, int *sdu_space, int max_sdu_sz); + + const static int MIN_RLC_SDU_LEN = 0; + const static int MAX_NOF_SUBHEADERS = 20; + const static int MAX_HARQ_PROC = 8; + + std::vector lch; + + // Keep track of the PIDs that transmitted BSR reports + bool pid_has_bsr[MAX_HARQ_PROC]; + + // Mutex for exclusive access + pthread_mutex_t mutex; + + srslte::log *log_h; + rlc_interface_mac *rlc; + bsr_proc *bsr_procedure; + phr_proc *phr_procedure; + uint16_t pending_crnti_ce; + + /* Msg3 Buffer */ + static const uint32_t MSG3_BUFF_SZ = 128; + uint8_t msg3_buff[MSG3_BUFF_SZ]; + + /* PDU Buffer */ + srslte::sch_pdu pdu_msg; + bool msg3_has_been_transmitted; + + + +}; + +} // namespace srsue + +#endif // MUX_H + diff --git a/srsue/hdr/mac/proc.h b/srsue/hdr/mac/proc.h new file mode 100644 index 000000000..3fa864cb0 --- /dev/null +++ b/srsue/hdr/mac/proc.h @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef PROC_H +#define PROC_H + +#include + +/* Interface for a MAC procedure */ + + +namespace srsue { + +class proc +{ +public: + proc() { + running = false; + } + void run() { + running = true; + } + void stop() { + running = false; + } + bool is_running() { + return running; + } + virtual void step(uint32_t tti) = 0; +private: + bool running; +}; + +} // namespace srsue + +#endif // PROC_H diff --git a/srsue/hdr/mac/proc_bsr.h b/srsue/hdr/mac/proc_bsr.h new file mode 100644 index 000000000..39c3e4ee9 --- /dev/null +++ b/srsue/hdr/mac/proc_bsr.h @@ -0,0 +1,101 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef PROCBSR_H +#define PROCBSR_H + +#include + +#include "common/log.h" +#include "common/mac_interface.h" +#include "common/interfaces.h" +#include "common/timers.h" + +/* Buffer status report procedure */ + +namespace srsue { + +class bsr_proc : public srslte::timer_callback +{ +public: + bsr_proc(); + void init(rlc_interface_mac *rlc, srslte::log *log_h, mac_interface_rrc::mac_cfg_t *mac_cfg, srslte::timers *timers_db); + void step(uint32_t tti); + void reset(); + void setup_lcg(uint32_t lcid, uint32_t new_lcg); + void set_priority(uint32_t lcid, uint32_t priority); + void timer_expired(uint32_t timer_id); + uint32_t get_buffer_state(); + + typedef enum { + LONG_BSR, + SHORT_BSR, + TRUNC_BSR + } bsr_format_t; + + typedef struct { + bsr_format_t format; + uint32_t buff_size[4]; + } bsr_t; + + bool need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr); + bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t *bsr); + bool need_to_send_sr(uint32_t tti); + bool need_to_reset_sr(); + void set_tx_tti(uint32_t tti); + +private: + + const static int QUEUE_STATUS_PERIOD_MS = 500; + + bool reset_sr; + mac_interface_rrc::mac_cfg_t *mac_cfg; + srslte::timers *timers_db; + srslte::log *log_h; + rlc_interface_mac *rlc; + bool initiated; + const static int MAX_LCID = 6; + int lcg[MAX_LCID]; + uint32_t last_pending_data[MAX_LCID]; + int priorities[MAX_LCID]; + uint32_t find_max_priority_lcid(); + typedef enum {NONE, REGULAR, PADDING, PERIODIC} triggered_bsr_type_t; + triggered_bsr_type_t triggered_bsr_type; + + bool sr_is_sent; + uint32_t last_print; + uint32_t next_tx_tti; + void update_pending_data(); + bool check_highest_channel(); + bool check_single_channel(); + bool generate_bsr(bsr_t *bsr, uint32_t nof_padding_bytes); + char* bsr_type_tostring(triggered_bsr_type_t type); + char* bsr_format_tostring(bsr_format_t format); +}; + +} // namespace srsue + +#endif // PROCBSR_H diff --git a/srsue/hdr/mac/proc_phr.h b/srsue/hdr/mac/proc_phr.h new file mode 100644 index 000000000..0d3de98fc --- /dev/null +++ b/srsue/hdr/mac/proc_phr.h @@ -0,0 +1,72 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef PROCPHR_H +#define PROCPHR_H + +#include +#include "common/timers.h" +#include "common/phy_interface.h" +#include "common/log.h" + +#include "common/mac_interface.h" + +/* Power headroom report procedure */ + + +namespace srsue { + +class phr_proc : public srslte::timer_callback +{ +public: + phr_proc(); + void init(phy_interface_mac* phy_h, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg, srslte::timers *timers_db_); + + void step(uint32_t tti); + void reset(); + + bool generate_phr_on_ul_grant(float *phr); + void timer_expired(uint32_t timer_id); + +private: + + bool pathloss_changed(); + + srslte::log* log_h; + mac_interface_rrc::mac_cfg_t *mac_cfg; + phy_interface_mac* phy_h; + srslte::timers* timers_db; + bool initiated; + int timer_prohibit; + int timer_periodic; + int dl_pathloss_change; + int last_pathloss_db; + bool phr_is_triggered; +}; + +} // namespace srsue + +#endif // PROCPHR_H diff --git a/srsue/hdr/mac/proc_ra.h b/srsue/hdr/mac/proc_ra.h new file mode 100644 index 000000000..f5fd49572 --- /dev/null +++ b/srsue/hdr/mac/proc_ra.h @@ -0,0 +1,201 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef PROCRA_H +#define PROCRA_H + +#include + +#include "common/log.h" +#include "common/timers.h" +#include "mac/mux.h" +#include "mac/demux.h" +#include "common/pdu.h" +#include "common/mac_pcap.h" + +/* Random access procedure as specified in Section 5.1 of 36.321 */ + + +namespace srsue { + +class ra_proc : public srslte::timer_callback +{ + public: + ra_proc() : rar_pdu_msg(20) { + bzero(&softbuffer_rar, sizeof(srslte_softbuffer_rx_t)); + pcap = NULL; + backoff_interval_start = 0; + backoff_inteval = 0; + received_target_power_dbm = 0; + ra_rnti = 0; + current_ta = 0; + state = IDLE; + last_msg3_group = RA_GROUP_A; + msg3_transmitted = false; + first_rar_received = false; + phy_h = NULL; + log_h = NULL; + mac_cfg = NULL; + timers_db = NULL; + mux_unit = NULL; + demux_unit = NULL; + rrc = NULL; + transmitted_contention_id = 0; + transmitted_crnti = 0; + pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; + started_by_pdcch = false; + rar_grant_nbytes = 0; + rar_grant_tti = 0; + msg3_flushed = false; + }; + void init(phy_interface_mac *phy_h, + rrc_interface_mac *rrc_, + srslte::log *log_h, + mac_interface_rrc::ue_rnti_t *rntis, + mac_interface_rrc::mac_cfg_t *mac_cfg, + srslte::timers *timers_db, + mux *mux_unit, + demux *demux_unit); + void reset(); + void start_pdcch_order(); + void start_mac_order(uint32_t msg_len_bits = 56); + void step(uint32_t tti); + bool is_successful(); + bool is_response_error(); + bool is_contention_resolution(); + void harq_retx(); + bool is_error(); + bool in_progress(); + void pdcch_to_crnti(bool contains_uplink_grant); + void timer_expired(uint32_t timer_id); + + void new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action); + void tb_decoded_ok(); + + void start_pcap(srslte::mac_pcap* pcap); +private: + static bool uecrid_callback(void *arg, uint64_t uecri); + + bool contention_resolution_id_received(uint64_t uecri); + void process_timeadv_cmd(uint32_t ta_cmd); + void step_initialization(); + void step_resource_selection(); + void step_preamble_transmission(); + void step_pdcch_setup(); + void step_response_reception(); + void step_response_error(); + void step_backoff_wait(); + void step_contention_resolution(); + void step_completition(); + + // Buffer to receive RAR PDU + static const uint32_t MAX_RAR_PDU_LEN = 2048; + uint8_t rar_pdu_buffer[MAX_RAR_PDU_LEN]; + srslte::rar_pdu rar_pdu_msg; + + // Random Access parameters provided by higher layers defined in 5.1.1 + uint32_t configIndex; + uint32_t nof_preambles; + uint32_t nof_groupA_preambles; + uint32_t nof_groupB_preambles; + uint32_t messagePowerOffsetGroupB; + uint32_t messageSizeGroupA; + uint32_t responseWindowSize; + uint32_t powerRampingStep; + uint32_t preambleTransMax; + uint32_t iniReceivedTargetPower; + int delta_preamble_db; + uint32_t contentionResolutionTimer; + uint32_t maskIndex; + int preambleIndex; + uint32_t new_ra_msg_len; + + // Internal variables + uint32_t preambleTransmissionCounter; + uint32_t backoff_param_ms; + uint32_t sel_maskIndex; + uint32_t sel_preamble; + uint32_t backoff_interval_start; + uint32_t backoff_inteval; + int received_target_power_dbm; + uint32_t ra_rnti; + uint32_t current_ta; + + srslte_softbuffer_rx_t softbuffer_rar; + + + enum { + IDLE = 0, + INITIALIZATION, // Section 5.1.1 + RESOURCE_SELECTION, // Section 5.1.2 + PREAMBLE_TRANSMISSION, // Section 5.1.3 + PDCCH_SETUP, + RESPONSE_RECEPTION, // Section 5.1.4 + RESPONSE_ERROR, + BACKOFF_WAIT, + CONTENTION_RESOLUTION, // Section 5.1.5 + COMPLETION, // Section 5.1.6 + COMPLETION_DONE, + RA_PROBLEM // Section 5.1.5 last part + } state; + + typedef enum {RA_GROUP_A, RA_GROUP_B} ra_group_t; + + ra_group_t last_msg3_group; + bool msg3_transmitted; + bool first_rar_received; + void read_params(); + + phy_interface_mac *phy_h; + srslte::log *log_h; + srslte::timers *timers_db; + mux *mux_unit; + demux *demux_unit; + srslte::mac_pcap *pcap; + rrc_interface_mac *rrc; + + mac_interface_rrc::ue_rnti_t *rntis; + mac_interface_rrc::mac_cfg_t *mac_cfg; + + uint64_t transmitted_contention_id; + uint16_t transmitted_crnti; + + enum { + PDCCH_CRNTI_NOT_RECEIVED = 0, + PDCCH_CRNTI_UL_GRANT, + PDCCH_CRNTI_DL_GRANT + } pdcch_to_crnti_received; + + bool started_by_pdcch; + uint32_t rar_grant_nbytes; + uint32_t rar_grant_tti; + bool msg3_flushed; + bool rar_received; +}; + +} // namespace srsue + +#endif // PROCRA_H diff --git a/srsue/hdr/mac/proc_sr.h b/srsue/hdr/mac/proc_sr.h new file mode 100644 index 000000000..1e5c3d57a --- /dev/null +++ b/srsue/hdr/mac/proc_sr.h @@ -0,0 +1,68 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef PROCSR_H +#define PROCSR_H + +#include +#include "common/phy_interface.h" +#include "common/interfaces.h" +#include "common/log.h" + +/* Scheduling Request procedure as defined in 5.4.4 of 36.321 */ + + +namespace srsue { + +class sr_proc +{ +public: + sr_proc(); + void init(phy_interface_mac *phy_h, rrc_interface_mac *rrc, srslte::log *log_h, mac_interface_rrc::mac_cfg_t *mac_cfg); + void step(uint32_t tti); + void reset(); + void start(); + bool need_random_access(); + +private: + bool need_tx(uint32_t tti); + + uint32_t sr_counter; + uint32_t dsr_transmax; + bool is_pending_sr; + mac_interface_rrc::mac_cfg_t *mac_cfg; + + rrc_interface_mac *rrc; + phy_interface_mac *phy_h; + srslte::log *log_h; + + bool initiated; + bool do_ra; +}; + +} // namespace srsue + +#endif // PROCSR_H diff --git a/srsue/hdr/mac/ul_harq.h b/srsue/hdr/mac/ul_harq.h new file mode 100644 index 000000000..1cf3a7482 --- /dev/null +++ b/srsue/hdr/mac/ul_harq.h @@ -0,0 +1,150 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef ULHARQ_H +#define ULHARQ_H + +#include "common/mac_interface.h" +#include "common/log.h" +#include "mac/mux.h" +#include "mac/ul_sps.h" +#include "common/mac_pcap.h" +#include "common/timers.h" + +/* Uplink HARQ entity as defined in 5.4.2 of 36.321 */ + + +namespace srsue { + +class ul_harq_entity +{ +public: + + const static uint32_t NOF_HARQ_PROC = 8; + static uint32_t pidof(uint32_t tti); + + ul_harq_entity() { + pcap = NULL; + timers_db = NULL; + mux_unit = NULL; + log_h = NULL; + mac_cfg = NULL; + rntis = NULL; + average_retx = 0; + nof_pkts = 0; + } + bool init(srslte::log *log_h, + mac_interface_rrc::ue_rnti_t *rntis, + mac_interface_rrc::mac_cfg_t *mac_cfg, + srslte::timers* timers_, + mux *mux_unit); + void reset(); + void reset_ndi(); + + void start_pcap(srslte::mac_pcap* pcap); + + + /***************** PHY->MAC interface for UL processes **************************/ + void new_grant_ul(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_ul_t *action); + void new_grant_ul_ack(mac_interface_phy::mac_grant_t grant, bool ack, mac_interface_phy::tb_action_ul_t *action); + void harq_recv(uint32_t tti, bool ack, mac_interface_phy::tb_action_ul_t *action); + + int get_current_tbs(uint32_t tti); + + float get_average_retx(); + +private: + + class ul_harq_process { + public: + ul_harq_process(); + bool init(uint32_t pid, ul_harq_entity *parent); + void reset(); + void reset_ndi(); + + void run_tti(uint32_t tti, mac_interface_phy::mac_grant_t *grant, mac_interface_phy::tb_action_ul_t* action); + + uint32_t get_rv(); + bool has_grant(); + + void set_harq_feedback(bool ack); + bool get_ndi(); + bool is_sps(); + uint32_t last_tx_tti(); + uint32_t get_nof_retx(); + int get_current_tbs(); + + private: + mac_interface_phy::mac_grant_t cur_grant; + + uint32_t pid; + uint32_t current_tx_nb; + uint32_t current_irv; + bool harq_feedback; + bool ndi; + srslte::log *log_h; + ul_harq_entity *harq_entity; + bool is_grant_configured; + srslte_softbuffer_tx_t softbuffer; + bool is_msg3; + bool is_initiated; + uint32_t tti_last_tx; + + + const static int payload_buffer_len = 128*1024; + uint8_t *payload_buffer; + uint8_t *pdu_ptr; + + void generate_retx(uint32_t tti_tx, mac_interface_phy::tb_action_ul_t *action); + void generate_retx(uint32_t tti_tx, mac_interface_phy::mac_grant_t *grant, + mac_interface_phy::tb_action_ul_t *action); + void generate_new_tx(uint32_t tti_tx, bool is_msg3, mac_interface_phy::mac_grant_t *grant, + mac_interface_phy::tb_action_ul_t *action); + void generate_tx(uint32_t tti_tx, mac_interface_phy::tb_action_ul_t *action); + }; + + + void run_tti(uint32_t tti, mac_interface_phy::mac_grant_t *grant, mac_interface_phy::tb_action_ul_t* action); + void set_ack(uint32_t tti, bool ack); + + ul_sps ul_sps_assig; + + srslte::timers *timers_db; + mux *mux_unit; + ul_harq_process proc[NOF_HARQ_PROC]; + srslte::log *log_h; + srslte::mac_pcap *pcap; + + mac_interface_rrc::ue_rnti_t *rntis; + mac_interface_rrc::mac_cfg_t *mac_cfg; + + float average_retx; + uint64_t nof_pkts; +}; + +} // namespace srsue + +#endif // ULHARQ_H diff --git a/srsue/hdr/mac/ul_sps.h b/srsue/hdr/mac/ul_sps.h new file mode 100644 index 000000000..5b3cde717 --- /dev/null +++ b/srsue/hdr/mac/ul_sps.h @@ -0,0 +1,53 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef ULSPS_H +#define ULSPS_H + +#include "common/log.h" +#include "common/timers.h" + +/* Uplink Semi-Persistent schedulign (Section 5.10.2) */ + + +namespace srsue { + +typedef _Complex float cf_t; + +class ul_sps +{ +public: + + void clear() {} + void reset(uint32_t tti) {} + bool get_pending_grant(uint32_t tti, mac_interface_phy::mac_grant_t *grant) { return false; } +private: + +}; + +} // namespace srsue + +#endif // ULSPS_H diff --git a/srsue/hdr/metrics_stdout.h b/srsue/hdr/metrics_stdout.h new file mode 100644 index 000000000..cd3efc1a3 --- /dev/null +++ b/srsue/hdr/metrics_stdout.h @@ -0,0 +1,73 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/****************************************************************************** + * File: metrics_stdout.h + * Description: Metrics class printing to stdout. + *****************************************************************************/ + +#ifndef METRICS_STDOUT_H +#define METRICS_STDOUT_H + +#include +#include +#include + +#include "ue_metrics_interface.h" + +namespace srsue { + +class metrics_stdout +{ +public: + metrics_stdout(); + + bool init(ue_metrics_interface *u, float report_period_secs=1.0); + void stop(); + void toggle_print(bool b); + static void* metrics_thread_start(void *m); + void metrics_thread_run(); + +private: + void print_metrics(); + void print_disconnect(); + std::string float_to_string(float f, int digits); + std::string float_to_eng_string(float f, int digits); + std::string int_to_eng_string(int f, int digits); + + ue_metrics_interface *ue_; + + bool started; + bool do_print; + pthread_t metrics_thread; + ue_metrics_t metrics; + float metrics_report_period; // seconds + uint8_t n_reports; +}; + +} // namespace srsue + +#endif // METRICS_STDOUT_H diff --git a/srsue/hdr/phy/nbiot_phy.h b/srsue/hdr/phy/nbiot_phy.h new file mode 100644 index 000000000..575c94431 --- /dev/null +++ b/srsue/hdr/phy/nbiot_phy.h @@ -0,0 +1,165 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UEPHY_H +#define UEPHY_H + +#include "srslte/srslte.h" +#include "common/phy_interface.h" +#include "common/log.h" +#include "phy/phy_metrics.h" +#include "phy/phch_recv.h" +#include "phy/prach.h" +#include "phy/phch_worker.h" +#include "phy/phch_common.h" +#include "radio/radio.h" +#include "common/task_dispatcher.h" +#include "common/trace.h" +#include "common/mac_interface.h" +#include "common/interfaces.h" + +namespace srsue { + +typedef _Complex float cf_t; + +class phy + : public phy_interface_mac + , public phy_interface_rrc +{ +public: + phy(); + bool init(srslte::radio *radio_handler, + mac_interface_phy *mac, + rrc_interface_phy *rrc, + srslte::log *log_h, + phy_args_t *args = NULL); + + void stop(); + + void set_agc_enable(bool enabled); + + void get_metrics(phy_metrics_t &m); + + void set_crnti(uint16_t rnti); + + + static uint32_t tti_to_SFN(uint32_t tti); + static uint32_t tti_to_subf(uint32_t tti); + + void enable_pregen_signals(bool enable); + + void start_trace(); + void write_trace(std::string filename); + + /********** RRC INTERFACE ********************/ + void reset(); + bool status_is_sync(); + void configure_ul_params(bool pregen_disabled = false); + void resync_sfn(); + + /********** MAC INTERFACE ********************/ + /* Functions to synchronize with a cell */ + void sync_start(); + void sync_stop(); + + /* Instructs the PHY to configure using the parameters written by set_param() */ + void configure_prach_params(); + + /* Transmits PRACH in the next opportunity */ + void prach_send(uint32_t preamble_idx, int allowed_subframe = -1, float target_power_dbm = 0.0); + int prach_tx_tti(); + + /* Indicates the transmission of a SR signal in the next opportunity */ + void sr_send(); + int sr_last_tx_tti(); + + // Time advance commands + void set_timeadv_rar(uint32_t ta_cmd); + void set_timeadv(uint32_t ta_cmd); + + /* Sets RAR grant payload */ + void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); + + /* Instruct the PHY to decode PDCCH with the CRC scrambled with given RNTI */ + void pdcch_ul_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1); + void pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1); + void pdcch_ul_search_reset(); + void pdcch_dl_search_reset(); + + /* Get/Set PHY parameters interface from RRC */ + void get_config(phy_cfg_t *phy_cfg); + void set_config(phy_cfg_t *phy_cfg); + void set_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated); + void set_config_common(phy_cfg_common_t *common); + void set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd); + void set_config_64qam_en(bool enable); + + + float get_phr(); + float get_pathloss_db(); + + uint32_t get_current_tti(); + void get_current_cell(srslte_cell_t *cell); + + void start_plot(); + void start_channel_emulator(const char *filename, int *path_taps, int nof_paths, int nof_coeffs, int nof_samples, int nof_tti); + +private: + + uint32_t nof_workers; + + const static int MAX_WORKERS = 4; + const static int DEFAULT_WORKERS = 2; + + const static int SF_RECV_THREAD_PRIO = 1; + const static int WORKERS_THREAD_PRIO = 0; + + srslte::radio *radio_handler; + srslte::log *log_h; + + srslte::thread_pool workers_pool; + std::vector workers; + phch_common workers_common; + phch_recv sf_recv; + prach prach_buffer; + + srslte_cell_t cell; + + phy_cfg_t config; + phy_args_t *args; + + /* Current time advance */ + uint32_t n_ta; + + bool init_(srslte::radio *radio_handler, mac_interface_phy *mac, srslte::log *log_h, bool do_agc, uint32_t nof_workers); + void set_default_args(phy_args_t *args); + bool check_args(phy_args_t *args); + +}; + +} // namespace srsue + +#endif // UEPHY_H diff --git a/srsue/hdr/phy/phch_common.h b/srsue/hdr/phy/phch_common.h new file mode 100644 index 000000000..0a712dd6d --- /dev/null +++ b/srsue/hdr/phy/phch_common.h @@ -0,0 +1,162 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UEPHYWORKERCOMMON_H +#define UEPHYWORKERCOMMON_H + +#include +#include +#include +#include "srslte/srslte.h" +#include "common/mac_interface.h" +#include "common/phy_interface.h" +#include "radio/radio.h" +#include "common/log.h" +#include "phy/phy_metrics.h" + +//#define CONTINUOUS_TX + + +namespace srsue { + +/* Subclass that manages variables common to all workers */ + class phch_common { + public: + + /* Common variables used by all phy workers */ + phy_interface_rrc::phy_cfg_t *config; + phy_args_t *args; + srslte::log *log_h; + mac_interface_phy *mac; + srslte_ue_ul_t ue_ul; + + /* Power control variables */ + float pathloss; + float cur_pathloss; + float p0_preamble; + float cur_radio_power; + float cur_pusch_power; + float avg_rsrp_db; + float avg_rsrq_db; + float rx_gain_offset; + float avg_snr_db; + float avg_noise; + float avg_rsrp; + + phch_common(uint32_t max_mutex = 3); + void init(phy_interface_rrc::phy_cfg_t *config, + phy_args_t *args, + srslte::log *_log, + srslte::radio *_radio, + mac_interface_phy *_mac); + + /* For RNTI searches, -1 means now or forever */ + void set_ul_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); + uint16_t get_ul_rnti(uint32_t tti); + srslte_rnti_type_t get_ul_rnti_type(); + + void set_dl_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); + uint16_t get_dl_rnti(uint32_t tti); + srslte_rnti_type_t get_dl_rnti_type(); + + void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); + bool get_pending_rar(uint32_t tti, srslte_dci_rar_grant_t *rar_grant = NULL); + + void reset_pending_ack(uint32_t tti); + void set_pending_ack(uint32_t tti, uint32_t I_lowest, uint32_t n_dmrs); + bool get_pending_ack(uint32_t tti); + bool get_pending_ack(uint32_t tti, uint32_t *I_lowest, uint32_t *n_dmrs); + + void worker_end(uint32_t tti, bool tx_enable, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + + void set_nof_mutex(uint32_t nof_mutex); + + bool sr_enabled; + int sr_last_tx_tti; + + srslte::radio* get_radio(); + + void set_cell(const srslte_cell_t &c); + uint32_t get_nof_prb(); + void set_dl_metrics(const dl_metrics_t &m); + void get_dl_metrics(dl_metrics_t &m); + void set_ul_metrics(const ul_metrics_t &m); + void get_ul_metrics(ul_metrics_t &m); + void set_sync_metrics(const sync_metrics_t &m); + void get_sync_metrics(sync_metrics_t &m); + + void reset_ul(); + + private: + + std::vector tx_mutex; + + bool is_first_of_burst; + srslte::radio *radio_h; + float cfo; + + + bool ul_rnti_active(uint32_t tti); + bool dl_rnti_active(uint32_t tti); + uint16_t ul_rnti, dl_rnti; + srslte_rnti_type_t ul_rnti_type, dl_rnti_type; + int ul_rnti_start, ul_rnti_end, dl_rnti_start, dl_rnti_end; + + float time_adv_sec; + + srslte_dci_rar_grant_t rar_grant; + bool rar_grant_pending; + uint32_t rar_grant_tti; + + typedef struct { + bool enabled; + uint32_t I_lowest; + uint32_t n_dmrs; + } pending_ack_t; + pending_ack_t pending_ack[10]; + + bool is_first_tx; + + uint32_t nof_workers; + uint32_t nof_mutex; + uint32_t max_mutex; + + srslte_cell_t cell; + + dl_metrics_t dl_metrics; + uint32_t dl_metrics_count; + bool dl_metrics_read; + ul_metrics_t ul_metrics; + uint32_t ul_metrics_count; + bool ul_metrics_read; + sync_metrics_t sync_metrics; + uint32_t sync_metrics_count; + bool sync_metrics_read; + }; + +} // namespace srsue + +#endif // UEPHYWORKERCOMMON_H diff --git a/srsue/hdr/phy/phch_recv.h b/srsue/hdr/phy/phch_recv.h new file mode 100644 index 000000000..ba3ef3c83 --- /dev/null +++ b/srsue/hdr/phy/phch_recv.h @@ -0,0 +1,122 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UEPHYRECV_H +#define UEPHYRECV_H + +#include "srslte/srslte.h" +#include "common/log.h" +#include "common/threads.h" +#include "common/thread_pool.h" +#include "radio/radio_multi.h" +#include "phy/prach.h" +#include "phy/phch_worker.h" +#include "phy/phch_common.h" +#include "common/interfaces.h" + +namespace srsue { + +typedef _Complex float cf_t; + +class phch_recv : public thread +{ +public: + phch_recv(); + void init(srslte::radio_multi* radio_handler, mac_interface_phy *mac,rrc_interface_phy *rrc, + prach *prach_buffer, srslte::thread_pool *_workers_pool, + phch_common *_worker_com, srslte::log* _log_h, uint32_t nof_rx_antennas, uint32_t prio, int sync_cpu_affinity = -1); + void stop(); + void set_agc_enable(bool enable); + + void resync_sfn(); + + uint32_t get_current_tti(); + + void sync_start(); + void sync_stop(); + bool status_is_sync(); + + void set_time_adv_sec(float time_adv_sec); + void get_current_cell(srslte_cell_t *cell); + + const static int MUTEX_X_WORKER = 4; + +private: + + void set_ue_sync_opts(srslte_ue_sync_t *q); + void run_thread(); + int sync_sfn(); + + bool running; + + srslte::radio_multi *radio_h; + mac_interface_phy *mac; + rrc_interface_phy *rrc; + srslte::log *log_h; + srslte::thread_pool *workers_pool; + phch_common *worker_com; + prach *prach_buffer; + + srslte_ue_sync_t ue_sync; + srslte_ue_mib_t ue_mib; + + uint32_t nof_rx_antennas; + + cf_t *sf_buffer_sfn[SRSLTE_MAX_PORTS]; + + // Sync metrics + sync_metrics_t metrics; + + enum { + IDLE, CELL_SEARCH, SYNCING, SYNC_DONE + } phy_state; + + srslte_cell_t cell; + bool cell_is_set; + bool is_sfn_synched; + bool started; + float time_adv_sec; + bool radio_is_streaming; + uint32_t tti; + bool do_agc; + + float last_gain; + float cellsearch_cfo; + uint32_t nof_tx_mutex; + uint32_t tx_mutex_cnt; + + uint32_t sync_sfn_cnt; + const static uint32_t SYNC_SFN_TIMEOUT = 5000; + float ul_dl_factor; + + bool cell_search(int force_N_id_2 = -1); + bool init_cell(); + void free_cell(); +}; + +} // namespace srsue + +#endif // UEPHYRECV_H diff --git a/srsue/hdr/phy/phch_worker.h b/srsue/hdr/phy/phch_worker.h new file mode 100644 index 000000000..4979bac26 --- /dev/null +++ b/srsue/hdr/phy/phch_worker.h @@ -0,0 +1,155 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UEPHYWORKER_H +#define UEPHYWORKER_H + +#include +#include "srslte/srslte.h" +#include "common/thread_pool.h" +#include "common/phy_interface.h" +#include "common/trace.h" +#include "phy/phch_common.h" + +#define LOG_EXECTIME + +namespace srsue { + +class phch_worker : public srslte::thread_pool::worker +{ +public: + + phch_worker(); + void reset(); + void set_common(phch_common *phy); + bool init_cell(srslte_cell_t cell); + void free_cell(); + + /* Functions used by main PHY thread */ + cf_t* get_buffer(uint32_t antenna_idx); + void set_tti(uint32_t tti, uint32_t tx_tti); + void set_tx_time(srslte_timestamp_t tx_time); + void set_cfo(float cfo); + void set_sample_offset(float sample_offset); + + void set_ul_params(bool pregen_disabled = false); + void set_crnti(uint16_t rnti); + void enable_pregen_signals(bool enabled); + + void start_trace(); + void write_trace(std::string filename); + + int read_ce_abs(float *ce_abs); + int read_pdsch_d(cf_t *pdsch_d); + void start_plot(); + +private: + /* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */ + void work_imp(); + + + /* Internal methods */ + bool extract_fft_and_pdcch_llr(); + + /* ... for DL */ + bool decode_pdcch_ul(mac_interface_phy::mac_grant_t *grant); + bool decode_pdcch_dl(mac_interface_phy::mac_grant_t *grant); + bool decode_phich(bool *ack); + bool decode_pdsch(srslte_ra_dl_grant_t *grant, uint8_t *payload, srslte_softbuffer_rx_t* softbuffer, int rv, uint16_t rnti, uint32_t pid); + + /* ... for UL */ + void encode_pusch(srslte_ra_ul_grant_t *grant, uint8_t *payload, uint32_t current_tx_nb, srslte_softbuffer_tx_t *softbuffer, + uint32_t rv, uint16_t rnti, bool is_from_rar); + void encode_pucch(); + void encode_srs(); + void reset_uci(); + void set_uci_sr(); + void set_uci_periodic_cqi(); + void set_uci_aperiodic_cqi(); + void set_uci_ack(bool ack); + bool srs_is_ready_to_send(); + float set_power(float tx_power); + void setup_tx_gain(); + + void update_measurements(); + + void tr_log_start(); + void tr_log_end(); + struct timeval tr_time[3]; + srslte::trace tr_exec; + bool trace_enabled; + + + /* Common objects */ + phch_common *phy; + srslte_cell_t cell; + bool cell_initiated; + cf_t *signal_buffer[SRSLTE_MAX_PORTS]; + uint32_t tti; + uint32_t tx_tti; + bool pregen_enabled; + uint32_t last_dl_pdcch_ncce; + bool rnti_is_set; + + /* Objects for DL */ + srslte_ue_dl_t ue_dl; + uint32_t cfi; + uint16_t dl_rnti; + + /* Objects for UL */ + srslte_ue_ul_t ue_ul; + srslte_timestamp_t tx_time; + srslte_uci_data_t uci_data; + uint16_t ul_rnti; + + // UL configuration parameters + srslte_refsignal_srs_cfg_t srs_cfg; + srslte_pucch_cfg_t pucch_cfg; + srslte_refsignal_dmrs_pusch_cfg_t dmrs_cfg; + srslte_pusch_hopping_cfg_t pusch_hopping; + srslte_pucch_sched_t pucch_sched; + srslte_uci_cfg_t uci_cfg; + srslte_cqi_periodic_cfg_t period_cqi; + srslte_ue_ul_powerctrl_t power_ctrl; + uint32_t I_sr; + float cfo; + bool rar_cqi_request; + + // Metrics + dl_metrics_t dl_metrics; + ul_metrics_t ul_metrics; + +#ifdef LOG_EXECTIME + struct timeval logtime_start[3]; + bool chest_done; +#endif + +}; + +} // namespace srsue + +#endif // UEPHYWORKER_H + diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h new file mode 100644 index 000000000..53ae65a44 --- /dev/null +++ b/srsue/hdr/phy/phy.h @@ -0,0 +1,167 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UEPHY_H +#define UEPHY_H + +#include "srslte/srslte.h" +#include "common/phy_interface.h" +#include "common/log.h" +#include "phy/phy_metrics.h" +#include "phy/phch_recv.h" +#include "phy/prach.h" +#include "phy/phch_worker.h" +#include "phy/phch_common.h" +#include "radio/radio.h" +#include "common/task_dispatcher.h" +#include "common/trace.h" +#include "common/mac_interface.h" +#include "common/interfaces.h" + +namespace srsue { + +typedef _Complex float cf_t; + +class phy + : public phy_interface_mac + , public phy_interface_rrc +{ +public: + phy(); + bool init(srslte::radio_multi *radio_handler, + mac_interface_phy *mac, + rrc_interface_phy *rrc, + srslte::log *log_h, + phy_args_t *args = NULL); + + void stop(); + + void set_agc_enable(bool enabled); + + void get_metrics(phy_metrics_t &m); + + + + static uint32_t tti_to_SFN(uint32_t tti); + static uint32_t tti_to_subf(uint32_t tti); + + void enable_pregen_signals(bool enable); + + void start_trace(); + void write_trace(std::string filename); + + /********** RRC INTERFACE ********************/ + void reset(); + bool status_is_sync(); + void configure_ul_params(bool pregen_disabled = false); + void resync_sfn(); + + /********** MAC INTERFACE ********************/ + /* Functions to synchronize with a cell */ + void sync_start(); + void sync_stop(); + + /* Sets a C-RNTI allowing the PHY to pregenerate signals if necessary */ + void set_crnti(uint16_t rnti); + + /* Instructs the PHY to configure using the parameters written by set_param() */ + void configure_prach_params(); + + /* Transmits PRACH in the next opportunity */ + void prach_send(uint32_t preamble_idx, int allowed_subframe = -1, float target_power_dbm = 0.0); + int prach_tx_tti(); + + /* Indicates the transmission of a SR signal in the next opportunity */ + void sr_send(); + int sr_last_tx_tti(); + + // Time advance commands + void set_timeadv_rar(uint32_t ta_cmd); + void set_timeadv(uint32_t ta_cmd); + + /* Sets RAR grant payload */ + void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); + + /* Instruct the PHY to decode PDCCH with the CRC scrambled with given RNTI */ + void pdcch_ul_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1); + void pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1); + void pdcch_ul_search_reset(); + void pdcch_dl_search_reset(); + + /* Get/Set PHY parameters interface from RRC */ + void get_config(phy_cfg_t *phy_cfg); + void set_config(phy_cfg_t *phy_cfg); + void set_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated); + void set_config_common(phy_cfg_common_t *common); + void set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd); + void set_config_64qam_en(bool enable); + + + float get_phr(); + float get_pathloss_db(); + + uint32_t get_current_tti(); + void get_current_cell(srslte_cell_t *cell); + + void start_plot(); + +private: + + uint32_t nof_workers; + + const static int MAX_WORKERS = 4; + const static int DEFAULT_WORKERS = 2; + + const static int SF_RECV_THREAD_PRIO = 1; + const static int WORKERS_THREAD_PRIO = 0; + + srslte::radio_multi *radio_handler; + srslte::log *log_h; + + srslte::thread_pool workers_pool; + std::vector workers; + phch_common workers_common; + phch_recv sf_recv; + prach prach_buffer; + + srslte_cell_t cell; + + phy_cfg_t config; + phy_args_t *args; + phy_args_t default_args; + + /* Current time advance */ + uint32_t n_ta; + + bool init_(srslte::radio *radio_handler, mac_interface_phy *mac, srslte::log *log_h, bool do_agc, uint32_t nof_workers); + void set_default_args(phy_args_t *args); + bool check_args(phy_args_t *args); + +}; + +} // namespace srsue + +#endif // UEPHY_H diff --git a/srsue/hdr/phy/phy_metrics.h b/srsue/hdr/phy/phy_metrics.h new file mode 100644 index 000000000..eafff2183 --- /dev/null +++ b/srsue/hdr/phy/phy_metrics.h @@ -0,0 +1,68 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UE_PHY_METRICS_H +#define UE_PHY_METRICS_H + + +namespace srsue { + +struct sync_metrics_t +{ + float cfo; + float sfo; +}; + +struct dl_metrics_t +{ + float n; + float sinr; + float rsrp; + float rsrq; + float rssi; + float turbo_iters; + float mcs; + float pathloss; + float mabr_mbps; +}; + +struct ul_metrics_t +{ + float mcs; + float power; + float mabr_mbps; +}; + +struct phy_metrics_t +{ + sync_metrics_t sync; + dl_metrics_t dl; + ul_metrics_t ul; +}; + +} // namespace srsue + +#endif // UE_PHY_METRICS_H diff --git a/srsue/hdr/phy/prach.h b/srsue/hdr/phy/prach.h new file mode 100644 index 000000000..c6f45f3f4 --- /dev/null +++ b/srsue/hdr/phy/prach.h @@ -0,0 +1,87 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UEPRACH_H +#define UEPRACH_H + +#include + +#include "srslte/srslte.h" +#include "radio/radio.h" +#include "common/log.h" +#include "common/phy_interface.h" + +namespace srsue { + + class prach { + public: + prach() { + bzero(&prach_obj, sizeof(srslte_prach_t)); + bzero(&cell, sizeof(srslte_cell_t)); + bzero(&cfo_h, sizeof(srslte_cfo_t)); + + args = NULL; + config = NULL; + initiated = false; + signal_buffer = NULL; + transmitted_tti = 0; + target_power_dbm = 0; + } + void init(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *config, phy_args_t *args, srslte::log *log_h); + bool init_cell(srslte_cell_t cell); + void free_cell(); + bool prepare_to_send(uint32_t preamble_idx, int allowed_subframe = -1, float target_power_dbm = -1); + bool is_ready_to_send(uint32_t current_tti); + int tx_tti(); + + void send(srslte::radio* radio_handler, float cfo, float pathloss, srslte_timestamp_t rx_time); + float get_p0_preamble(); + + static const uint32_t tx_advance_sf = 4; // Number of subframes to advance transmission + + private: + + LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *config; + phy_args_t *args; + + srslte::log *log_h; + int preamble_idx; + int allowed_subframe; + bool initiated; + uint32_t len; + cf_t *buffer[64]; + srslte_prach_t prach_obj; + int transmitted_tti; + srslte_cell_t cell; + cf_t *signal_buffer; + srslte_cfo_t cfo_h; + float target_power_dbm; + + }; + +} // namespace srsue + +#endif // UEPRACH_H diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h new file mode 100644 index 000000000..b3c760ad5 --- /dev/null +++ b/srsue/hdr/ue.h @@ -0,0 +1,201 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/****************************************************************************** + * File: ue.h + * Description: Top-level UE class. Creates and links all + * layers and helpers. + *****************************************************************************/ + +#ifndef UE_H +#define UE_H + +#include +#include +#include + +#include "radio/radio_multi.h" +#include "phy/phy.h" +#include "mac/mac.h" +#include "upper/rlc.h" +#include "upper/pdcp.h" +#include "upper/rrc.h" +#include "upper/nas.h" +#include "upper/gw.h" +#include "upper/usim.h" + +#include "common/buffer_pool.h" +#include "common/interfaces.h" +#include "common/logger.h" +#include "common/log_filter.h" + +#include "ue_metrics_interface.h" + +namespace srsue { + +/******************************************************************************* + UE Parameters +*******************************************************************************/ + +typedef struct { + float dl_freq; + float ul_freq; + float rx_gain; + float tx_gain; + uint32_t nof_rx_ant; + std::string device_name; + std::string device_args; + std::string time_adv_nsamples; + std::string burst_preamble; +}rf_args_t; + +typedef struct { + bool enable; + std::string filename; +}pcap_args_t; + +typedef struct { + bool enable; + std::string phy_filename; + std::string radio_filename; +}trace_args_t; + +typedef struct { + std::string phy_level; + std::string mac_level; + std::string rlc_level; + std::string pdcp_level; + std::string rrc_level; + std::string gw_level; + std::string nas_level; + std::string usim_level; + std::string all_level; + int phy_hex_limit; + int mac_hex_limit; + int rlc_hex_limit; + int pdcp_hex_limit; + int rrc_hex_limit; + int gw_hex_limit; + int nas_hex_limit; + int usim_hex_limit; + int all_hex_limit; + std::string filename; +}log_args_t; + +typedef struct { + bool enable; +}gui_args_t; + +typedef struct { + phy_args_t phy; + float metrics_period_secs; + bool pregenerate_signals; + int ue_cateogry; + +}expert_args_t; + +typedef struct { + rf_args_t rf; + rf_cal_t rf_cal; + pcap_args_t pcap; + trace_args_t trace; + log_args_t log; + gui_args_t gui; + usim_args_t usim; + expert_args_t expert; +}all_args_t; + +/******************************************************************************* + Main UE class +*******************************************************************************/ + +class ue + :public ue_interface + ,public ue_metrics_interface +{ +public: + static ue* get_instance(void); + static void cleanup(void); + + bool init(all_args_t *args_); + void stop(); + bool is_attached(); + void start_plot(); + + static void rf_msg(srslte_rf_error_t error); + void handle_rf_msg(srslte_rf_error_t error); + + // UE metrics interface + bool get_metrics(ue_metrics_t &m); + + void pregenerate_signals(bool enable); + + // Testing + void test_con_restablishment(); + + +private: + static ue *instance; + ue(); + ~ue(); + + srslte::radio_multi radio; + srsue::phy phy; + srsue::mac mac; + srslte::mac_pcap mac_pcap; + srsue::rlc rlc; + srsue::pdcp pdcp; + srsue::rrc rrc; + srsue::nas nas; + srsue::gw gw; + srsue::usim usim; + + srslte::logger logger; + srslte::log_filter rf_log; + srslte::log_filter phy_log; + srslte::log_filter mac_log; + srslte::log_filter rlc_log; + srslte::log_filter pdcp_log; + srslte::log_filter rrc_log; + srslte::log_filter nas_log; + srslte::log_filter gw_log; + srslte::log_filter usim_log; + + srslte::byte_buffer_pool *pool; + + all_args_t *args; + bool started; + rf_metrics_t rf_metrics; + + srslte::LOG_LEVEL_ENUM level(std::string l); + + bool check_srslte_version(); +}; + +} // namespace srsue + +#endif // UE_H + diff --git a/srsue/hdr/ue_metrics_interface.h b/srsue/hdr/ue_metrics_interface.h new file mode 100644 index 000000000..19cf15c5c --- /dev/null +++ b/srsue/hdr/ue_metrics_interface.h @@ -0,0 +1,63 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef UE_METRICS_INTERFACE_H +#define UE_METRICS_INTERFACE_H + +#include + +#include "upper/gw_metrics.h" +#include "upper/rlc_metrics.h" +#include "mac/mac_metrics.h" +#include "phy/phy_metrics.h" + +namespace srsue { + +typedef struct { + uint32_t rf_o; + uint32_t rf_u; + uint32_t rf_l; + bool rf_error; +}rf_metrics_t; + +typedef struct { + rf_metrics_t rf; + phy_metrics_t phy; + mac_metrics_t mac; + rlc_metrics_t rlc; + gw_metrics_t gw; +}ue_metrics_t; + +// UE interface +class ue_metrics_interface +{ +public: + virtual bool get_metrics(ue_metrics_t &m) = 0; +}; + +} // namespace srsue + +#endif // UE_METRICS_INTERFACE_H diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt new file mode 100644 index 000000000..fc13d814b --- /dev/null +++ b/srsue/src/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright 2015 Software Radio Systems Limited +# +# This file is part of srsUE +# +# srsUE 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. +# +# srsUE 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. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_subdirectory(phy) +add_subdirectory(mac) + +if (RPATH) + SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +endif (RPATH) + +add_executable(ue main.cc ue.cc metrics_stdout.cc) +target_link_libraries(ue srsue_mac + srsue_phy + srslte_common + srslte_phy + srslte_upper + srslte_radio + ${SRSLTE_LIBRARIES} + ${LIBLTE_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) + +if (RPATH) + set_target_properties(ue PROPERTIES INSTALL_RPATH ".") +endif (RPATH) + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "Added custom post-build command: ${BUILD_CMD}") + add_custom_command(TARGET ue POST_BUILD COMMAND ${BUILD_CMD}) +else(NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "No post-build command defined") +endif (NOT ${BUILD_CMD} STREQUAL "") diff --git a/srsue/src/mac/CMakeLists.txt b/srsue/src/mac/CMakeLists.txt new file mode 100644 index 000000000..595b0dbda --- /dev/null +++ b/srsue/src/mac/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2015 Software Radio Systems Limited +# +# This file is part of srsUE +# +# srsUE 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. +# +# srsUE 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. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsue_mac ${SOURCES}) +target_link_libraries(srsue_mac) diff --git a/srsue/src/mac/demux.cc b/srsue/src/mac/demux.cc new file mode 100644 index 000000000..98f74a8e5 --- /dev/null +++ b/srsue/src/mac/demux.cc @@ -0,0 +1,206 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include "mac/mac.h" +#include "mac/demux.h" +#include "common/phy_interface.h" + +namespace srsue { + +demux::demux() : mac_msg(20), pending_mac_msg(20) +{ +} + +void demux::init(phy_interface_mac* phy_h_, rlc_interface_mac *rlc_, srslte::log* log_h_, srslte::timers* timers_db_) +{ + phy_h = phy_h_; + log_h = log_h_; + rlc = rlc_; + timers_db = timers_db_; + pdus.init(this, log_h); +} + +void demux::set_uecrid_callback(bool (*callback)(void*,uint64_t), void *arg) { + uecrid_callback = callback; + uecrid_callback_arg = arg; +} + +bool demux::get_uecrid_successful() { + return is_uecrid_successful; +} + +void demux::deallocate(uint8_t* payload_buffer_ptr) +{ + if (payload_buffer_ptr != bcch_buffer) { + pdus.deallocate(payload_buffer_ptr); + } +} + +uint8_t* demux::request_buffer(uint32_t pid, uint32_t len) +{ + uint8_t *buff = NULL; + if (pid < NOF_HARQ_PID) { + return pdus.request(len); + } else if (pid == NOF_HARQ_PID) { + buff = bcch_buffer; + } else { + Error("Requested buffer for invalid PID=%d\n", pid); + } + return buff; +} + +/* Demultiplexing of MAC PDU associated with a Temporal C-RNTI. The PDU will + * remain in buffer until demultiplex_pending_pdu() is called. + * This features is provided to enable the Random Access Procedure to decide + * wether the PDU shall pass to upper layers or not, which depends on the + * Contention Resolution result. + * + * Warning: this function does some processing here assuming ACK deadline is not an + * issue here because Temp C-RNTI messages have small payloads + */ +void demux::push_pdu_temp_crnti(uint8_t *buff, uint32_t nof_bytes) +{ + if (nof_bytes > 0) { + // Unpack DLSCH MAC PDU + pending_mac_msg.init_rx(nof_bytes); + pending_mac_msg.parse_packet(buff); + + // Look for Contention Resolution UE ID + is_uecrid_successful = false; + while(pending_mac_msg.next() && !is_uecrid_successful) { + if (pending_mac_msg.get()->ce_type() == srslte::sch_subh::CON_RES_ID) { + Debug("Found Contention Resolution ID CE\n"); + is_uecrid_successful = uecrid_callback(uecrid_callback_arg, pending_mac_msg.get()->get_con_res_id()); + } + } + + pending_mac_msg.reset(); + + Debug("Saved MAC PDU with Temporal C-RNTI in buffer\n"); + + pdus.push(buff, nof_bytes); + } else { + Warning("Trying to push PDU with payload size zero\n"); + } +} + +/* Demultiplexing of logical channels and dissassemble of MAC CE + * This function enqueues the packet and returns quicly because ACK + * deadline is important here. + */ +void demux::push_pdu(uint32_t pid, uint8_t *buff, uint32_t nof_bytes) +{ + if (pid < NOF_HARQ_PID) { + return pdus.push(buff, nof_bytes); + } else if (pid == NOF_HARQ_PID) { + /* Demultiplexing of MAC PDU associated with SI-RNTI. The PDU passes through + * the MAC in transparent mode. + * Warning: In this case function sends the message to RLC now, since SI blocks do not + * require ACK feedback to be transmitted quickly. + */ + Debug("Pushed BCCH MAC PDU in transparent mode\n"); + rlc->write_pdu_bcch_dlsch(buff, nof_bytes); + } else { + Error("Pushed buffer for invalid PID=%d\n", pid); + } +} + +bool demux::process_pdus() +{ + return pdus.process_pdus(); +} + +void demux::process_pdu(uint8_t *mac_pdu, uint32_t nof_bytes) +{ + // Unpack DLSCH MAC PDU + mac_msg.init_rx(nof_bytes); + mac_msg.parse_packet(mac_pdu); + + process_sch_pdu(&mac_msg); + //srslte_vec_fprint_byte(stdout, mac_pdu, nof_bytes); + Debug("MAC PDU processed\n"); +} + +void demux::process_sch_pdu(srslte::sch_pdu *pdu_msg) +{ + while(pdu_msg->next()) { + if (pdu_msg->get()->is_sdu()) { + bool route_pdu = true; + if (pdu_msg->get()->get_sdu_lcid() == 0) { + uint8_t *x = pdu_msg->get()->get_sdu_ptr(); + uint32_t sum = 0; + for (int i=0;iget()->get_payload_size();i++) { + sum += x[i]; + } + if (sum == 0) { + route_pdu = false; + Warning("Received all zero PDU\n"); + } + } + // Route logical channel + if (route_pdu) { + Info("Delivering PDU for lcid=%d, %d bytes\n", pdu_msg->get()->get_sdu_lcid(), pdu_msg->get()->get_payload_size()); + rlc->write_pdu(pdu_msg->get()->get_sdu_lcid(), pdu_msg->get()->get_sdu_ptr(), pdu_msg->get()->get_payload_size()); + } + } else { + // Process MAC Control Element + if (!process_ce(pdu_msg->get())) { + Warning("Received Subheader with invalid or unkonwn LCID\n"); + } + } + } +} + +bool demux::process_ce(srslte::sch_subh *subh) { + switch(subh->ce_type()) { + case srslte::sch_subh::CON_RES_ID: + // Do nothing + break; + case srslte::sch_subh::TA_CMD: + phy_h->set_timeadv(subh->get_ta_cmd()); + Info("Received TA=%d\n", subh->get_ta_cmd()); + + // Start or restart timeAlignmentTimer + timers_db->get(mac::TIME_ALIGNMENT)->reset(); + timers_db->get(mac::TIME_ALIGNMENT)->run(); + break; + case srslte::sch_subh::PADDING: + break; + default: + Error("MAC CE 0x%x not supported\n", subh->ce_type()); + break; + } + return true; +} + + +} diff --git a/srsue/src/mac/dl_harq.cc b/srsue/src/mac/dl_harq.cc new file mode 100644 index 000000000..27dc881e5 --- /dev/null +++ b/srsue/src/mac/dl_harq.cc @@ -0,0 +1,337 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include "mac/mac.h" +#include "mac/dl_harq.h" + + +namespace srsue { + + + /*********************************************************** + * + * HARQ ENTITY + * + *********************************************************/ + +dl_harq_entity::dl_harq_entity() +{ + pcap = NULL; +} + +bool dl_harq_entity::init(srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_, srslte::timers* timers_, demux *demux_unit_) +{ + timers_db = timers_; + demux_unit = demux_unit_; + mac_cfg = mac_cfg_; + si_window_start = 0; + log_h = log_h_; + for (uint32_t i=0;iget(mac::TIME_ALIGNMENT)->is_running()) { + //phy_h->send_sps_ack(); + Warning("PHY Send SPS ACK not implemented\n"); + } + } else { + Error("SPS not implemented\n"); + //dl_sps_assig.reset(grant.tti, grant); + //grant.ndi = true; + //procs[harq_pid].save_grant(); + } + } + } +} + +void dl_harq_entity::tb_decoded(bool ack, srslte_rnti_type_t rnti_type, uint32_t harq_pid) +{ + if (rnti_type == SRSLTE_RNTI_SI) { + proc[NOF_HARQ_PROC].tb_decoded(ack); + } else { + proc[harq_pid%NOF_HARQ_PROC].tb_decoded(ack); + } +} + +int dl_harq_entity::get_current_tbs(uint32_t harq_pid) +{ + return proc[harq_pid%NOF_HARQ_PROC].get_current_tbs(); +} + + +bool dl_harq_entity::generate_ack_callback(void *arg) +{ + demux *demux_unit = (demux*) arg; + return demux_unit->get_uecrid_successful(); +} + +void dl_harq_entity::set_si_window_start(int si_window_start_) +{ + si_window_start = si_window_start_; +} + +float dl_harq_entity::get_average_retx() +{ + return average_retx; +} + + /*********************************************************** + * + * HARQ PROCESS + * + *********************************************************/ + +dl_harq_entity::dl_harq_process::dl_harq_process() { + is_initiated = false; + ack = false; + bzero(&cur_grant, sizeof(mac_interface_phy::mac_grant_t)); +} + +void dl_harq_entity::dl_harq_process::reset() { + ack = false; + payload_buffer_ptr = NULL; + bzero(&cur_grant, sizeof(mac_interface_phy::mac_grant_t)); + if (is_initiated) { + srslte_softbuffer_rx_reset(&softbuffer); + } +} + +bool dl_harq_entity::dl_harq_process::init(uint32_t pid_, dl_harq_entity *parent) { + if (srslte_softbuffer_rx_init(&softbuffer, 110)) { + Error("Error initiating soft buffer\n"); + return false; + } else { + pid = pid_; + is_initiated = true; + harq_entity = parent; + log_h = harq_entity->log_h; + return true; + } +} + +bool dl_harq_entity::dl_harq_process::is_sps() +{ + return false; +} + +bool dl_harq_entity::dl_harq_process::calc_is_new_transmission(mac_interface_phy::mac_grant_t grant) { + + bool is_new_tb = true; + if (srslte_tti_interval(grant.tti, cur_grant.tti) <= 8 && grant.n_bytes == cur_grant.n_bytes || + pid == HARQ_BCCH_PID) + { + is_new_tb = false; + } + + if ((grant.ndi != cur_grant.ndi && !is_new_tb) || // NDI toggled for same TB + is_new_tb || // is new TB + (pid == HARQ_BCCH_PID && grant.rv == 0)) // Broadcast PID and 1st TX (RV=0) + { + is_new_transmission = true; + Debug("Set HARQ for new transmission\n"); + } else { + is_new_transmission = false; + Debug("Set HARQ for retransmission\n"); + } + + return is_new_transmission; +} + +void dl_harq_entity::dl_harq_process::new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action) +{ + // Compute RV for BCCH when not specified in PDCCH format + if (pid == HARQ_BCCH_PID && grant.rv == -1) { + uint32_t k; + if ((grant.tti/10)%2 == 0 && grant.tti%10 == 5) { // This is SIB1, k is different + k = (grant.tti/20)%4; + grant.rv = ((uint32_t) ceilf((float)1.5*k))%4; + } else if (grant.rv == -1) { + k = (grant.tti-harq_entity->si_window_start)%4; + grant.rv = ((uint32_t) ceilf((float)1.5*k))%4; + } + } + calc_is_new_transmission(grant); + if (is_new_transmission) { + ack = false; + srslte_softbuffer_rx_reset_tbs(&softbuffer, cur_grant.n_bytes*8); + n_retx = 0; + } + + // Save grant + grant.last_ndi = cur_grant.ndi; + grant.last_tti = cur_grant.tti; + memcpy(&cur_grant, &grant, sizeof(mac_interface_phy::mac_grant_t)); + + // Fill action structure + bzero(action, sizeof(mac_interface_phy::tb_action_dl_t)); + action->default_ack = ack; + action->generate_ack = true; + action->decode_enabled = false; + + // If data has not yet been successfully decoded + if (ack == false) { + + // Instruct the PHY To combine the received data and attempt to decode it + payload_buffer_ptr = harq_entity->demux_unit->request_buffer(pid, cur_grant.n_bytes); + action->payload_ptr = payload_buffer_ptr; + if (!action->payload_ptr) { + action->decode_enabled = false; + Error("Can't get a buffer for TBS=%d\n", cur_grant.n_bytes); + return; + } + action->decode_enabled = true; + action->rv = cur_grant.rv; + action->rnti = cur_grant.rnti; + action->softbuffer = &softbuffer; + memcpy(&action->phy_grant, &cur_grant.phy_grant, sizeof(srslte_phy_grant_t)); + n_retx++; + + } else { + Warning("DL PID %d: Received duplicate TB. Discarting and retransmitting ACK\n", pid); + } + + if (pid == HARQ_BCCH_PID || harq_entity->timers_db->get(mac::TIME_ALIGNMENT)->is_expired()) { + // Do not generate ACK + Debug("Not generating ACK\n"); + action->generate_ack = false; + } else { + if (cur_grant.rnti_type == SRSLTE_RNTI_TEMP && ack == false) { + // Postpone ACK after contention resolution is resolved + action->generate_ack_callback = harq_entity->generate_ack_callback; + action->generate_ack_callback_arg = harq_entity->demux_unit; + Debug("ACK pending contention resolution\n"); + } else { + Debug("Generating ACK\n"); + } + } +} + +int dl_harq_entity::dl_harq_process::get_current_tbs() +{ + return cur_grant.n_bytes*8; +} + +void dl_harq_entity::dl_harq_process::tb_decoded(bool ack_) +{ + ack = ack_; + if (ack == true) { + if (pid == HARQ_BCCH_PID) { + if (harq_entity->pcap) { + harq_entity->pcap->write_dl_sirnti(payload_buffer_ptr, cur_grant.n_bytes, ack, cur_grant.tti); + } + Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (BCCH)\n", cur_grant.n_bytes); + harq_entity->demux_unit->push_pdu(pid, payload_buffer_ptr, cur_grant.n_bytes); + } else { + if (harq_entity->pcap) { + harq_entity->pcap->write_dl_crnti(payload_buffer_ptr, cur_grant.n_bytes, cur_grant.rnti, ack, cur_grant.tti); + } + if (ack) { + if (cur_grant.rnti_type == SRSLTE_RNTI_TEMP) { + Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI)\n", cur_grant.n_bytes); + harq_entity->demux_unit->push_pdu_temp_crnti(payload_buffer_ptr, cur_grant.n_bytes); + } else { + Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit\n", cur_grant.n_bytes); + harq_entity->demux_unit->push_pdu(pid, payload_buffer_ptr, cur_grant.n_bytes); + + // Compute average number of retransmissions per packet + harq_entity->average_retx = SRSLTE_VEC_CMA((float) n_retx, harq_entity->average_retx, harq_entity->nof_pkts++); + } + } + } + } else { + harq_entity->demux_unit->deallocate(payload_buffer_ptr); + } + + Info("DL %d: %s tbs=%d, rv=%d, ack=%s, ndi=%d (%d), tti=%d (%d)\n", + pid, is_new_transmission?"newTX":"reTX ", + cur_grant.n_bytes, cur_grant.rv, ack?"OK":"KO", + cur_grant.ndi, cur_grant.last_ndi, cur_grant.tti, cur_grant.last_tti); + + if (ack && pid == HARQ_BCCH_PID) { + reset(); + } +} + + + +} diff --git a/srsue/src/mac/mac.cc b/srsue/src/mac/mac.cc new file mode 100644 index 000000000..c06fb13fa --- /dev/null +++ b/srsue/src/mac/mac.cc @@ -0,0 +1,543 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include +#include +#include +#include + +#include "common/log.h" +#include "mac/mac.h" +#include "common/pcap.h" + + +namespace srsue { + +mac::mac() : ttisync(10240), + timers_db((uint32_t) NOF_MAC_TIMERS), + pdu_process_thread(&demux_unit) +{ + started = false; + pcap = NULL; + signals_pregenerated = false; + bzero(&metrics, sizeof(mac_metrics_t)); +} + +bool mac::init(phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac *rrc, srslte::log *log_h_) +{ + started = false; + phy_h = phy; + rlc_h = rlc; + rrc_h = rrc; + log_h = log_h_; + tti = 0; + is_synchronized = false; + last_temporal_crnti = 0; + phy_rnti = 0; + + srslte_softbuffer_rx_init(&pch_softbuffer, 100); + + bsr_procedure.init( rlc_h, log_h, &config, &timers_db); + phr_procedure.init(phy_h, log_h, &config, &timers_db); + mux_unit.init ( rlc_h, log_h, &bsr_procedure, &phr_procedure); + demux_unit.init (phy_h, rlc_h, log_h, &timers_db); + ra_procedure.init (phy_h, rrc, log_h, &uernti, &config, &timers_db, &mux_unit, &demux_unit); + sr_procedure.init (phy_h, rrc, log_h, &config); + ul_harq.init ( log_h, &uernti, &config, &timers_db, &mux_unit); + dl_harq.init ( log_h, &config, &timers_db, &demux_unit); + + reset(); + + started = true; + start(MAC_MAIN_THREAD_PRIO); + + + return started; +} + +void mac::stop() +{ + started = false; + ttisync.increase(); + upper_timers_thread.thread_cancel(); + pdu_process_thread.stop(); + wait_thread_finish(); +} + +void mac::start_pcap(srslte::mac_pcap* pcap_) +{ + pcap = pcap_; + dl_harq.start_pcap(pcap); + ul_harq.start_pcap(pcap); + ra_procedure.start_pcap(pcap); +} + +// Implement Section 5.8 +void mac::reconfiguration() +{ + +} + +// Implement Section 5.9 +void mac::reset() +{ + bzero(&metrics, sizeof(mac_metrics_t)); + + Info("Resetting MAC\n"); + + timers_db.stop_all(); + upper_timers_thread.reset(); + + ul_harq.reset_ndi(); + + mux_unit.msg3_flush(); + mux_unit.reset(); + + ra_procedure.reset(); + sr_procedure.reset(); + bsr_procedure.reset(); + phr_procedure.reset(); + + dl_harq.reset(); + phy_h->pdcch_dl_search_reset(); + phy_h->pdcch_ul_search_reset(); + + signals_pregenerated = false; + is_first_ul_grant = true; + + bzero(&uernti, sizeof(ue_rnti_t)); +} + +void mac::run_thread() { + int cnt=0; + + Info("Waiting PHY to synchronize with cell\n"); + phy_h->sync_start(); + while(!phy_h->get_current_tti() && started) { + usleep(50000); + } + Debug("Setting ttysync to %d\n", phy_h->get_current_tti()); + ttisync.set_producer_cntr(phy_h->get_current_tti()); + + while(started) { + + /* Warning: Here order of invocation of procedures is important!! */ + ttisync.wait(); + tti = phy_h->get_current_tti(); + + if (started) { + log_h->step(tti); + + // Step all procedures + bsr_procedure.step(tti); + phr_procedure.step(tti); + + // Check if BSR procedure need to start SR + + if (bsr_procedure.need_to_send_sr(tti)) { + Debug("Starting SR procedure by BSR request, PHY TTI=%d\n", tti); + sr_procedure.start(); + } + if (bsr_procedure.need_to_reset_sr()) { + Debug("Resetting SR procedure by BSR request\n"); + sr_procedure.reset(); + } + sr_procedure.step(tti); + + // Check SR if we need to start RA + if (sr_procedure.need_random_access()) { + ra_procedure.start_mac_order(); + } + ra_procedure.step(tti); + + if (ra_procedure.is_successful() && !signals_pregenerated) { + + // Configure PHY to look for UL C-RNTI grants + phy_h->pdcch_ul_search(SRSLTE_RNTI_USER, uernti.crnti); + phy_h->pdcch_dl_search(SRSLTE_RNTI_USER, uernti.crnti); + + // Pregenerate UL signals and C-RNTI scrambling sequences + Debug("Pre-computing C-RNTI scrambling sequences for C-RNTI=0x%x\n", uernti.crnti); + phy_h->set_crnti(uernti.crnti); + signals_pregenerated = true; + } + } + } +} + +void mac::bcch_start_rx() +{ + bcch_start_rx(tti, -1); +} + +void mac::bcch_start_rx(int si_window_start, int si_window_length) +{ + if (si_window_length >= 0 && si_window_start >= 0) { + dl_harq.set_si_window_start(si_window_start); + phy_h->pdcch_dl_search(SRSLTE_RNTI_SI, SRSLTE_SIRNTI, si_window_start, si_window_start+si_window_length); + } else { + phy_h->pdcch_dl_search(SRSLTE_RNTI_SI, SRSLTE_SIRNTI, si_window_start); + } + Info("SCHED: Searching for DL grant for SI-RNTI window_st=%d, window_len=%d\n", si_window_start, si_window_length); +} + +void mac::bcch_stop_rx() +{ + phy_h->pdcch_dl_search_reset(); +} + +void mac::pcch_start_rx() +{ + phy_h->pdcch_dl_search(SRSLTE_RNTI_PCH, SRSLTE_PRNTI); + Info("SCHED: Searching for DL grant for P-RNTI\n"); +} + +void mac::pcch_stop_rx() +{ + phy_h->pdcch_dl_search_reset(); +} + + +void mac::tti_clock(uint32_t tti) +{ + ttisync.increase(); +} + +void mac::bch_decoded_ok(uint8_t* payload, uint32_t len) +{ + // Send MIB to RLC + rlc_h->write_pdu_bcch_bch(payload, len); + + if (pcap) { + pcap->write_dl_bch(payload, len, true, phy_h->get_current_tti()); + } +} + +void mac::pch_decoded_ok(uint32_t len) +{ + // Send PCH payload to RLC + rlc_h->write_pdu_pcch(pch_payload_buffer, len); + + if (pcap) { + pcap->write_dl_pch(pch_payload_buffer, len, true, phy_h->get_current_tti()); + } +} + +void mac::tb_decoded(bool ack, srslte_rnti_type_t rnti_type, uint32_t harq_pid) +{ + if (rnti_type == SRSLTE_RNTI_RAR) { + if (ack) { + ra_procedure.tb_decoded_ok(); + } + } else { + dl_harq.tb_decoded(ack, rnti_type, harq_pid); + if (ack) { + pdu_process_thread.notify(); + metrics.rx_brate += dl_harq.get_current_tbs(harq_pid); + } else { + metrics.rx_errors++; + } + metrics.rx_pkts++; + } +} + +void mac::new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action) +{ + if (grant.rnti_type == SRSLTE_RNTI_RAR) { + ra_procedure.new_grant_dl(grant, action); + } else if (grant.rnti_type == SRSLTE_RNTI_PCH) { + + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + action->generate_ack = false; + action->decode_enabled = true; + srslte_softbuffer_rx_reset_cb(&pch_softbuffer, 1); + action->payload_ptr = pch_payload_buffer; + action->softbuffer = &pch_softbuffer; + action->rnti = grant.rnti; + action->rv = grant.rv; + if (grant.n_bytes > pch_payload_buffer_sz) { + Error("Received grant for PCH (%d bytes) exceeds buffer (%d bytes)\n", grant.n_bytes, pch_payload_buffer_sz); + action->decode_enabled = false; + } + } else { + // If PDCCH for C-RNTI and RA procedure in Contention Resolution, notify it + if (grant.rnti_type == SRSLTE_RNTI_USER && ra_procedure.is_contention_resolution()) { + ra_procedure.pdcch_to_crnti(false); + } + dl_harq.new_grant_dl(grant, action); + } +} + +uint32_t mac::get_current_tti() +{ + return phy_h->get_current_tti(); +} + +void mac::new_grant_ul(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_ul_t* action) +{ + /* Start PHR Periodic timer on first UL grant */ + if (is_first_ul_grant) { + is_first_ul_grant = false; + timers_db.get(mac::PHR_TIMER_PERIODIC)->run(); + } + if (grant.rnti_type == SRSLTE_RNTI_USER && ra_procedure.is_contention_resolution()) { + ra_procedure.pdcch_to_crnti(true); + } + ul_harq.new_grant_ul(grant, action); + metrics.tx_pkts++; +} + +void mac::new_grant_ul_ack(mac_interface_phy::mac_grant_t grant, bool ack, mac_interface_phy::tb_action_ul_t* action) +{ + int tbs = ul_harq.get_current_tbs(tti); + ul_harq.new_grant_ul_ack(grant, ack, action); + if (!ack) { + metrics.tx_errors++; + } else { + metrics.tx_brate += tbs; + } + metrics.tx_pkts++; + if (!ack && ra_procedure.is_contention_resolution()) { + ra_procedure.harq_retx(); + } + if (grant.rnti_type == SRSLTE_RNTI_USER && ra_procedure.is_contention_resolution()) { + ra_procedure.pdcch_to_crnti(true); + } +} + +void mac::harq_recv(uint32_t tti, bool ack, mac_interface_phy::tb_action_ul_t* action) +{ + int tbs = ul_harq.get_current_tbs(tti); + ul_harq.harq_recv(tti, ack, action); + if (!ack) { + metrics.tx_errors++; + metrics.tx_pkts++; + } else { + metrics.tx_brate += tbs; + } + if (!ack && ra_procedure.is_contention_resolution()) { + ra_procedure.harq_retx(); + } +} + +void mac::setup_timers() +{ + int value = liblte_rrc_time_alignment_timer_num[config.main.time_alignment_timer]; + if (value > 0) { + timers_db.get(TIME_ALIGNMENT)->set(this, value); + } +} + +void mac::timer_expired(uint32_t timer_id) +{ + switch(timer_id) { + case TIME_ALIGNMENT: + timeAlignmentTimerExpire(); + break; + default: + break; + } +} + +/* Function called on expiry of TimeAlignmentTimer */ +void mac::timeAlignmentTimerExpire() +{ + printf("timeAlignmentTimer has expired value=%d ms\n", timers_db.get(TIME_ALIGNMENT)->get_timeout()); + rrc_h->release_pucch_srs(); + dl_harq.reset(); + ul_harq.reset(); +} + +void mac::get_rntis(ue_rnti_t* rntis) +{ + memcpy(rntis, &uernti, sizeof(ue_rnti_t)); +} + +void mac::set_contention_id(uint64_t uecri) +{ + uernti.contention_id = uecri; +} + +void mac::get_config(mac_cfg_t* mac_cfg) +{ + memcpy(mac_cfg, &config, sizeof(mac_cfg_t)); +} + +void mac::set_config(mac_cfg_t* mac_cfg) +{ + memcpy(&config, mac_cfg, sizeof(mac_cfg_t)); + setup_timers(); +} + +void mac::set_config_main(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT* main_cfg) +{ + memcpy(&config.main, main_cfg, sizeof(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT)); + setup_timers(); +} + +void mac::set_config_rach(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT* rach_cfg, uint32_t prach_config_index) +{ + memcpy(&config.rach, rach_cfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + config.prach_config_index = prach_config_index; +} + +void mac::set_config_sr(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT* sr_cfg) +{ + memcpy(&config.sr, sr_cfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); +} + +void mac::setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority, int PBR_x_tti, uint32_t BSD) +{ + Info("Logical Channel Setup: LCID=%d, LCG=%d, priority=%d, PBR=%d, BSd=%d\n", + lcid, lcg, priority, PBR_x_tti, BSD); + mux_unit.set_priority(lcid, priority, PBR_x_tti, BSD); + bsr_procedure.setup_lcg(lcid, lcg); + bsr_procedure.set_priority(lcid, priority); +} + +uint32_t mac::get_unique_id() +{ + return upper_timers_thread.get_unique_id(); +} + +/* Front-end to upper-layer timers */ +srslte::timers::timer* mac::get(uint32_t timer_id) +{ + return upper_timers_thread.get(timer_id); +} + + +void mac::get_metrics(mac_metrics_t &m) +{ + Info("DL retx: %.2f \%%, perpkt: %.2f, UL retx: %.2f \%% perpkt: %.2f\n", + metrics.rx_pkts?((float) 100*metrics.rx_errors/metrics.rx_pkts):0.0, + dl_harq.get_average_retx(), + metrics.tx_pkts?((float) 100*metrics.tx_errors/metrics.tx_pkts):0.0, + dl_harq.get_average_retx()); + + metrics.ul_buffer = (int) bsr_procedure.get_buffer_state(); + m = metrics; + bzero(&metrics, sizeof(mac_metrics_t)); +} + + +/******************************************************** + * + * Class to run upper-layer timers with normal priority + * + *******************************************************/ + +mac::upper_timers::upper_timers() : timers_db(MAC_NOF_UPPER_TIMERS) +{ + start_periodic(1000, MAC_MAIN_THREAD_PRIO+1); +} + +void mac::upper_timers::run_period() +{ + timers_db.step_all(); +} + +srslte::timers::timer* mac::upper_timers::get(uint32_t timer_id) +{ + return timers_db.get(timer_id%MAC_NOF_UPPER_TIMERS); +} + +uint32_t mac::upper_timers::get_unique_id() +{ + return timers_db.get_unique_id(); +} + +void mac::upper_timers::reset() +{ + timers_db.stop_all(); +} + + + + +/******************************************************** + * + * Class that runs a thread to process DL MAC PDUs from + * DEMU unit + * + *******************************************************/ +mac::pdu_process::pdu_process(demux *demux_unit_) +{ + demux_unit = demux_unit_; + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cvar, NULL); + have_data = false; + start(MAC_PDU_THREAD_PRIO); +} + +void mac::pdu_process::stop() +{ + pthread_mutex_lock(&mutex); + running = false; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + + wait_thread_finish(); +} + +void mac::pdu_process::notify() +{ + pthread_mutex_lock(&mutex); + have_data = true; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); +} + +void mac::pdu_process::run_thread() +{ + running = true; + while(running) { + have_data = demux_unit->process_pdus(); + if (!have_data) { + pthread_mutex_lock(&mutex); + while(!have_data && running) { + pthread_cond_wait(&cvar, &mutex); + } + pthread_mutex_unlock(&mutex); + } + } +} + + + + + + + +} + + + diff --git a/srsue/src/mac/mux.cc b/srsue/src/mac/mux.cc new file mode 100644 index 000000000..a927f9d72 --- /dev/null +++ b/srsue/src/mac/mux.cc @@ -0,0 +1,358 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include "mac/mux.h" +#include "mac/mac.h" + +#include +#include + +namespace srsue { + +mux::mux() : pdu_msg(MAX_NOF_SUBHEADERS) +{ + pthread_mutex_init(&mutex, NULL); + + pending_crnti_ce = 0; + + log_h = NULL; + rlc = NULL; + bsr_procedure = NULL; + phr_procedure = NULL; + + msg3_flush(); +} + +void mux::init(rlc_interface_mac *rlc_, srslte::log *log_h_, bsr_proc *bsr_procedure_, phr_proc *phr_procedure_) +{ + log_h = log_h_; + rlc = rlc_; + bsr_procedure = bsr_procedure_; + phr_procedure = phr_procedure_; + reset(); +} + +void mux::reset() +{ + lch.clear(); + pending_crnti_ce = 0; +} + +bool mux::is_pending_any_sdu() +{ + for (int i=0;iget_buffer_state(lch[i].id)) { + return true; + } + } + return false; +} + +bool mux::is_pending_sdu(uint32_t lch_id) { + return rlc->get_buffer_state(lch_id)>0; +} + +int mux::find_lchid(uint32_t lcid) +{ + for (int i=0;i= 0) { + lch.erase(lch.begin()+pos); + } else { + Error("Deleting logical channel id %d. Does not exist\n", lch_id); + } +} + +void mux::set_priority(uint32_t lch_id, uint32_t new_priority, int set_PBR, uint32_t set_BSD) +{ + int pos = find_lchid(lch_id); + + // Create new channel if it does not exist + if (pos < 0) { + lchid_t ch; + ch.id = lch_id; + ch.priority = new_priority; + ch.BSD = set_BSD; + ch.PBR = set_PBR; + ch.Bj = 0; + lch.push_back(ch); + } else { + lch[pos].priority = new_priority; + lch[pos].PBR = set_PBR; + lch[pos].BSD = set_BSD; + } + + // sort according to priority (increasing is lower priority) + std::sort(lch.begin(), lch.end(), sortPriority); +} + +srslte::sch_subh::cetype bsr_format_convert(bsr_proc::bsr_format_t format) { + switch(format) { + case bsr_proc::LONG_BSR: + return srslte::sch_subh::LONG_BSR; + case bsr_proc::SHORT_BSR: + return srslte::sch_subh::SHORT_BSR; + case bsr_proc::TRUNC_BSR: + return srslte::sch_subh::TRUNC_BSR; + } +} + +void mux::pusch_retx(uint32_t tx_tti, uint32_t pid) +{ + if (pid_has_bsr[pid%MAX_HARQ_PROC]) { + bsr_procedure->set_tx_tti(tx_tti); + } +} + +// Multiplexing and logical channel priorization as defined in Section 5.4.3 +uint8_t* mux::pdu_get(uint8_t *payload, uint32_t pdu_sz, uint32_t tx_tti, uint32_t pid) +{ + pthread_mutex_lock(&mutex); + + // Update Bj + for (int i=0;i= 0) { + lch[i].Bj += lch[i].PBR; + } + if (lch[i].Bj >= lch[i].BSD) { + lch[i].Bj = lch[i].BSD*lch[i].PBR; + } + } + +// Logical Channel Procedure + + pdu_msg.init_tx(payload, pdu_sz, true); + + // MAC control element for C-RNTI or data from UL-CCCH + if (!allocate_sdu(0, &pdu_msg, -1)) { + if (pending_crnti_ce) { + if (pdu_msg.new_subh()) { + if (!pdu_msg.get()->set_c_rnti(pending_crnti_ce)) { + Warning("Pending C-RNTI CE could not be inserted in MAC PDU\n"); + } + } + } + } + pending_crnti_ce = 0; + + bsr_proc::bsr_t bsr; + bool regular_bsr = bsr_procedure->need_to_send_bsr_on_ul_grant(pdu_msg.rem_size(), &bsr); + bool bsr_is_inserted = false; + + // MAC control element for BSR, with exception of BSR included for padding; + if (regular_bsr) { + if (pdu_msg.new_subh()) { + pdu_msg.get()->set_bsr(bsr.buff_size, bsr_format_convert(bsr.format)); + bsr_is_inserted = true; + } + } + // MAC control element for PHR + float phr_value; + if (phr_procedure->generate_phr_on_ul_grant(&phr_value)) { + if (pdu_msg.new_subh()) { + pdu_msg.get()->set_phr(phr_value); + } + } + // Update buffer states for all logical channels + int sdu_space = pdu_msg.get_sdu_space(); + for (int i=0;iget_buffer_state(lch[i].id); + lch[i].sched_len = 0; + } + + // data from any Logical Channel, except data from UL-CCCH; + // first only those with positive Bj + for (int i=0;i= 0) { + lch[i].Bj -= lch[i].sched_len; + } + } + } + + // If resources remain, allocate regardless of their Bj value + for (int i=0;i 0) { + for (int i=lch.size()-1;i--;i>=0) { + if (lch[i].sched_len > 0) { + lch[i].sched_len = -1; + break; + } + } + } + // Now allocate the SDUs from the RLC + for (int i=0;iinfo("Allocating scheduled lch=%d len=%d\n", lch[i].id, lch[i].sched_len); + allocate_sdu(lch[i].id, &pdu_msg, lch[i].sched_len); + } + } + + if (!regular_bsr) { + // Insert Padding BSR if not inserted Regular/Periodic BSR + if (bsr_procedure->generate_padding_bsr(pdu_msg.rem_size(), &bsr)) { + if (pdu_msg.new_subh()) { + pdu_msg.get()->set_bsr(bsr.buff_size, bsr_format_convert(bsr.format)); + bsr_is_inserted = true; + } + } + } + + log_h->debug("Assembled MAC PDU msg size %d/%d bytes\n", pdu_msg.get_pdu_len()-pdu_msg.rem_size(), pdu_sz); + + /* Generate MAC PDU and save to buffer */ + uint8_t *ret = pdu_msg.write_packet(log_h); + + pid_has_bsr[pid%MAX_HARQ_PROC] = bsr_is_inserted; + if (bsr_is_inserted) { + bsr_procedure->set_tx_tti(tx_tti); + } + + pthread_mutex_unlock(&mutex); + + + return ret; +} + +void mux::append_crnti_ce_next_tx(uint16_t crnti) { + pending_crnti_ce = crnti; +} + +bool mux::sched_sdu(lchid_t *ch, int *sdu_space, int max_sdu_sz) +{ + + if (*sdu_space > 0) { + // Get n-th pending SDU pointer and length + int sched_len = ch->buffer_len; + if (sched_len > 0) { // there is pending SDU to allocate + if (sched_len > max_sdu_sz && max_sdu_sz >= 0) { + sched_len = max_sdu_sz; + } + if (sched_len > *sdu_space) { + sched_len = *sdu_space; + } + + log_h->info("SDU: scheduled lcid=%d, rlc_buffer=%d, allocated=%d/%d\n", + ch->id, ch->buffer_len, sched_len, *sdu_space); + + *sdu_space -= sched_len; + ch->buffer_len -= sched_len; + ch->sched_len += sched_len; + return true; + } + } + return false; +} + +bool mux::allocate_sdu(uint32_t lcid, srslte::sch_pdu* pdu_msg, int max_sdu_sz) +{ + + // Get n-th pending SDU pointer and length + int sdu_len = rlc->get_buffer_state(lcid); + + if (sdu_len > 0) { // there is pending SDU to allocate + int buffer_state = sdu_len; + if (sdu_len > max_sdu_sz && max_sdu_sz >= 0) { + sdu_len = max_sdu_sz; + } + int sdu_space = pdu_msg->get_sdu_space(); + if (sdu_len > sdu_space) { + sdu_len = sdu_space; + } + if (sdu_len > MIN_RLC_SDU_LEN) { + if (pdu_msg->new_subh()) { // there is space for a new subheader + int sdu_len2 = sdu_len; + sdu_len = pdu_msg->get()->set_sdu(lcid, sdu_len, rlc); + if (sdu_len > 0) { // new SDU could be added + + Info("SDU: allocated lcid=%d, rlc_buffer=%d, allocated=%d/%d, max_sdu_sz=%d, remaining=%d\n", + lcid, buffer_state, sdu_len, sdu_space, max_sdu_sz, pdu_msg->rem_size()); + return true; + } else { + Warning("SDU: rlc_buffer=%d, allocated=%d/%d, remaining=%d\n", + buffer_state, sdu_len, sdu_space, pdu_msg->rem_size()); + pdu_msg->del_subh(); + } + } + } + } + return false; +} + +void mux::msg3_flush() +{ + if (log_h) { + Debug("Msg3 buffer flushed\n"); + } + msg3_has_been_transmitted = false; + bzero(msg3_buff, sizeof(MSG3_BUFF_SZ)); +} + +bool mux::msg3_is_transmitted() +{ + return msg3_has_been_transmitted; +} + +/* Returns a pointer to the Msg3 buffer */ +uint8_t* mux::msg3_get(uint8_t *payload, uint32_t pdu_sz) +{ + uint8_t* msg3_buff_start_pdu = pdu_get(msg3_buff, pdu_sz, 0, 0); + if (!msg3_buff_start_pdu) { + Error("Moving PDU from Mux unit to Msg3 buffer\n"); + return NULL; + } + memcpy(payload, msg3_buff_start_pdu, sizeof(uint8_t)*pdu_sz); + msg3_has_been_transmitted = true; + return payload; +} + + +} diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc new file mode 100644 index 000000000..1f4c40c31 --- /dev/null +++ b/srsue/src/mac/proc_bsr.cc @@ -0,0 +1,403 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include "mac/proc_bsr.h" +#include "mac/mac.h" +#include "mac/mux.h" + + + namespace srsue { + +bsr_proc::bsr_proc() +{ + initiated = false; + last_print = 0; + next_tx_tti = 0; + triggered_bsr_type=NONE; + +} + +void bsr_proc::init(rlc_interface_mac *rlc_, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_, srslte::timers *timers_db_) +{ + log_h = log_h_; + rlc = rlc_; + mac_cfg = mac_cfg_; + timers_db = timers_db_; + reset(); + initiated = true; +} + +void bsr_proc::reset() +{ + timers_db->get(mac::BSR_TIMER_PERIODIC)->stop(); + timers_db->get(mac::BSR_TIMER_PERIODIC)->reset(); + timers_db->get(mac::BSR_TIMER_RETX)->stop(); + timers_db->get(mac::BSR_TIMER_RETX)->reset(); + + reset_sr = false; + sr_is_sent = false; + triggered_bsr_type = NONE; + for (int i=0;imain.ulsch_cnfg.periodic_bsr_timer]; + if (periodic >= 0) { + triggered_bsr_type = REGULAR; + Info("BSR: Triggering BSR reTX\n"); + sr_is_sent = false; + } + break; + } +} + +// Checks if data is available for a a channel with higher priority than others +bool bsr_proc::check_highest_channel() { + int pending_data_lcid = -1; + + for (int i=0;i= 0) { + if (rlc->get_buffer_state(i) > 0) { + pending_data_lcid = i; + for (int j=0;jget_buffer_state(j) > 0) { + if (priorities[j] > priorities[i]) { + pending_data_lcid = -1; + } + } + } + } + } + } + if (pending_data_lcid >= 0) { + // If there is new data available for this logical channel + uint32_t nbytes = rlc->get_buffer_state(pending_data_lcid); + if (nbytes > last_pending_data[pending_data_lcid]) + { + if (triggered_bsr_type != REGULAR) { + Info("BSR: Triggered REGULAR BSR for Max Priority LCID=%d\n", pending_data_lcid); + } + triggered_bsr_type = REGULAR; + return true; + } + } + return false; +} + +uint32_t bsr_proc::get_buffer_state() { + uint32_t buffer = 0; + for (int i=0;i= 0) { + buffer += rlc->get_buffer_state(i); + } + } + return buffer; +} + +// Checks if only one logical channel has data avaiable for Tx +bool bsr_proc::check_single_channel() { + uint32_t pending_data_lcid = 0; + uint32_t nof_nonzero_lcid = 0; + + for (int i=0;i= 0) { + if (rlc->get_buffer_state(i) > 0) { + pending_data_lcid = i; + nof_nonzero_lcid++; + } + } + } + if (nof_nonzero_lcid == 1) { + uint32_t nbytes = rlc->get_buffer_state(pending_data_lcid); + // If there is new data available for this logical channel + if (nbytes > last_pending_data[pending_data_lcid]) { + triggered_bsr_type = REGULAR; + Info("BSR: Triggered REGULAR BSR for single LCID=%d\n", pending_data_lcid); + return true; + } + } + return false; +} + +void bsr_proc::update_pending_data() { + for (int i=0;iget_buffer_state(i); + } +} + +bool bsr_proc::generate_bsr(bsr_t *bsr, uint32_t nof_padding_bytes) { + bool ret = false; + uint32_t nof_lcg=0; + bzero(bsr, sizeof(bsr_t)); + for (int i=0;i= 0) { + uint32_t n = rlc->get_buffer_state(i); + bsr->buff_size[lcg[i]] += n; + if (n > 0) { + nof_lcg++; + ret = true; + } + } + } + if (triggered_bsr_type == PADDING) { + if (nof_padding_bytes < 4) { + // If space only for short + if (nof_lcg > 1) { + bsr->format = TRUNC_BSR; + uint32_t max_prio_ch = find_max_priority_lcid(); + for (int i=0;i<4;i++) { + if (lcg[max_prio_ch] != i) { + bsr->buff_size[i] = 0; + } + } + } else { + bsr->format = SHORT_BSR; + } + } else { + // If space for long BSR + bsr->format = LONG_BSR; + } + } else { + bsr->format = SHORT_BSR; + if (nof_lcg > 1) { + bsr->format = LONG_BSR; + } + } + return ret; +} + +// Checks if Regular BSR must be assembled, as defined in 5.4.5 +// Padding BSR is assembled when called by mux_unit when UL grant is received +// Periodic BSR is triggered by the expiration of the timers +void bsr_proc::step(uint32_t tti) +{ + if (!initiated) { + return; + } + + int periodic = liblte_rrc_periodic_bsr_timer_num[mac_cfg->main.ulsch_cnfg.periodic_bsr_timer]; + if (periodic != timers_db->get(mac::BSR_TIMER_PERIODIC)->get_timeout() && periodic > 0) + { + timers_db->get(mac::BSR_TIMER_PERIODIC)->set(this, periodic); + timers_db->get(mac::BSR_TIMER_PERIODIC)->run(); + Info("BSR: Configured timer periodic %d ms\n", periodic); + } + int retx = liblte_rrc_retransmission_bsr_timer_num[mac_cfg->main.ulsch_cnfg.retx_bsr_timer]; + if (retx != timers_db->get(mac::BSR_TIMER_RETX)->get_timeout() && retx > 0) + { + timers_db->get(mac::BSR_TIMER_RETX)->set(this, retx); + timers_db->get(mac::BSR_TIMER_RETX)->run(); + Info("BSR: Configured timer reTX %d ms\n", retx); + } + + // Check condition 1 in Sec 5.4.5 + if (triggered_bsr_type == NONE) { + check_single_channel(); + } + // Higher priority channel is reported regardless of a BSR being already triggered + check_highest_channel(); + + update_pending_data(); + + + if ((tti - last_print)%10240 > QUEUE_STATUS_PERIOD_MS) { + char str[128]; + bzero(str, 128); + for (int i=0;iget_buffer_state(i), last_pending_data[i]); + } + Info("BSR: QUEUE status: %s\n", str); + last_print = tti; + } + +} + +char* bsr_proc::bsr_type_tostring(triggered_bsr_type_t type) { + switch(type) { + case bsr_proc::REGULAR: + return (char*) "Regular"; + case bsr_proc::PADDING: + return (char*) "Padding"; + case bsr_proc::PERIODIC: + return (char*) "Periodic"; + default: + return (char*) "Regular"; + } +} + +char* bsr_proc::bsr_format_tostring(bsr_format_t format) { + switch(format) { + case bsr_proc::LONG_BSR: + return (char*) "Long"; + case bsr_proc::SHORT_BSR: + return (char*) "Short"; + case bsr_proc::TRUNC_BSR: + return (char*) "Truncated"; + } +} + +bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) +{ + bool ret = false; + + uint32_t bsr_sz = 0; + if (triggered_bsr_type == PERIODIC || triggered_bsr_type == REGULAR) { + /* Check if grant + MAC SDU headers is enough to accomodate all pending data */ + int total_data = 0; + for (int i=0;iget_buffer_state(i))+rlc->get_buffer_state(i); + } + total_data--; // Because last SDU has no size header + + /* All triggered BSRs shall be cancelled in case the UL grant can accommodate all pending data available for transmission + but is not sufficient to additionally accommodate the BSR MAC control element plus its subheader. + */ + generate_bsr(bsr, 0); + bsr_sz = bsr->format==LONG_BSR?3:1; + if (total_data <= grant_size && total_data + 1 + bsr_sz > grant_size) { + Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + } else { + Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", + grant_size, total_data, bsr_sz); + ret = true; + } + if (timers_db->get(mac::BSR_TIMER_PERIODIC)->get_timeout() && bsr->format != TRUNC_BSR) { + timers_db->get(mac::BSR_TIMER_PERIODIC)->reset(); + timers_db->get(mac::BSR_TIMER_PERIODIC)->run(); + } + } + // Cancel all triggered BSR and SR + triggered_bsr_type = NONE; + reset_sr = true; + // Restart or Start ReTX timer + if (timers_db->get(mac::BSR_TIMER_RETX)->get_timeout()) { + timers_db->get(mac::BSR_TIMER_RETX)->reset(); + timers_db->get(mac::BSR_TIMER_RETX)->run(); + } + return ret; +} + +bool bsr_proc::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t *bsr) +{ + bool ret = false; + + if (triggered_bsr_type != NONE || nof_padding_bytes >= 2) { + + if (triggered_bsr_type == NONE) { + triggered_bsr_type = PADDING; + } + generate_bsr(bsr, nof_padding_bytes); + ret = true; + Info("BSR: Including BSR type %s, format %s, nof_padding_bytes=%d\n", + bsr_type_tostring(triggered_bsr_type), bsr_format_tostring(bsr->format), nof_padding_bytes); + + if (timers_db->get(mac::BSR_TIMER_PERIODIC)->get_timeout() && bsr->format != TRUNC_BSR) { + timers_db->get(mac::BSR_TIMER_PERIODIC)->reset(); + timers_db->get(mac::BSR_TIMER_PERIODIC)->run(); + } + + } + return ret; +} + +void bsr_proc::set_tx_tti(uint32_t tti) { + Debug("BSR: Set next_tx_tti=%d\n", tti); + next_tx_tti = tti; +} + +bool bsr_proc::need_to_reset_sr() { + if (reset_sr) { + reset_sr = false; + sr_is_sent = false; + Debug("BSR: SR reset. sr_is_sent and reset_rs false\n"); + return true; + } else { + return false; + } +} + +bool bsr_proc::need_to_send_sr(uint32_t tti) { + if (!sr_is_sent && triggered_bsr_type == REGULAR) { + if (srslte_tti_interval(tti,next_tx_tti)>0 && srslte_tti_interval(tti,next_tx_tti) < 10240-4) { + reset_sr = false; + sr_is_sent = true; + Info("BSR: Need to send sr: sr_is_sent=true, reset_sr=false, tti=%d, next_tx_tti=%d\n", tti, next_tx_tti); + return true; + } else { + Debug("BSR: Not sending SR because tti=%d, next_tx_tti=%d\n", tti, next_tx_tti); + } + } + return false; +} + +void bsr_proc::setup_lcg(uint32_t lcid, uint32_t new_lcg) +{ + if (lcid < MAX_LCID && new_lcg < 4) { + lcg[lcid] = new_lcg; + } +} + +void bsr_proc::set_priority(uint32_t lcid, uint32_t priority) { + if (lcid < MAX_LCID) { + priorities[lcid] = priority; + } +} + +uint32_t bsr_proc::find_max_priority_lcid() { + uint32_t max_prio = 0, max_idx = 0; + for (int i=0;i max_prio) { + max_prio = priorities[i]; + max_idx = i; + } + } + return max_idx; +} + +} diff --git a/srsue/src/mac/proc_phr.cc b/srsue/src/mac/proc_phr.cc new file mode 100644 index 000000000..c0252f82f --- /dev/null +++ b/srsue/src/mac/proc_phr.cc @@ -0,0 +1,156 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include "mac/proc_phr.h" +#include "mac/mac.h" +#include "mac/mux.h" +#include "common/phy_interface.h" + + + namespace srsue { + +phr_proc::phr_proc() +{ + initiated = false; +} + +void phr_proc::init(phy_interface_mac* phy_h_, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_, srslte::timers *timers_db_) +{ + phy_h = phy_h_; + log_h = log_h_; + mac_cfg = mac_cfg_; + timers_db = timers_db_; + initiated = true; + reset(); +} + +void phr_proc::reset() +{ + phr_is_triggered = false; + timer_periodic = -2; + timer_prohibit = -2; + dl_pathloss_change = -2; +} + +bool phr_proc::pathloss_changed() { + + int min_change = liblte_rrc_dl_pathloss_change_num[mac_cfg->main.phr_cnfg.dl_pathloss_change]; + int cur_pathloss_db = (int) phy_h->get_pathloss_db(); + + if (abs(cur_pathloss_db - last_pathloss_db) > min_change && min_change > 0) { + last_pathloss_db = cur_pathloss_db; + return true; + } else { + return false; + } +} + +/* Trigger PHR when timers exire */ +void phr_proc::timer_expired(uint32_t timer_id) { + switch(timer_id) { + case mac::PHR_TIMER_PERIODIC: + timers_db->get(mac::PHR_TIMER_PERIODIC)->reset(); + timers_db->get(mac::PHR_TIMER_PERIODIC)->run(); + Debug("PHR: Triggered by timer periodic (timer expired).\n"); + phr_is_triggered = true; + break; + case mac::PHR_TIMER_PROHIBIT: + int pathloss_db = liblte_rrc_dl_pathloss_change_num[mac_cfg->main.phr_cnfg.dl_pathloss_change]; + if (pathloss_changed()) { + Info("PHR: Triggered by pathloss difference. cur_pathloss_db=%f (timer expired)\n", last_pathloss_db); + phr_is_triggered = true; + } + break; + } +} + +void phr_proc::step(uint32_t tti) +{ + if (!initiated) { + return; + } + + if (mac_cfg->main.phr_cnfg.setup_present) { + int cfg_timer_periodic = liblte_rrc_periodic_phr_timer_num[mac_cfg->main.phr_cnfg.periodic_phr_timer]; + + // Setup timers and trigger PHR when configuration changed by higher layers + if (timer_periodic != cfg_timer_periodic && cfg_timer_periodic > 0) + { + timer_periodic = cfg_timer_periodic; + timers_db->get(mac::PHR_TIMER_PERIODIC)->set(this, timer_periodic); + timers_db->get(mac::PHR_TIMER_PERIODIC)->run(); + phr_is_triggered = true; + Info("PHR: Configured timer periodic %d ms\n", timer_periodic); + } + + } + + int cfg_timer_prohibit = liblte_rrc_prohibit_phr_timer_num[mac_cfg->main.phr_cnfg.prohibit_phr_timer]; + + if (timer_prohibit != cfg_timer_prohibit && cfg_timer_prohibit > 0) + { + timer_prohibit = cfg_timer_prohibit; + timers_db->get(mac::PHR_TIMER_PROHIBIT)->set(this, timer_prohibit); + timers_db->get(mac::PHR_TIMER_PROHIBIT)->run(); + Info("PHR: Configured timer prohibit %d ms\n", timer_prohibit); + phr_is_triggered = true; + } + if (pathloss_changed() && timers_db->get(mac::PHR_TIMER_PROHIBIT)->is_expired()) + { + Info("PHR: Triggered by pathloss difference. cur_pathloss_db=%f\n", last_pathloss_db); + phr_is_triggered = true; + } +} + +bool phr_proc::generate_phr_on_ul_grant(float *phr) +{ + + if (phr_is_triggered) { + if (phr) { + *phr = phy_h->get_phr(); + } + + Debug("PHR: Generating PHR=%f\n", phr?*phr:0.0); + + timers_db->get(mac::PHR_TIMER_PERIODIC)->reset(); + timers_db->get(mac::PHR_TIMER_PROHIBIT)->reset(); + timers_db->get(mac::PHR_TIMER_PERIODIC)->run(); + timers_db->get(mac::PHR_TIMER_PROHIBIT)->run(); + + phr_is_triggered = false; + + return true; + } else { + return false; + } +} + +} diff --git a/srsue/src/mac/proc_ra.cc b/srsue/src/mac/proc_ra.cc new file mode 100644 index 000000000..88e1a74e5 --- /dev/null +++ b/srsue/src/mac/proc_ra.cc @@ -0,0 +1,565 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include +#include +#include + +#include "mac/proc_ra.h" +#include "mac/mac.h" +#include "mac/mux.h" + +/* Random access procedure as specified in Section 5.1 of 36.321 */ + + +namespace srsue { + +// Table 7.2-1. Backoff Parameter values +uint32_t backoff_table[16] = {0, 10, 20, 30, 40, 60, 80, 120, 160, 240, 320, 480, 960, 960, 960, 960}; + +// Table 7.6-1: DELTA_PREAMBLE values. +int delta_preamble_db_table[5] = {0, 0, -3, -3, 8}; + +void ra_proc::init(phy_interface_mac* phy_h_, + rrc_interface_mac *rrc_, + srslte::log* log_h_, + mac_interface_rrc::ue_rnti_t *rntis_, + mac_interface_rrc::mac_cfg_t *mac_cfg_, + srslte::timers* timers_db_, + mux* mux_unit_, + demux* demux_unit_) +{ + phy_h = phy_h_; + log_h = log_h_; + mac_cfg = mac_cfg_; + rntis = rntis_; + timers_db = timers_db_; + mux_unit = mux_unit_; + demux_unit= demux_unit_; + rrc = rrc_; + srslte_softbuffer_rx_init(&softbuffer_rar, 10); + + // Tell demux to call us when a UE CRID is received + demux_unit->set_uecrid_callback(uecrid_callback, this); + + reset(); +} + +void ra_proc::reset() { + state = IDLE; + msg3_transmitted = false; + started_by_pdcch = false; +} + +void ra_proc::start_pcap(srslte::mac_pcap* pcap_) +{ + pcap = pcap_; +} + +void ra_proc::read_params() { + + // Read initialization parameters + configIndex = mac_cfg->prach_config_index; + preambleIndex = 0; // pass when called from higher layers for non-contention based RA + maskIndex = 0; // same + nof_preambles = liblte_rrc_number_of_ra_preambles_num[mac_cfg->rach.num_ra_preambles]; + if (mac_cfg->rach.preambles_group_a_cnfg.present) { + nof_groupA_preambles = liblte_rrc_size_of_ra_preambles_group_a_num[mac_cfg->rach.preambles_group_a_cnfg.size_of_ra]; + } else { + nof_groupA_preambles = nof_preambles; + } + + if (nof_groupA_preambles > nof_preambles) { + nof_groupA_preambles = nof_preambles; + } + + nof_groupB_preambles = nof_preambles - nof_groupA_preambles; + if (nof_groupB_preambles) { + messagePowerOffsetGroupB= liblte_rrc_message_power_offset_group_b_num[mac_cfg->rach.preambles_group_a_cnfg.msg_pwr_offset_group_b]; + messageSizeGroupA = liblte_rrc_message_size_group_a_num[mac_cfg->rach.preambles_group_a_cnfg.msg_size]; + } + responseWindowSize = liblte_rrc_ra_response_window_size_num[mac_cfg->rach.ra_resp_win_size]; + powerRampingStep = liblte_rrc_power_ramping_step_num[mac_cfg->rach.pwr_ramping_step]; + preambleTransMax = liblte_rrc_preamble_trans_max_num[mac_cfg->rach.preamble_trans_max]; + iniReceivedTargetPower = liblte_rrc_preamble_initial_received_target_power_num[mac_cfg->rach.preamble_init_rx_target_pwr]; + contentionResolutionTimer = liblte_rrc_mac_contention_resolution_timer_num[mac_cfg->rach.mac_con_res_timer]; + + delta_preamble_db = delta_preamble_db_table[configIndex%5]; + + if (contentionResolutionTimer > 0) { + timers_db->get(mac::CONTENTION_TIMER)->set(this, contentionResolutionTimer); + } + +} + +bool ra_proc::in_progress() +{ + return (state > IDLE && state != COMPLETION_DONE); +} + +bool ra_proc::is_successful() { + return state == COMPLETION_DONE; +} + +bool ra_proc::is_response_error() { + return state == RESPONSE_ERROR; +} + +bool ra_proc::is_contention_resolution() { + return state == CONTENTION_RESOLUTION; +} + +bool ra_proc::is_error() { + return state == RA_PROBLEM; +} + +const char* state_str[12] = {"Idle", + "RA: INIT: ", + "RA: Select: ", + "RA: TX: ", + "RA: PDCCH: ", + "RA: Rx: ", + "RA: RxErr: ", + "RA: Backof: ", + "RA: ConRes: ", + "RA: Done: ", + "RA: Done: ", + "RA: Error: "}; + + +#define rError(fmt, ...) Error("%s" fmt, state_str[state], ##__VA_ARGS__) +#define rInfo(fmt, ...) Info("%s" fmt, state_str[state], ##__VA_ARGS__) +#define rDebug(fmt, ...) Debug("%s" fmt, state_str[state], ##__VA_ARGS__) + + +// Process Timing Advance Command as defined in Section 5.2 +void ra_proc::process_timeadv_cmd(uint32_t ta) { + if (preambleIndex == 0) { + // Preamble not selected by UE MAC + phy_h->set_timeadv_rar(ta); + timers_db->get(mac::TIME_ALIGNMENT)->reset(); + timers_db->get(mac::TIME_ALIGNMENT)->run(); + Debug("Applying RAR TA CMD %d\n", ta); + } else { + // Preamble selected by UE MAC + if (!timers_db->get(mac::TIME_ALIGNMENT)->is_running()) { + phy_h->set_timeadv_rar(ta); + timers_db->get(mac::TIME_ALIGNMENT)->run(); + Debug("Applying RAR TA CMD %d\n", ta); + } else { + // Ignore TA CMD + Warning("Ignoring RAR TA CMD because timeAlignmentTimer still running\n"); + } + } +} + +void ra_proc::step_initialization() { + read_params(); + pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; + transmitted_contention_id = 0; + preambleTransmissionCounter = 1; + first_rar_received = true; + mux_unit->msg3_flush(); + msg3_flushed = false; + backoff_param_ms = 0; + + // FIXME: This is because RA in Connected state not working in amarisoft + transmitted_crnti = rntis->crnti; + if(transmitted_crnti) { + state = RESPONSE_ERROR; + } + + // Instruct phy to configure PRACH + phy_h->configure_prach_params(); + state = RESOURCE_SELECTION; +} + +void ra_proc::step_resource_selection() { + ra_group_t sel_group; + + if (preambleIndex > 0) { + // Preamble is chosen by Higher layers (ie Network) + sel_maskIndex = maskIndex; + sel_preamble = (uint32_t) preambleIndex%nof_preambles; + } else { + // Preamble is chosen by MAC UE + if (!msg3_transmitted) { + if (nof_groupB_preambles > 0 && new_ra_msg_len > messageSizeGroupA) { // Check also pathloss (Pcmax,deltaPreamble and powerOffset) + sel_group = RA_GROUP_B; + } else { + sel_group = RA_GROUP_A; + } + last_msg3_group = sel_group; + } else { + sel_group = last_msg3_group; + } + if (sel_group == RA_GROUP_A) { + if (nof_groupA_preambles) { + sel_preamble = preambleTransmissionCounter%nof_groupA_preambles; + } else { + rError("Selected group preamble A but nof_groupA_preambles=0\n"); + state = RA_PROBLEM; + return; + } + } else { + if (nof_groupB_preambles) { + sel_preamble = nof_groupA_preambles + rand()%nof_groupB_preambles; + } else { + rError("Selected group preamble B but nof_groupA_preambles=0\n"); + state = RA_PROBLEM; + return; + } + } + sel_maskIndex = 0; + } + + rDebug("Selected preambleIndex=%d maskIndex=%d GroupA=%d, GroupB=%d\n", + sel_preamble, sel_maskIndex,nof_groupA_preambles, nof_groupB_preambles); + state = PREAMBLE_TRANSMISSION; +} + +void ra_proc::step_preamble_transmission() { + received_target_power_dbm = iniReceivedTargetPower + + delta_preamble_db + + (preambleTransmissionCounter-1)*powerRampingStep; + + rar_received = false; + phy_h->prach_send(sel_preamble, sel_maskIndex - 1, received_target_power_dbm); + state = PDCCH_SETUP; +} + +void ra_proc::step_pdcch_setup() { + int ra_tti = phy_h->prach_tx_tti(); + if (ra_tti > 0) { + ra_rnti = 1+ra_tti%10; + rInfo("seq=%d, ra-rnti=0x%x, ra-tti=%d\n", sel_preamble, ra_rnti, ra_tti); + log_h->console("Random Access Transmission: seq=%d, ra-rnti=0x%x\n", sel_preamble, ra_rnti); + phy_h->pdcch_dl_search(SRSLTE_RNTI_RAR, ra_rnti, ra_tti+3, ra_tti+3+responseWindowSize); + state = RESPONSE_RECEPTION; + } +} + +void ra_proc::new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action) +{ + if (grant.n_bytes < MAX_RAR_PDU_LEN) { + rDebug("DL grant found RA-RNTI=%d\n", ra_rnti); + action->decode_enabled = true; + action->default_ack = false; + action->generate_ack = false; + action->payload_ptr = rar_pdu_buffer; + action->rnti = grant.rnti; + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + action->rv = grant.rv; + action->softbuffer = &softbuffer_rar; + rar_grant_nbytes = grant.n_bytes; + rar_grant_tti = grant.tti; + if (action->rv == 0) { + srslte_softbuffer_rx_reset(&softbuffer_rar); + } + } else { + rError("Received RAR grant exceeds buffer length (%d>%d)\n", grant.n_bytes, MAX_RAR_PDU_LEN); + action->decode_enabled = false; + state = RESPONSE_ERROR; + } +} + +void ra_proc::tb_decoded_ok() { + if (pcap) { + pcap->write_dl_ranti(rar_pdu_buffer, rar_grant_nbytes, ra_rnti, true, rar_grant_tti); + } + + rDebug("RAR decoded successfully TBS=%d\n", rar_grant_nbytes); + + rar_pdu_msg.init_rx(rar_grant_nbytes); + rar_pdu_msg.parse_packet(rar_pdu_buffer); + // Set Backoff parameter + if (rar_pdu_msg.has_backoff()) { + backoff_param_ms = backoff_table[rar_pdu_msg.get_backoff()%16]; + } else { + backoff_param_ms = 0; + } + + current_ta = 0; + + while(rar_pdu_msg.next()) { + if (rar_pdu_msg.get()->get_rapid() == sel_preamble) { + + rar_received = true; + process_timeadv_cmd(rar_pdu_msg.get()->get_ta_cmd()); + + // FIXME: Indicate received target power + //phy_h->set_target_power_rar(iniReceivedTargetPower, (preambleTransmissionCounter-1)*powerRampingStep); + + uint8_t grant[srslte::rar_subh::RAR_GRANT_LEN]; + rar_pdu_msg.get()->get_sched_grant(grant); + + phy_h->pdcch_dl_search_reset(); + + phy_h->set_rar_grant(rar_grant_tti, grant); + + current_ta = rar_pdu_msg.get()->get_ta_cmd(); + + rInfo("RAPID=%d, TA=%d\n", sel_preamble, rar_pdu_msg.get()->get_ta_cmd()); + + if (preambleIndex > 0) { + // Preamble selected by Network + state = COMPLETION; + } else { + // Preamble selected by UE MAC + rntis->temp_rnti = rar_pdu_msg.get()->get_temp_crnti(); + phy_h->pdcch_dl_search(SRSLTE_RNTI_TEMP, rar_pdu_msg.get()->get_temp_crnti()); + + if (first_rar_received) { + first_rar_received = false; + + // Save transmitted C-RNTI (if any) + transmitted_crnti = rntis->crnti; + + // If we have a C-RNTI, tell Mux unit to append C-RNTI CE if no CCCH SDU transmission + if (transmitted_crnti) { + rDebug("Appending C-RNTI MAC CE in next transmission\n"); + mux_unit->append_crnti_ce_next_tx(transmitted_crnti); + phy_h->pdcch_ul_search(SRSLTE_RNTI_USER, transmitted_crnti); + phy_h->pdcch_dl_search(SRSLTE_RNTI_USER, transmitted_crnti); + } + } + rDebug("Going to Contention Resolution state\n"); + state = CONTENTION_RESOLUTION; + + // Start contention resolution timer + timers_db->get(mac::CONTENTION_TIMER)->reset(); + timers_db->get(mac::CONTENTION_TIMER)->run(); + } + } else { + rDebug("Found RAR for preamble %d\n", rar_pdu_msg.get()->get_rapid()); + } + } +} + +void ra_proc::step_response_reception() { + // do nothing. Processing done in tb_decoded_ok() + int ra_tti = phy_h->prach_tx_tti(); + if (ra_tti >= 0 && !rar_received) { + uint32_t interval = srslte_tti_interval(phy_h->get_current_tti(), ra_tti+3+responseWindowSize); + if (interval > 1 && interval < 100) { + rDebug("RA response not received within the response window\n"); + state = RESPONSE_ERROR; + } + } +} + +void ra_proc::step_response_error() { + + preambleTransmissionCounter++; + if (preambleTransmissionCounter >= preambleTransMax + 1) { + rError("Maximum number of transmissions reached (%d)\n", preambleTransMax); + rrc->ra_problem(); + state = RA_PROBLEM; + } else { + backoff_interval_start = phy_h->get_current_tti(); + if (backoff_param_ms) { + backoff_inteval = rand()%backoff_param_ms; + } else { + backoff_inteval = 0; + } + if (backoff_inteval) { + rDebug("Backoff wait interval %d\n", backoff_inteval); + state = BACKOFF_WAIT; + } else { + rDebug("Transmitting inmediatly (%d/%d)\n", preambleTransmissionCounter, preambleTransMax); + state = RESOURCE_SELECTION; + } + } +} + +void ra_proc::step_backoff_wait() { + if (srslte_tti_interval(phy_h->get_current_tti(), backoff_interval_start) >= backoff_inteval) { + state = RESOURCE_SELECTION; + } +} + +bool ra_proc::uecrid_callback(void *arg, uint64_t uecri) { + return ((ra_proc*) arg)->contention_resolution_id_received(uecri); +} + +// Random Access initiated by RRC by the transmission of CCCH SDU +bool ra_proc::contention_resolution_id_received(uint64_t rx_contention_id) { + bool uecri_successful = false; + + rDebug("MAC PDU Contains Contention Resolution ID CE\n"); + + // MAC PDU successfully decoded and contains MAC CE contention Id + timers_db->get(mac::CONTENTION_TIMER)->stop(); + + if (transmitted_contention_id == rx_contention_id) + { + // UE Contention Resolution ID included in MAC CE matches the CCCH SDU transmitted in Msg3 + rntis->crnti = rntis->temp_rnti; + // finish the disassembly and demultiplexing of the MAC PDU + uecri_successful = true; + state = COMPLETION; + } else { + rInfo("Transmitted UE Contention Id differs from received Contention ID (0x%lx != 0x%lx)\n", + transmitted_contention_id, rx_contention_id); + // Discard MAC PDU + uecri_successful = false; + + // Contention Resolution not successfully is like RAR not successful + // FIXME: Need to flush Msg3 HARQ buffer. Why? + state = RESPONSE_ERROR; + } + rntis->temp_rnti = 0; + + return uecri_successful; +} + +void ra_proc::step_contention_resolution() { + // If Msg3 has been sent + if (mux_unit->msg3_is_transmitted()) + { + msg3_transmitted = true; + if (transmitted_crnti) + { + // Random Access with transmission of MAC C-RNTI CE + if ((!started_by_pdcch && pdcch_to_crnti_received == PDCCH_CRNTI_UL_GRANT) || + started_by_pdcch && pdcch_to_crnti_received != PDCCH_CRNTI_NOT_RECEIVED) + { + rDebug("PDCCH for C-RNTI received\n"); + timers_db->get(mac::CONTENTION_TIMER)->stop(); + rntis->temp_rnti = 0; + state = COMPLETION; + } + pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; + } else { + // RA with transmission of CCCH SDU is resolved in contention_resolution_id_received() callback function + if (!transmitted_contention_id) { + // Save transmitted UE contention id, as defined by higher layers + transmitted_contention_id = rntis->contention_id; + rntis->contention_id = 0; + } + } + } else { + rDebug("Msg3 not yet transmitted\n"); + } + +} + +void ra_proc::step_completition() { + log_h->console("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); + rInfo("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); + if (!msg3_flushed) { + mux_unit->msg3_flush(); + msg3_flushed = true; + } + msg3_transmitted = false; + state = COMPLETION_DONE; +} + +void ra_proc::step(uint32_t tti_) +{ + switch(state) { + case IDLE: + break; + case INITIALIZATION: + step_initialization(); + break; + case RESOURCE_SELECTION: + step_resource_selection(); + break; + case PREAMBLE_TRANSMISSION: + step_preamble_transmission(); + break; + case PDCCH_SETUP: + step_pdcch_setup(); + break; + case RESPONSE_RECEPTION: + step_response_reception(); + break; + case RESPONSE_ERROR: + step_response_error(); + break; + case BACKOFF_WAIT: + step_backoff_wait(); + break; + case CONTENTION_RESOLUTION: + step_contention_resolution(); + break; + case COMPLETION: + step_completition(); + case COMPLETION_DONE: + break; + } +} + +void ra_proc::start_mac_order(uint32_t msg_len_bits) +{ + if (state == IDLE || state == COMPLETION_DONE || state == RA_PROBLEM) { + started_by_pdcch = false; + new_ra_msg_len = msg_len_bits; + state = INITIALIZATION; + rInfo("Starting PRACH by MAC order\n"); + } +} + +void ra_proc::start_pdcch_order() +{ + if (state == IDLE || state == COMPLETION_DONE || state == RA_PROBLEM) { + started_by_pdcch = true; + state = INITIALIZATION; + rInfo("Starting PRACH by PDCCH order\n"); + } +} + +// Contention Resolution Timer is expired (Section 5.1.5) +void ra_proc::timer_expired(uint32_t timer_id) +{ + rInfo("Contention Resolution Timer expired. Stopping PDCCH Search and going to Response Error\n"); + rntis->temp_rnti = 0; + state = RESPONSE_ERROR; + phy_h->pdcch_dl_search_reset(); +} + +void ra_proc::pdcch_to_crnti(bool contains_uplink_grant) { + rDebug("PDCCH to C-RNTI received %s UL grant\n", contains_uplink_grant?"with":"without"); + if (contains_uplink_grant) { + pdcch_to_crnti_received = PDCCH_CRNTI_UL_GRANT; + } else if (pdcch_to_crnti_received == PDCCH_CRNTI_NOT_RECEIVED) { + pdcch_to_crnti_received = PDCCH_CRNTI_DL_GRANT; + } +} + +void ra_proc::harq_retx() +{ + timers_db->get(mac::CONTENTION_TIMER)->reset(); +} + +} + diff --git a/srsue/src/mac/proc_sr.cc b/srsue/src/mac/proc_sr.cc new file mode 100644 index 000000000..0aa7fb77e --- /dev/null +++ b/srsue/src/mac/proc_sr.cc @@ -0,0 +1,129 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include "mac/proc_sr.h" + + +namespace srsue { + +sr_proc::sr_proc() { + initiated = false; +} + +void sr_proc::init(phy_interface_mac* phy_h_, rrc_interface_mac *rrc_, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_) +{ + log_h = log_h_; + rrc = rrc_; + mac_cfg = mac_cfg_; + phy_h = phy_h_; + initiated = true; + do_ra = false; +} + +void sr_proc::reset() +{ + is_pending_sr = false; +} + +bool sr_proc::need_tx(uint32_t tti) +{ + int last_tx_tti = phy_h->sr_last_tx_tti(); + if (last_tx_tti >= 0) { + if (tti > last_tx_tti) { + if (tti - last_tx_tti > 8) { + return true; + } + } else { + uint32_t interval = 10240-last_tx_tti+tti; + if (interval > 8 && tti < 8) { + return true; + } + } + } + return false; +} + +void sr_proc::step(uint32_t tti) +{ + if (initiated) { + if (is_pending_sr) { + if (mac_cfg->sr.setup_present) { + if (sr_counter < dsr_transmax) { + if (sr_counter == 0 || need_tx(tti)) { + sr_counter++; + Info("SR: Signalling PHY sr_counter=%d\n", sr_counter); + phy_h->sr_send(); + } + } else { + if (need_tx(tti)) { + Info("SR: Releasing PUCCH/SRS resources, sr_counter=%d, dsr_transmax=%d\n", + sr_counter, dsr_transmax); + log_h->console("Scheduling request failed: releasing RRC connection...\n"); + rrc->release_pucch_srs(); + do_ra = true; + is_pending_sr = false; + } + } + } else { + Info("SR: PUCCH not configured. Starting RA procedure\n"); + do_ra = true; + reset(); + } + } + } +} + +bool sr_proc::need_random_access() { + if (initiated) { + if (do_ra) { + do_ra = false; + return true; + } else { + return false; + } + } + return false; +} + +void sr_proc::start() +{ + if (initiated) { + if (!is_pending_sr) { + sr_counter = 0; + is_pending_sr = true; + } + dsr_transmax = liblte_rrc_dsr_trans_max_num[mac_cfg->sr.dsr_trans_max]; + Debug("SR: Starting Procedure. dsrTransMax=%d\n", dsr_transmax); + } +} + +} + diff --git a/srsue/src/mac/ul_harq.cc b/srsue/src/mac/ul_harq.cc new file mode 100644 index 000000000..43cafef65 --- /dev/null +++ b/srsue/src/mac/ul_harq.cc @@ -0,0 +1,394 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include "common/log.h" +#include "mac/mac.h" +#include "mac/ul_harq.h" + + + namespace srsue { + + /*********************************************************** + * + * HARQ ENTITY + * + *********************************************************/ + +bool ul_harq_entity::init(srslte::log *log_h_, + mac_interface_rrc::ue_rnti_t *rntis_, + mac_interface_rrc::mac_cfg_t *mac_cfg_, + srslte::timers *timers_db_, + mux *mux_unit_) { + log_h = log_h_; + mux_unit = mux_unit_; + mac_cfg = mac_cfg_; + rntis = rntis_; + timers_db = timers_db_; + for (uint32_t i=0;ilog_h; + pid = pid_; + payload_buffer = (uint8_t*) srslte_vec_malloc(payload_buffer_len*sizeof(uint8_t)); + if (!payload_buffer) { + Error("Allocating memory\n"); + return false; + } + pdu_ptr = payload_buffer; + return true; + } +} + +void ul_harq_entity::ul_harq_process::run_tti(uint32_t tti_tx, mac_interface_phy::mac_grant_t* grant, mac_interface_phy::tb_action_ul_t* action) +{ + + + int max_retx; + if (is_msg3) { + max_retx = harq_entity->mac_cfg->rach.max_harq_msg3_tx; + } else { + max_retx = liblte_rrc_max_harq_tx_num[harq_entity->mac_cfg->main.ulsch_cnfg.max_harq_tx]; + } + + + // Receive and route HARQ feedbacks + if (grant) { + if ((!grant->rnti_type == SRSLTE_RNTI_TEMP && grant->ndi != get_ndi()) || + (grant->rnti_type == SRSLTE_RNTI_USER && !has_grant()) || + grant->is_from_rar) + { + // New transmission + + // Uplink grant in a RAR + if (grant->is_from_rar) { + Debug("Getting Msg3 buffer payload, grant size=%d bytes\n", grant->n_bytes); + pdu_ptr = harq_entity->mux_unit->msg3_get(payload_buffer, grant->n_bytes); + if (pdu_ptr) { + generate_new_tx(tti_tx, true, grant, action); + } else { + Warning("UL RAR grant available but no Msg3 on buffer\n"); + } + + // Normal UL grant + } else { + // Request a MAC PDU from the Multiplexing & Assemble Unit + pdu_ptr = harq_entity->mux_unit->pdu_get(payload_buffer, grant->n_bytes, tti_tx, pid); + if (pdu_ptr) { + generate_new_tx(tti_tx, false, grant, action); + } else { + Warning("Uplink grant but no MAC PDU in Multiplex Unit buffer\n"); + } + } + } else { + // Adaptive Re-TX + if (current_tx_nb >= max_retx) { + Info("UL %d: Maximum number of ReTX reached (%d). Discarting TB.\n", pid, max_retx); + reset(); + action->expect_ack = false; + } else { + generate_retx(tti_tx, grant, action); + } + } + } else if (has_grant()) { + // Non-Adaptive Re-Tx + if (current_tx_nb >= max_retx) { + Info("UL %d: Maximum number of ReTX reached (%d). Discarting TB.\n", pid, max_retx); + reset(); + action->expect_ack = false; + } else { + generate_retx(tti_tx, action); + } + } + if (harq_entity->pcap && grant) { + if (grant->is_from_rar) { + grant->rnti = harq_entity->rntis->temp_rnti; + } + harq_entity->pcap->write_ul_crnti(pdu_ptr, grant->n_bytes, grant->rnti, get_nof_retx(), tti_tx); + } + + + +} + +int ul_harq_entity::ul_harq_process::get_current_tbs() +{ + return cur_grant.n_bytes*8; +} + +void ul_harq_entity::ul_harq_process::generate_retx(uint32_t tti_tx, mac_interface_phy::tb_action_ul_t *action) +{ + generate_retx(tti_tx, NULL, action); +} + +// Retransmission with or w/o grant (Section 5.4.2.2) +void ul_harq_entity::ul_harq_process::generate_retx(uint32_t tti_tx, mac_interface_phy::mac_grant_t *grant, + mac_interface_phy::tb_action_ul_t *action) +{ + if (grant) { + // HARQ entity requests an adaptive transmission + if (grant->rv) { + current_irv = irv_of_rv[grant->rv%4]; + } + memcpy(&cur_grant, grant, sizeof(mac_interface_phy::mac_grant_t)); + harq_feedback = false; + Info("UL %d: Adaptive retx=%d, RV=%d, TBS=%d\n", + pid, current_tx_nb, get_rv(), grant->n_bytes); + generate_tx(tti_tx, action); + } else { + Info("UL %d: Non-Adaptive retx=%d, RV=%d, TBS=%d\n", + pid, current_tx_nb, get_rv(), cur_grant.n_bytes); + // HARQ entity requests a non-adaptive transmission + if (!harq_feedback) { + generate_tx(tti_tx, action); + } + } + + // On every Msg3 retransmission, restart mac-ContentionResolutionTimer as defined in Section 5.1.5 + if (is_msg3) { + harq_entity->timers_db->get(mac::CONTENTION_TIMER)->reset(); + } + + harq_entity->mux_unit->pusch_retx(tti_tx, pid); +} + +// New transmission (Section 5.4.2.2) +void ul_harq_entity::ul_harq_process::generate_new_tx(uint32_t tti_tx, bool is_msg3_, + mac_interface_phy::mac_grant_t *grant, + mac_interface_phy::tb_action_ul_t *action) +{ + if (grant) { + + // Compute average number of retransmissions per packet considering previous packet + harq_entity->average_retx = SRSLTE_VEC_CMA((float) current_tx_nb, harq_entity->average_retx, harq_entity->nof_pkts++); + + + memcpy(&cur_grant, grant, sizeof(mac_interface_phy::mac_grant_t)); + harq_feedback = false; + is_grant_configured = true; + current_tx_nb = 0; + current_irv = 0; + is_msg3 = is_msg3_; + Info("UL %d: New TX%s, RV=%d, TBS=%d, RNTI=%d\n", + pid, is_msg3?" for Msg3":"", get_rv(), cur_grant.n_bytes, cur_grant.rnti); + generate_tx(tti_tx, action); + } +} + +// Transmission of pending frame (Section 5.4.2.2) +void ul_harq_entity::ul_harq_process::generate_tx(uint32_t tti_tx, mac_interface_phy::tb_action_ul_t *action) +{ + action->current_tx_nb = current_tx_nb; + current_tx_nb++; + action->expect_ack = true; + action->rnti = is_msg3?harq_entity->rntis->temp_rnti:cur_grant.rnti; + action->rv = cur_grant.rv>0?cur_grant.rv:get_rv(); + action->softbuffer = &softbuffer; + action->tx_enabled = true; + action->payload_ptr = pdu_ptr; + memcpy(&action->phy_grant, &cur_grant.phy_grant, sizeof(srslte_phy_grant_t)); + + current_irv = (current_irv+1)%4; + tti_last_tx = tti_tx; +} + +bool ul_harq_entity::ul_harq_process::is_sps() +{ + return false; +} + +uint32_t ul_harq_entity::ul_harq_process::last_tx_tti() +{ + return tti_last_tx; +} + +uint32_t ul_harq_entity::ul_harq_process::get_nof_retx() +{ + return current_tx_nb; +} + +} diff --git a/srsue/src/main.cc b/srsue/src/main.cc new file mode 100644 index 000000000..51d14054e --- /dev/null +++ b/srsue/src/main.cc @@ -0,0 +1,379 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ue.h" +#include "metrics_stdout.h" +#include "srslte/version.h" + +using namespace std; +using namespace srsue; +namespace bpo = boost::program_options; + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +string config_file; + +void parse_args(all_args_t *args, int argc, char* argv[]) { + + // Command line only options + bpo::options_description general("General options"); + general.add_options() + ("help,h", "Produce help message") + ("version,v", "Print version information and exit") + ; + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + ("rf.dl_freq", bpo::value(&args->rf.dl_freq)->default_value(2680000000), "Downlink centre frequency") + ("rf.ul_freq", bpo::value(&args->rf.ul_freq)->default_value(2560000000), "Uplink centre frequency") + ("rf.rx_gain", bpo::value(&args->rf.rx_gain)->default_value(-1), "Front-end receiver gain") + ("rf.tx_gain", bpo::value(&args->rf.tx_gain)->default_value(-1), "Front-end transmitter gain") + ("rf.nof_rx_ant", bpo::value(&args->rf.nof_rx_ant)->default_value(1), "Number of RX antennas") + + ("rf.device_name", bpo::value(&args->rf.device_name)->default_value("auto"), "Front-end device name") + ("rf.device_args", bpo::value(&args->rf.device_args)->default_value("auto"), "Front-end device arguments") + ("rf.time_adv_nsamples", bpo::value(&args->rf.time_adv_nsamples)->default_value("auto"), "Transmission time advance") + ("rf.burst_preamble_us", bpo::value(&args->rf.burst_preamble)->default_value("auto"), "Transmission time advance") + + ("pcap.enable", bpo::value(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") + ("pcap.filename", bpo::value(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename") + + ("trace.enable", bpo::value(&args->trace.enable)->default_value(false), "Enable PHY and radio timing traces") + ("trace.phy_filename",bpo::value(&args->trace.phy_filename)->default_value("ue.phy_trace"), "PHY timing traces filename") + ("trace.radio_filename",bpo::value(&args->trace.radio_filename)->default_value("ue.radio_trace"), "Radio timing traces filename") + + ("gui.enable", bpo::value(&args->gui.enable)->default_value(false), "Enable GUI plots") + + ("log.phy_level", bpo::value(&args->log.phy_level), "PHY log level") + ("log.phy_hex_limit", bpo::value(&args->log.phy_hex_limit), "PHY log hex dump limit") + ("log.mac_level", bpo::value(&args->log.mac_level), "MAC log level") + ("log.mac_hex_limit", bpo::value(&args->log.mac_hex_limit), "MAC log hex dump limit") + ("log.rlc_level", bpo::value(&args->log.rlc_level), "RLC log level") + ("log.rlc_hex_limit", bpo::value(&args->log.rlc_hex_limit), "RLC log hex dump limit") + ("log.pdcp_level", bpo::value(&args->log.pdcp_level), "PDCP log level") + ("log.pdcp_hex_limit",bpo::value(&args->log.pdcp_hex_limit), "PDCP log hex dump limit") + ("log.rrc_level", bpo::value(&args->log.rrc_level), "RRC log level") + ("log.rrc_hex_limit", bpo::value(&args->log.rrc_hex_limit), "RRC log hex dump limit") + ("log.gw_level", bpo::value(&args->log.gw_level), "GW log level") + ("log.gw_hex_limit", bpo::value(&args->log.gw_hex_limit), "GW log hex dump limit") + ("log.nas_level", bpo::value(&args->log.nas_level), "NAS log level") + ("log.nas_hex_limit", bpo::value(&args->log.nas_hex_limit), "NAS log hex dump limit") + ("log.usim_level", bpo::value(&args->log.usim_level), "USIM log level") + ("log.usim_hex_limit",bpo::value(&args->log.usim_hex_limit), "USIM log hex dump limit") + + ("log.all_level", bpo::value(&args->log.all_level)->default_value("info"), "ALL log level") + ("log.all_hex_limit", bpo::value(&args->log.all_hex_limit)->default_value(32), "ALL log hex dump limit") + + ("log.filename", bpo::value(&args->log.filename)->default_value("/tmp/ue.log"),"Log filename") + + ("usim.algo", bpo::value(&args->usim.algo), "USIM authentication algorithm") + ("usim.op", bpo::value(&args->usim.op), "USIM operator variant") + ("usim.amf", bpo::value(&args->usim.amf), "USIM authentication management field") + ("usim.imsi", bpo::value(&args->usim.imsi), "USIM IMSI") + ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") + ("usim.k", bpo::value(&args->usim.k), "USIM K") + + + /* Expert section */ + ("expert.phy.worker_cpu_mask", + bpo::value(&args->expert.phy.worker_cpu_mask)->default_value(254), + "cpu bit mask (eg 255 = 1111 1111)") + + ("expert.phy.sync_cpu_affinity", + bpo::value(&args->expert.phy.sync_cpu_affinity)->default_value(2), + "index of the core used by the sync thread") + + ("expert.ue_category", + bpo::value(&args->expert.ue_cateogry)->default_value(4), + "UE Category (1 to 5)") + + ("expert.metrics_period_secs", + bpo::value(&args->expert.metrics_period_secs)->default_value(1.0), + "Periodicity for metrics in seconds") + + ("expert.pregenerate_signals", + bpo::value(&args->expert.pregenerate_signals)->default_value(false), + "Pregenerate uplink signals after attach. Improves CPU performance.") + + ("expert.rssi_sensor_enabled", + bpo::value(&args->expert.phy.rssi_sensor_enabled)->default_value(true), + "Enable or disable RF frontend RSSI sensor. In some USRP devices can cause segmentation fault") + + ("expert.prach_gain", + bpo::value(&args->expert.phy.prach_gain)->default_value(-1.0), + "Disable PRACH power control") + + ("expert.cqi_max", + bpo::value(&args->expert.phy.cqi_max)->default_value(15), + "Upper bound on the maximum CQI to be reported. Default 15.") + + ("expert.cqi_fixed", + bpo::value(&args->expert.phy.cqi_fixed)->default_value(-1), + "Fixes the reported CQI to a constant value. Default disabled.") + + ("expert.snr_ema_coeff", + bpo::value(&args->expert.phy.snr_ema_coeff)->default_value(0.1), + "Sets the SNR exponential moving average coefficient (Default 0.1)") + + ("expert.snr_estim_alg", + bpo::value(&args->expert.phy.snr_estim_alg)->default_value("refs"), + "Sets the noise estimation algorithm. (Default refs)") + + ("expert.pdsch_max_its", + bpo::value(&args->expert.phy.pdsch_max_its)->default_value(4), + "Maximum number of turbo decoder iterations") + + ("expert.attach_enable_64qam", + bpo::value(&args->expert.phy.attach_enable_64qam)->default_value(false), + "PUSCH 64QAM modulation before attachment") + + ("expert.nof_phy_threads", + bpo::value(&args->expert.phy.nof_phy_threads)->default_value(2), + "Number of PHY threads") + + ("expert.equalizer_mode", + bpo::value(&args->expert.phy.equalizer_mode)->default_value("mmse"), + "Equalizer mode") + + ("expert.cfo_integer_enabled", + bpo::value(&args->expert.phy.cfo_integer_enabled)->default_value(false), + "Enables integer CFO estimation and correction.") + + ("expert.cfo_correct_tol_hz", + bpo::value(&args->expert.phy.cfo_correct_tol_hz)->default_value(50.0), + "Tolerance (in Hz) for digial CFO compensation.") + + ("expert.time_correct_period", + bpo::value(&args->expert.phy.time_correct_period)->default_value(5), + "Period for sampling time offset correction.") + + ("expert.sfo_correct_disable", + bpo::value(&args->expert.phy.sfo_correct_disable)->default_value(false), + "Disables phase correction before channel estimation.") + + ("expert.sss_algorithm", + bpo::value(&args->expert.phy.sss_algorithm)->default_value("full"), + "Selects the SSS estimation algorithm.") + + ("expert.estimator_fil_w", + bpo::value(&args->expert.phy.estimator_fil_w)->default_value(0.1), + "Chooses the coefficients for the 3-tap channel estimator centered filter.") + + + ("rf_calibration.tx_corr_dc_gain", bpo::value(&args->rf_cal.tx_corr_dc_gain)->default_value(0.0), "TX DC offset gain correction") + ("rf_calibration.tx_corr_dc_phase", bpo::value(&args->rf_cal.tx_corr_dc_phase)->default_value(0.0), "TX DC offset phase correction") + ("rf_calibration.tx_corr_iq_i", bpo::value(&args->rf_cal.tx_corr_iq_i)->default_value(0.0), "TX IQ imbalance inphase correction") + ("rf_calibration.tx_corr_iq_q", bpo::value(&args->rf_cal.tx_corr_iq_q)->default_value(0.0), "TX IQ imbalance quadrature correction") + + ; + + // Positional options - config file location + bpo::options_description position("Positional options"); + position.add_options() + ("config_file", bpo::value< string >(&config_file), "UE configuration file") + ; + bpo::positional_options_description p; + p.add("config_file", -1); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help")) { + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + cout << common << endl << general << endl; + exit(0); + } + + // print version number and exit + if (vm.count("version")) { + cout << "Version " << + srslte_get_version_major() << "." << + srslte_get_version_minor() << "." << + srslte_get_version_patch() << endl; + exit(0); + } + + // no config file given - print usage and exit + if (!vm.count("config_file")) { + cout << "Error: Configuration file not provided" << endl; + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + exit(0); + } else { + cout << "Reading configuration file " << config_file << "..." << endl; + ifstream conf(config_file.c_str(), ios::in); + if(conf.fail()) { + cout << "Failed to read configuration file " << config_file << " - exiting" << endl; + exit(1); + } + bpo::store(bpo::parse_config_file(conf, common), vm); + bpo::notify(vm); + } + + // Apply all_level to any unset layers + if (vm.count("log.all_level")) { + if(!vm.count("log.phy_level")) { + args->log.phy_level = args->log.all_level; + } + if(!vm.count("log.mac_level")) { + args->log.mac_level = args->log.all_level; + } + if(!vm.count("log.rlc_level")) { + args->log.rlc_level = args->log.all_level; + } + if(!vm.count("log.pdcp_level")) { + args->log.pdcp_level = args->log.all_level; + } + if(!vm.count("log.rrc_level")) { + args->log.rrc_level = args->log.all_level; + } + if(!vm.count("log.nas_level")) { + args->log.nas_level = args->log.all_level; + } + if(!vm.count("log.gw_level")) { + args->log.gw_level = args->log.all_level; + } + if(!vm.count("log.usim_level")) { + args->log.usim_level = args->log.all_level; + } + } + + // Apply all_hex_limit to any unset layers + if (vm.count("log.all_hex_limit")) { + if(!vm.count("log.phy_hex_limit")) { + args->log.phy_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.mac_hex_limit")) { + args->log.mac_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.rlc_hex_limit")) { + args->log.rlc_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.pdcp_hex_limit")) { + args->log.pdcp_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.rrc_hex_limit")) { + args->log.rrc_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.nas_hex_limit")) { + args->log.nas_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.gw_hex_limit")) { + args->log.gw_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.usim_hex_limit")) { + args->log.usim_hex_limit = args->log.all_hex_limit; + } + } +} + +static bool running = true; +static bool do_metrics = false; + +void sig_int_handler(int signo) +{ + running = false; +} + +void *input_loop(void *m) +{ + metrics_stdout *metrics = (metrics_stdout*)m; + char key; + while(running) { + cin >> key; + if('t' == key) { + do_metrics = !do_metrics; + if(do_metrics) { + cout << "Enter t to stop trace." << endl; + } else { + cout << "Enter t to restart trace." << endl; + } + metrics->toggle_print(do_metrics); + } + } + return NULL; +} + +int main(int argc, char *argv[]) +{ + signal(SIGINT, sig_int_handler); + all_args_t args; + metrics_stdout metrics; + ue *ue = ue::get_instance(); + + cout << "--- Software Radio Systems LTE UE ---" << endl << endl; + + parse_args(&args, argc, argv); + if(!ue->init(&args)) { + exit(1); + } + metrics.init(ue, args.expert.metrics_period_secs); + + pthread_t input; + pthread_create(&input, NULL, &input_loop, &metrics); + + bool plot_started = false; + bool signals_pregenerated = false; + while(running) { + if (ue->is_attached()) { + if (!signals_pregenerated && args.expert.pregenerate_signals) { + ue->pregenerate_signals(true); + signals_pregenerated = true; + } + if (!plot_started && args.gui.enable) { + ue->start_plot(); + plot_started = true; + } + } + sleep(1); + } + pthread_cancel(input); + metrics.stop(); + ue->stop(); + ue->cleanup(); + cout << "--- exiting ---" << endl; + exit(0); +} diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc new file mode 100644 index 000000000..0a86683d2 --- /dev/null +++ b/srsue/src/metrics_stdout.cc @@ -0,0 +1,180 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2015 The srsUE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "metrics_stdout.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +namespace srsue{ + +char const * const prefixes[2][9] = +{ + { "", "m", "u", "n", "p", "f", "a", "z", "y", }, + { "", "k", "M", "G", "T", "P", "E", "Z", "Y", }, +}; + +metrics_stdout::metrics_stdout() + :started(false) + ,do_print(false) + ,n_reports(10) +{ +} + +bool metrics_stdout::init(ue_metrics_interface *u, float report_period_secs) +{ + ue_ = u; + metrics_report_period = report_period_secs; + + started = true; + pthread_create(&metrics_thread, NULL, &metrics_thread_start, this); + return true; +} + +void metrics_stdout::stop() +{ + if(started) + { + started = false; + pthread_join(metrics_thread, NULL); + } +} + +void metrics_stdout::toggle_print(bool b) +{ + do_print = b; +} + +void* metrics_stdout::metrics_thread_start(void *m_) +{ + metrics_stdout *m = (metrics_stdout*)m_; + m->metrics_thread_run(); + return NULL; +} + +void metrics_stdout::metrics_thread_run() +{ + while(started) + { + usleep(metrics_report_period*1e6); + if(ue_->get_metrics(metrics)) { + print_metrics(); + } else { + print_disconnect(); + } + } +} + +void metrics_stdout::print_metrics() +{ + if(!do_print) + return; + + if(++n_reports > 10) + { + n_reports = 0; + cout << endl; + cout << "--Signal--------------DL------------------------------UL----------------------" << endl; + cout << " rsrp pl cfo mcs snr turbo brate bler mcs buff brate bler" << endl; + } + cout << float_to_string(metrics.phy.dl.rsrp, 2); + cout << float_to_string(metrics.phy.dl.pathloss, 2); + cout << float_to_eng_string(metrics.phy.sync.cfo, 2); + cout << float_to_string(metrics.phy.dl.mcs, 2); + cout << float_to_string(metrics.phy.dl.sinr, 2); + cout << float_to_string(metrics.phy.dl.turbo_iters, 2); + cout << float_to_eng_string((float) metrics.mac.rx_brate/metrics_report_period, 2); + if (metrics.mac.rx_pkts > 0) { + cout << float_to_string((float) 100*metrics.mac.rx_errors/metrics.mac.rx_pkts, 1) << "%"; + } else { + cout << float_to_string(0, 2) << "%"; + } + cout << float_to_string(metrics.phy.ul.mcs, 2); + cout << float_to_eng_string((float) metrics.mac.ul_buffer, 2); + cout << float_to_eng_string((float) metrics.mac.tx_brate/metrics_report_period, 2); + if (metrics.mac.tx_pkts > 0) { + cout << float_to_string((float) 100*metrics.mac.tx_errors/metrics.mac.tx_pkts, 1) << "%"; + } else { + cout << float_to_string(0, 2) << "%"; + } + cout << endl; + + if(metrics.rf.rf_error) { + printf("RF status: O=%d, U=%d, L=%d\n", metrics.rf.rf_o, metrics.rf.rf_u, metrics.rf.rf_l); + } + +} + +void metrics_stdout::print_disconnect() +{ + if(do_print) { + cout << "--- disconnected ---" << endl; + } +} + +std::string metrics_stdout::float_to_string(float f, int digits) +{ + std::ostringstream os; + const int precision = (f == 0.0) ? digits-1 : digits - log10(fabs(f))-2*DBL_EPSILON; + os << std::setw(6) << std::fixed << std::setprecision(precision) << f; + return os.str(); +} + +std::string metrics_stdout::float_to_eng_string(float f, int digits) +{ + const int degree = (f == 0.0) ? 0 : lrint( floor( log10( fabs( f ) ) / 3) ); + + std::string factor; + + if ( abs( degree ) < 9 ) + { + if(degree < 0) + factor = prefixes[0][ abs( degree ) ]; + else + factor = prefixes[1][ abs( degree ) ]; + } else { + return "failed"; + } + + const double scaled = f * pow( 1000.0, -degree ); + if (degree != 0) { + return float_to_string(scaled, digits) + factor; + } else { + return " " + float_to_string(scaled, digits) + factor; + } +} + +} // namespace srsue diff --git a/srsue/src/phy/CMakeLists.txt b/srsue/src/phy/CMakeLists.txt new file mode 100644 index 000000000..85ac4a985 --- /dev/null +++ b/srsue/src/phy/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright 2015 Software Radio Systems Limited +# +# This file is part of srsUE +# +# srsUE 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. +# +# srsUE 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. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsue_phy ${SOURCES}) +target_link_libraries(srsue_phy ${SRSLTE_PHY_LIBRARY}) + +if(ENABLE_GUI AND SRSGUI_FOUND) + target_link_libraries(srsue_phy ${SRSGUI_LIBRARIES}) +endif(ENABLE_GUI AND SRSGUI_FOUND) diff --git a/srsue/src/phy/phch_common.cc b/srsue/src/phy/phch_common.cc new file mode 100644 index 000000000..0fe112ec4 --- /dev/null +++ b/srsue/src/phy/phch_common.cc @@ -0,0 +1,334 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include "srslte/srslte.h" +#include "phy/phch_common.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#define TX_MODE_CONTINUOUS 0 + +namespace srsue { + +cf_t zeros[50000]; + +phch_common::phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_) +{ + config = NULL; + args = NULL; + log_h = NULL; + radio_h = NULL; + mac = NULL; + max_mutex = max_mutex_; + nof_mutex = 0; + sr_enabled = false; + is_first_of_burst = true; + is_first_tx = true; + rar_grant_pending = false; + pathloss = 0; + cur_pathloss = 0; + cur_pusch_power = 0; + p0_preamble = 0; + cur_radio_power = 0; + rx_gain_offset = 0; + sr_last_tx_tti = -1; + cur_pusch_power = 0; + bzero(zeros, 50000*sizeof(cf_t)); + + bzero(&dl_metrics, sizeof(dl_metrics_t)); + dl_metrics_read = true; + dl_metrics_count = 0; + bzero(&ul_metrics, sizeof(ul_metrics_t)); + ul_metrics_read = true; + ul_metrics_count = 0; + bzero(&sync_metrics, sizeof(sync_metrics_t)); + sync_metrics_read = true; + sync_metrics_count = 0; +} + +void phch_common::init(phy_interface_rrc::phy_cfg_t *_config, phy_args_t *_args, srslte::log *_log, srslte::radio *_radio, mac_interface_phy *_mac) +{ + log_h = _log; + radio_h = _radio; + mac = _mac; + config = _config; + args = _args; + is_first_tx = true; + sr_last_tx_tti = -1; + + for (int i=0;i= ul_rnti_start && ul_rnti_start >= 0 || ul_rnti_start < 0) && + (tti < ul_rnti_end && ul_rnti_end >= 0 || ul_rnti_end < 0)) + { + return true; + } else { + return false; + } +} + +bool phch_common::dl_rnti_active(uint32_t tti) { + Debug("tti=%d, dl_rnti_start=%d, dl_rnti_end=%d, dl_rnti=0x%x\n", tti, dl_rnti_start, dl_rnti_end, dl_rnti); + if (((tti >= dl_rnti_start && dl_rnti_start >= 0) || dl_rnti_start < 0) && + ((tti < dl_rnti_end && dl_rnti_end >= 0) || dl_rnti_end < 0)) + { + bool ret = true; + // FIXME: This scheduling decision belongs to RRC + if (dl_rnti_type == SRSLTE_RNTI_SI) { + if (dl_rnti_end - dl_rnti_start > 1) { // This is not a SIB1 + if ((tti/10)%2 == 0 && (tti%10) == 5) { // Skip subframe #5 for which SFN mod 2 = 0 + ret = false; + } + } + } + return ret; + } else { + return false; + } +} + +srslte::radio* phch_common::get_radio() +{ + return radio_h; +} + +// Unpack RAR grant as defined in Section 6.2 of 36.213 +void phch_common::set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]) +{ + srslte_dci_rar_grant_unpack(&rar_grant, grant_payload); + rar_grant_pending = true; + // PUSCH is at n+6 or n+7 and phch_worker assumes default delay of 4 ttis + if (rar_grant.ul_delay) { + rar_grant_tti = (tti + 3) % 10240; + } else { + rar_grant_tti = (tti + 2) % 10240; + } +} + +bool phch_common::get_pending_rar(uint32_t tti, srslte_dci_rar_grant_t *rar_grant_) +{ + if (rar_grant_pending && tti >= rar_grant_tti) { + if (rar_grant_) { + rar_grant_pending = false; + memcpy(rar_grant_, &rar_grant, sizeof(srslte_dci_rar_grant_t)); + } + return true; + } + return false; +} + +/* Common variables used by all phy workers */ +uint16_t phch_common::get_ul_rnti(uint32_t tti) { + if (ul_rnti_active(tti)) { + return ul_rnti; + } else { + return 0; + } +} +srslte_rnti_type_t phch_common::get_ul_rnti_type() { + return ul_rnti_type; +} +void phch_common::set_ul_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start, int tti_end) { + ul_rnti = rnti_value; + ul_rnti_type = type; + ul_rnti_start = tti_start; + ul_rnti_end = tti_end; +} +uint16_t phch_common::get_dl_rnti(uint32_t tti) { + if (dl_rnti_active(tti)) { + return dl_rnti; + } else { + return 0; + } +} +srslte_rnti_type_t phch_common::get_dl_rnti_type() { + return dl_rnti_type; +} +void phch_common::set_dl_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start, int tti_end) { + dl_rnti = rnti_value; + dl_rnti_type = type; + dl_rnti_start = tti_start; + dl_rnti_end = tti_end; + Debug("Set DL rnti: start=%d, end=%d, value=0x%x\n", tti_start, tti_end, rnti_value); +} + +void phch_common::reset_pending_ack(uint32_t tti) { + pending_ack[tti%10].enabled = false; +} + +void phch_common::set_pending_ack(uint32_t tti, uint32_t I_lowest, uint32_t n_dmrs) { + pending_ack[tti%10].enabled = true; + pending_ack[tti%10].I_lowest = I_lowest; + pending_ack[tti%10].n_dmrs = n_dmrs; + Debug("Set pending ACK for tti=%d I_lowest=%d, n_dmrs=%d\n", tti, I_lowest, n_dmrs); +} + +bool phch_common::get_pending_ack(uint32_t tti) { + return get_pending_ack(tti, NULL, NULL); +} + +bool phch_common::get_pending_ack(uint32_t tti, uint32_t *I_lowest, uint32_t *n_dmrs) { + if (I_lowest) { + *I_lowest = pending_ack[tti%10].I_lowest; + } + if (n_dmrs) { + *n_dmrs = pending_ack[tti%10].n_dmrs; + } + return pending_ack[tti%10].enabled; +} + +/* The transmisison of UL subframes must be in sequence. Each worker uses this function to indicate + * that all processing is done and data is ready for transmission or there is no transmission at all (tx_enable). + * In that case, the end of burst message will be send to the radio + */ +void phch_common::worker_end(uint32_t tti, bool tx_enable, + cf_t *buffer, uint32_t nof_samples, + srslte_timestamp_t tx_time) +{ + + // Wait previous TTIs to be transmitted + if (is_first_tx) { + is_first_tx = false; + } else { + pthread_mutex_lock(&tx_mutex[tti%nof_mutex]); + } + + radio_h->set_tti(tti); + if (tx_enable) { + radio_h->tx(buffer, nof_samples, tx_time); + is_first_of_burst = false; + } else { + if (TX_MODE_CONTINUOUS) { + if (!is_first_of_burst) { + radio_h->tx(zeros, nof_samples, tx_time); + } + } else { + if (!is_first_of_burst) { + radio_h->tx_end(); + is_first_of_burst = true; + } + } + } + // Trigger next transmission + pthread_mutex_unlock(&tx_mutex[(tti+1)%nof_mutex]); + + // Trigger MAC clock + mac->tti_clock(tti); + +} + + +void phch_common::set_cell(const srslte_cell_t &c) { + cell = c; +} + +uint32_t phch_common::get_nof_prb() { + return cell.nof_prb; +} + +void phch_common::set_dl_metrics(const dl_metrics_t &m) { + if(dl_metrics_read) { + dl_metrics = m; + dl_metrics_count = 1; + dl_metrics_read = false; + } else { + dl_metrics_count++; + dl_metrics.mcs = dl_metrics.mcs + (m.mcs - dl_metrics.mcs)/dl_metrics_count; + dl_metrics.n = dl_metrics.n + (m.n - dl_metrics.n)/dl_metrics_count; + dl_metrics.rsrp = dl_metrics.rsrp + (m.rsrp - dl_metrics.rsrp)/dl_metrics_count; + dl_metrics.rsrq = dl_metrics.rsrq + (m.rsrq - dl_metrics.rsrq)/dl_metrics_count; + dl_metrics.rssi = dl_metrics.rssi + (m.rssi - dl_metrics.rssi)/dl_metrics_count; + dl_metrics.sinr = dl_metrics.sinr + (m.sinr - dl_metrics.sinr)/dl_metrics_count; + dl_metrics.pathloss = dl_metrics.pathloss + (m.pathloss - dl_metrics.pathloss)/dl_metrics_count; + dl_metrics.turbo_iters = dl_metrics.turbo_iters + (m.turbo_iters - dl_metrics.turbo_iters)/dl_metrics_count; + } +} + +void phch_common::get_dl_metrics(dl_metrics_t &m) { + m = dl_metrics; + dl_metrics_read = true; +} + +void phch_common::set_ul_metrics(const ul_metrics_t &m) { + if(ul_metrics_read) { + ul_metrics = m; + ul_metrics_count = 1; + ul_metrics_read = false; + } else { + ul_metrics_count++; + ul_metrics.mcs = ul_metrics.mcs + (m.mcs - ul_metrics.mcs)/ul_metrics_count; + ul_metrics.power = ul_metrics.power + (m.power - ul_metrics.power)/ul_metrics_count; + } +} + +void phch_common::get_ul_metrics(ul_metrics_t &m) { + m = ul_metrics; + ul_metrics_read = true; +} + +void phch_common::set_sync_metrics(const sync_metrics_t &m) { + + if(sync_metrics_read) { + sync_metrics = m; + sync_metrics_count = 1; + sync_metrics_read = false; + } else { + sync_metrics_count++; + sync_metrics.cfo = sync_metrics.cfo + (m.cfo - sync_metrics.cfo)/sync_metrics_count; + sync_metrics.sfo = sync_metrics.sfo + (m.sfo - sync_metrics.sfo)/sync_metrics_count; + } +} + +void phch_common::get_sync_metrics(sync_metrics_t &m) { + m = sync_metrics; + sync_metrics_read = true; +} + +void phch_common::reset_ul() +{ + is_first_tx = true; + is_first_of_burst = true; + for (int i=0;i +#include "srslte/srslte.h" +#include "common/log.h" +#include "phy/phch_worker.h" +#include "phy/phch_common.h" +#include "phy/phch_recv.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +namespace srsue { + + +phch_recv::phch_recv() { + running = false; +} + +void phch_recv::init(srslte::radio_multi* _radio_handler, mac_interface_phy *_mac, rrc_interface_phy *_rrc, + prach* _prach_buffer, srslte::thread_pool* _workers_pool, + phch_common* _worker_com, srslte::log* _log_h, uint32_t nof_rx_antennas_, uint32_t prio, int sync_cpu_affinity) +{ + radio_h = _radio_handler; + log_h = _log_h; + mac = _mac; + rrc = _rrc; + workers_pool = _workers_pool; + worker_com = _worker_com; + prach_buffer = _prach_buffer; + nof_rx_antennas = nof_rx_antennas_; + + tx_mutex_cnt = 0; + running = true; + phy_state = IDLE; + time_adv_sec = 0; + cell_is_set = false; + sync_sfn_cnt = 0; + + for (int i=0;iget_nof_workers(); + worker_com->set_nof_mutex(nof_tx_mutex); + if(sync_cpu_affinity < 0){ + start(prio); + } else { + start_cpu(prio, sync_cpu_affinity); + } + + +} + +void phch_recv::stop() { + running = false; + wait_thread_finish(); + for (int i=0;irx_now(data, nsamples, rx_time)) { + int offset = nsamples-radio_h->get_tti_len(); + if (abs(offset)<10 && offset != 0) { + radio_h->tx_offset(offset); + } else if (nsamples<10) { + radio_h->tx_offset(nsamples); + } + return nsamples; + } else { + return -1; + } +} + +double callback_set_rx_gain(void *h, double gain) { + srslte::radio_multi *radio_handler = (srslte::radio_multi*) h; + return radio_handler->set_rx_gain_th(gain); +} + +void phch_recv::set_time_adv_sec(float _time_adv_sec) { + time_adv_sec = _time_adv_sec; +} + +void phch_recv::set_ue_sync_opts(srslte_ue_sync_t *q) { + if (worker_com->args->cfo_integer_enabled) { + srslte_ue_sync_cfo_i_detec_en(q, true); + } + + float cfo_tol = worker_com->args->cfo_correct_tol_hz; + srslte_cfo_set_tol(&q->strack.cfocorr, cfo_tol/(15000*q->fft_size)); + srslte_cfo_set_tol(&q->sfind.cfocorr, cfo_tol/(15000*q->fft_size)); + + int time_correct_period = worker_com->args->time_correct_period; + if (time_correct_period > 0) { + srslte_ue_sync_set_sample_offset_correct_period(q, time_correct_period); + } + + sss_alg_t sss_alg = SSS_FULL; + if (!worker_com->args->sss_algorithm.compare("diff")) { + sss_alg = SSS_DIFF; + } else if (!worker_com->args->sss_algorithm.compare("partial")) { + sss_alg = SSS_PARTIAL_3; + } else if (!worker_com->args->sss_algorithm.compare("full")){ + sss_alg = SSS_FULL; + } else { + Warning("Invalid SSS algorithm %s. Using 'full'\n", worker_com->args->sss_algorithm.c_str()); + } + srslte_sync_set_sss_algorithm(&q->strack, (sss_alg_t) sss_alg); + srslte_sync_set_sss_algorithm(&q->sfind, (sss_alg_t) sss_alg); +} + +bool phch_recv::init_cell() { + cell_is_set = false; + if (!srslte_ue_mib_init(&ue_mib, cell)) + { + if (!srslte_ue_sync_init_multi(&ue_sync, cell, radio_recv_wrapper_cs, nof_rx_antennas, radio_h)) + { + + // Set options defined in expert section + set_ue_sync_opts(&ue_sync); + + for (int i=0;iget_nof_workers();i++) { + if (!((phch_worker*) workers_pool->get_worker(i))->init_cell(cell)) { + Error("Error setting cell: initiating PHCH worker\n"); + return false; + } + } + radio_h->set_tti_len(SRSLTE_SF_LEN_PRB(cell.nof_prb)); + if (do_agc) { + srslte_ue_sync_start_agc(&ue_sync, callback_set_rx_gain, last_gain); + } + srslte_ue_sync_set_cfo(&ue_sync, cellsearch_cfo); + cell_is_set = true; + } else { + Error("Error setting cell: initiating ue_sync"); + } + } else { + Error("Error setting cell: initiating ue_mib\n"); + } + return cell_is_set; +} + +void phch_recv::free_cell() +{ + if (cell_is_set) { + for (int i=0;iget_nof_workers();i++) { + ((phch_worker*) workers_pool->get_worker(i))->free_cell(); + } + prach_buffer->free_cell(); + } +} + + +bool phch_recv::cell_search(int force_N_id_2) +{ + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; + uint8_t bch_payload_bits[SRSLTE_BCH_PAYLOAD_LEN/8]; + + srslte_ue_cellsearch_result_t found_cells[3]; + srslte_ue_cellsearch_t cs; + + bzero(found_cells, 3*sizeof(srslte_ue_cellsearch_result_t)); + + log_h->console("Searching for cell...\n"); + if (srslte_ue_cellsearch_init_multi(&cs, SRSLTE_DEFAULT_MAX_FRAMES_PSS, radio_recv_wrapper_cs, nof_rx_antennas, radio_h)) { + Error("Initiating UE cell search\n"); + return false; + } + + srslte_ue_cellsearch_set_nof_valid_frames(&cs, SRSLTE_DEFAULT_NOF_VALID_PSS_FRAMES); + + // Set options defined in expert section + set_ue_sync_opts(&cs.ue_sync); + + if (do_agc) { + srslte_ue_sync_start_agc(&cs.ue_sync, callback_set_rx_gain, last_gain); + } + + radio_h->set_rx_srate(1.92e6); + radio_h->start_rx(); + + /* Find a cell in the given N_id_2 or go through the 3 of them to find the strongest */ + uint32_t max_peak_cell = 0; + int ret = SRSLTE_ERROR; + + if (force_N_id_2 >= 0 && force_N_id_2 < 3) { + ret = srslte_ue_cellsearch_scan_N_id_2(&cs, force_N_id_2, &found_cells[force_N_id_2]); + max_peak_cell = force_N_id_2; + } else { + ret = srslte_ue_cellsearch_scan(&cs, found_cells, &max_peak_cell); + } + + last_gain = srslte_agc_get_gain(&cs.ue_sync.agc); + + radio_h->stop_rx(); + srslte_ue_cellsearch_free(&cs); + + if (ret < 0) { + Error("Error decoding MIB: Error searching PSS\n"); + return false; + } else if (ret == 0) { + Error("Error decoding MIB: Could not find any PSS in this frequency\n"); + return false; + } + + // Save result + cell.id = found_cells[max_peak_cell].cell_id; + cell.cp = found_cells[max_peak_cell].cp; + cellsearch_cfo = found_cells[max_peak_cell].cfo; + + log_h->console("Found CELL ID: %d CP: %s, CFO: %.1f KHz.\nTrying to decode MIB...\n", + cell.id, srslte_cp_string(cell.cp), cellsearch_cfo/1000); + + srslte_ue_mib_sync_t ue_mib_sync; + + if (srslte_ue_mib_sync_init_multi(&ue_mib_sync, cell.id, cell.cp, radio_recv_wrapper_cs, nof_rx_antennas, radio_h)) { + Error("Initiating UE MIB synchronization\n"); + return false; + } + + // Set options defined in expert section + set_ue_sync_opts(&ue_mib_sync.ue_sync); + + if (do_agc) { + srslte_ue_sync_start_agc(&ue_mib_sync.ue_sync, callback_set_rx_gain, last_gain); + } + + srslte_ue_sync_set_cfo(&ue_mib_sync.ue_sync, cellsearch_cfo); + + /* Find and decode MIB */ + uint32_t sfn; + int sfn_offset; + radio_h->start_rx(); + ret = srslte_ue_mib_sync_decode(&ue_mib_sync, + SRSLTE_DEFAULT_MAX_FRAMES_PBCH, + bch_payload, &cell.nof_ports, &sfn_offset); + radio_h->stop_rx(); + last_gain = srslte_agc_get_gain(&ue_mib_sync.ue_sync.agc); + cellsearch_cfo = srslte_ue_sync_get_cfo(&ue_mib_sync.ue_sync); + srslte_ue_mib_sync_free(&ue_mib_sync); + + if (ret == 1) { + srslte_pbch_mib_unpack(bch_payload, &cell, NULL); + worker_com->set_cell(cell); + srslte_cell_fprint(stdout, &cell, 0); + + srslte_bit_pack_vector(bch_payload, bch_payload_bits, SRSLTE_BCH_PAYLOAD_LEN); + mac->bch_decoded_ok(bch_payload_bits, SRSLTE_BCH_PAYLOAD_LEN/8); + return true; + } else { + Warning("Error decoding MIB: Error decoding PBCH\n"); + return false; + } +} + + +int phch_recv::sync_sfn(void) { + + int ret = SRSLTE_ERROR; + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; + + srslte_ue_sync_decode_sss_on_track(&ue_sync, true); + ret = srslte_ue_sync_zerocopy_multi(&ue_sync, sf_buffer_sfn); + if (ret < 0) { + Error("Error calling ue_sync_get_buffer"); + return -1; + } + + if (ret == 1) { + if (srslte_ue_sync_get_sfidx(&ue_sync) == 0) { + int sfn_offset=0; + Info("SYNC: Decoding MIB...\n"); + int n = srslte_ue_mib_decode(&ue_mib, sf_buffer_sfn[0], bch_payload, NULL, &sfn_offset); + if (n < 0) { + Error("Error decoding MIB while synchronising SFN"); + return -1; + } else if (n == SRSLTE_UE_MIB_FOUND) { + uint32_t sfn; + srslte_pbch_mib_unpack(bch_payload, &cell, &sfn); + + sfn = (sfn + sfn_offset)%1024; + tti = sfn*10; + + srslte_ue_sync_decode_sss_on_track(&ue_sync, true); + Info("SYNC: DONE, TTI=%d, sfn_offset=%d\n", tti, sfn_offset); + srslte_ue_mib_reset(&ue_mib); + return 1; + } + } + } else { + Debug("SYNC: PSS/SSS not found...\n"); + } + return 0; +} + +void phch_recv::resync_sfn() { + sync_sfn_cnt = 0; + phy_state = SYNCING; +} + +void phch_recv::run_thread() +{ + int sync_res; + phch_worker *worker = NULL; + cf_t *buffer[SRSLTE_MAX_PORTS]; + while(running) { + switch(phy_state) { + case CELL_SEARCH: + if (cell_search()) { + log_h->console("Initializating cell configuration...\n"); + init_cell(); + float srate = (float) srslte_sampling_freq_hz(cell.nof_prb); + + if (30720%((int) srate/1000) == 0) { + radio_h->set_master_clock_rate(30.72e6); + } else { + radio_h->set_master_clock_rate(23.04e6); + } + + log_h->console("Setting Sampling frequency %.2f MHz\n", (float) srate/1000000); + radio_h->set_rx_srate(srate); + radio_h->set_tx_srate(srate); + + ul_dl_factor = radio_h->get_tx_freq()/radio_h->get_rx_freq(); + + Info("SYNC: Cell found. Synchronizing...\n"); + phy_state = SYNCING; + sync_sfn_cnt = 0; + srslte_ue_mib_reset(&ue_mib); + } + break; + case SYNCING: + + srslte_ue_sync_decode_sss_on_track(&ue_sync, true); + + if (!radio_is_streaming) { + // Start streaming + radio_h->start_rx(); + radio_is_streaming = true; + } + + switch(sync_sfn()) { + default: + log_h->console("Going IDLE\n"); + phy_state = IDLE; + break; + case 1: + srslte_ue_sync_set_agc_period(&ue_sync, 20); + phy_state = SYNC_DONE; + break; + case 0: + break; + } + sync_sfn_cnt++; + if (sync_sfn_cnt >= SYNC_SFN_TIMEOUT) { + sync_sfn_cnt = 0; + radio_h->stop_rx(); + radio_is_streaming = false; + log_h->console("Timeout while synchronizing SFN\n"); + log_h->warning("Timeout while synchronizing SFN\n"); + } + break; + case SYNC_DONE: + tti = (tti+1)%10240; + worker = (phch_worker*) workers_pool->wait_worker(tti); + sync_res = 0; + if (worker) { + for (int i=0;iget_buffer(i); + } + + sync_res = srslte_ue_sync_zerocopy_multi(&ue_sync, buffer); + if (sync_res == 1) { + + log_h->step(tti); + + Debug("Worker %d synchronized\n", worker->get_id()); + + metrics.sfo = srslte_ue_sync_get_sfo(&ue_sync); + metrics.cfo = srslte_ue_sync_get_cfo(&ue_sync); + worker->set_cfo(ul_dl_factor*metrics.cfo/15000); + worker_com->set_sync_metrics(metrics); + + float sample_offset = (float) srslte_ue_sync_get_sfo(&ue_sync)/1000; + worker->set_sample_offset(sample_offset); + + /* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */ + srslte_timestamp_t rx_time, tx_time, tx_time_prach; + srslte_ue_sync_get_last_timestamp(&ue_sync, &rx_time); + srslte_timestamp_copy(&tx_time, &rx_time); + srslte_timestamp_add(&tx_time, 0, 4e-3 - time_adv_sec); + worker->set_tx_time(tx_time); + + Debug("Settting TTI=%d, tx_mutex=%d to worker %d\n", tti, tx_mutex_cnt, worker->get_id()); + worker->set_tti(tti, tx_mutex_cnt); + tx_mutex_cnt = (tx_mutex_cnt+1)%nof_tx_mutex; + + // Check if we need to TX a PRACH + if (prach_buffer->is_ready_to_send(tti)) { + srslte_timestamp_copy(&tx_time_prach, &rx_time); + srslte_timestamp_add(&tx_time_prach, 0, prach::tx_advance_sf*1e-3); + prach_buffer->send(radio_h, ul_dl_factor*metrics.cfo/15000, worker_com->pathloss, tx_time_prach); + radio_h->tx_end(); + worker_com->p0_preamble = prach_buffer->get_p0_preamble(); + worker_com->cur_radio_power = SRSLTE_MIN(SRSLTE_PC_MAX, worker_com->pathloss + worker_com->p0_preamble); + } + workers_pool->start_worker(worker); + // Notify RRC in-sync every 1 frame + if ((tti%10) == 0) { + rrc->in_sync(); + log_h->debug("Sending in-sync to RRC\n"); + } + } else { + log_h->console("Sync error.\n"); + log_h->error("Sync error. Sending out-of-sync to RRC\n"); + // Notify RRC of out-of-sync frame + rrc->out_of_sync(); + worker->release(); + worker_com->reset_ul(); + phy_state = SYNCING; + } + } else { + // wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here + running = false; + } + break; + case IDLE: + usleep(1000); + break; + } + } +} + +uint32_t phch_recv::get_current_tti() +{ + return tti; +} + +bool phch_recv::status_is_sync() +{ + return phy_state == SYNC_DONE; +} + +void phch_recv::get_current_cell(srslte_cell_t* cell_) +{ + if (cell_) { + memcpy(cell_, &cell, sizeof(srslte_cell_t)); + } +} + +void phch_recv::sync_start() +{ + radio_h->set_master_clock_rate(30.72e6); + phy_state = CELL_SEARCH; +} + +void phch_recv::sync_stop() +{ + free_cell(); + radio_h->stop_rx(); + radio_is_streaming = false; + phy_state = IDLE; +} + +} diff --git a/srsue/src/phy/phch_worker.cc b/srsue/src/phy/phch_worker.cc new file mode 100644 index 000000000..e0ed424f0 --- /dev/null +++ b/srsue/src/phy/phch_worker.cc @@ -0,0 +1,1168 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include "phy/phch_worker.h" +#include "common/mac_interface.h" +#include "common/phy_interface.h" +#include "asn1/liblte_rrc.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) phy->log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) phy->log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) phy->log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) phy->log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + + +/* This is to visualize the channel response */ +#ifdef ENABLE_GUI +#include "srsgui/srsgui.h" +#include +void init_plots(srsue::phch_worker *worker); +pthread_t plot_thread; +sem_t plot_sem; +static int plot_worker_id = -1; +#else +#warning Compiling without srsGUI support +#endif +/*********************************************/ + + +namespace srsue { + + +phch_worker::phch_worker() : tr_exec(10240) +{ + phy = NULL; + bzero(signal_buffer, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + + cell_initiated = false; + pregen_enabled = false; + trace_enabled = false; + + reset(); +} + +void phch_worker::reset() +{ + bzero(&dl_metrics, sizeof(dl_metrics_t)); + bzero(&ul_metrics, sizeof(ul_metrics_t)); + bzero(&dmrs_cfg, sizeof(srslte_refsignal_dmrs_pusch_cfg_t)); + bzero(&pusch_hopping, sizeof(srslte_pusch_hopping_cfg_t)); + bzero(&uci_cfg, sizeof(srslte_uci_cfg_t)); + bzero(&pucch_cfg, sizeof(srslte_pucch_cfg_t)); + bzero(&pucch_sched, sizeof(srslte_pucch_sched_t)); + bzero(&srs_cfg, sizeof(srslte_refsignal_srs_cfg_t)); + bzero(&period_cqi, sizeof(srslte_cqi_periodic_cfg_t)); + I_sr = 0; + rnti_is_set = false; + rar_cqi_request = false; + cfi = 0; +} + +void phch_worker::set_common(phch_common* phy_) +{ + phy = phy_; +} + +bool phch_worker::init_cell(srslte_cell_t cell_) +{ + memcpy(&cell, &cell_, sizeof(srslte_cell_t)); + + // ue_sync in phy.cc requires a buffer for 3 subframes + for (int i=0;iargs->nof_rx_ant;i++) { + signal_buffer[i] = (cf_t*) srslte_vec_malloc(3 * sizeof(cf_t) * SRSLTE_SF_LEN_PRB(cell.nof_prb)); + if (!signal_buffer[i]) { + Error("Allocating memory\n"); + return false; + } + } + + if (srslte_ue_dl_init_multi(&ue_dl, cell, phy->args->nof_rx_ant)) { + Error("Initiating UE DL\n"); + return false; + } + + if (srslte_ue_ul_init(&ue_ul, cell)) { + Error("Initiating UE UL\n"); + return false; + } + srslte_ue_ul_set_normalization(&ue_ul, true); + srslte_ue_ul_set_cfo_enable(&ue_ul, true); + + cell_initiated = true; + + return true; +} + +void phch_worker::free_cell() +{ + if (cell_initiated) { + for (int i=0;iargs->nof_rx_ant;i++) { + if (signal_buffer[i]) { + free(signal_buffer[i]); + } + } + srslte_ue_dl_free(&ue_dl); + srslte_ue_ul_free(&ue_ul); + } +} + +cf_t* phch_worker::get_buffer(uint32_t antenna_idx) +{ + return signal_buffer[antenna_idx]; +} + +void phch_worker::set_tti(uint32_t tti_, uint32_t tx_tti_) +{ + tti = tti_; + tx_tti = tx_tti_; +} + +void phch_worker::set_cfo(float cfo_) +{ + cfo = cfo_; +} + +void phch_worker::set_sample_offset(float sample_offset) +{ + if (phy->args->sfo_correct_disable) { + sample_offset = 0; + } + srslte_ue_dl_set_sample_offset(&ue_dl, sample_offset); +} + +void phch_worker::set_crnti(uint16_t rnti) +{ + srslte_ue_dl_set_rnti(&ue_dl, rnti); + srslte_ue_ul_set_rnti(&ue_ul, rnti); + rnti_is_set = true; +} + +void phch_worker::work_imp() +{ + if (!cell_initiated) { + return; + } + + Debug("TTI %d running\n", tti); + +#ifdef LOG_EXECTIME + gettimeofday(&logtime_start[1], NULL); +#endif + + tr_log_start(); + + reset_uci(); + + bool dl_grant_available = false; + bool ul_grant_available = false; + bool dl_ack = false; + + mac_interface_phy::mac_grant_t dl_mac_grant; + mac_interface_phy::tb_action_dl_t dl_action; + bzero(&dl_action, sizeof(mac_interface_phy::tb_action_dl_t)); + + mac_interface_phy::mac_grant_t ul_mac_grant; + mac_interface_phy::tb_action_ul_t ul_action; + bzero(&ul_action, sizeof(mac_interface_phy::tb_action_ul_t)); + + /* Do FFT and extract PDCCH LLR, or quit if no actions are required in this subframe */ + if (extract_fft_and_pdcch_llr()) { + + + /***** Downlink Processing *******/ + + /* PDCCH DL + PDSCH */ + dl_grant_available = decode_pdcch_dl(&dl_mac_grant); + if(dl_grant_available) { + /* Send grant to MAC and get action for this TB */ + phy->mac->new_grant_dl(dl_mac_grant, &dl_action); + + /* Decode PDSCH if instructed to do so */ + dl_ack = dl_action.default_ack; + if (dl_action.decode_enabled) { + dl_ack = decode_pdsch(&dl_action.phy_grant.dl, dl_action.payload_ptr, + dl_action.softbuffer, dl_action.rv, dl_action.rnti, + dl_mac_grant.pid); + } + if (dl_action.generate_ack_callback && dl_action.decode_enabled) { + phy->mac->tb_decoded(dl_ack, dl_mac_grant.rnti_type, dl_mac_grant.pid); + dl_ack = dl_action.generate_ack_callback(dl_action.generate_ack_callback_arg); + Debug("Calling generate ACK callback returned=%d\n", dl_ack); + } + Debug("dl_ack=%d, generate_ack=%d\n", dl_ack, dl_action.generate_ack); + if (dl_action.generate_ack) { + set_uci_ack(dl_ack); + } + } + } + + // Decode PHICH + bool ul_ack; + bool ul_ack_available = decode_phich(&ul_ack); + + /***** Uplink Processing + Transmission *******/ + + /* Generate SR if required*/ + set_uci_sr(); + + /* Check if we have UL grant. ul_phy_grant will be overwritten by new grant */ + ul_grant_available = decode_pdcch_ul(&ul_mac_grant); + + /* Generate CQI reports if required, note that in case both aperiodic + and periodic ones present, only aperiodic is sent (36.213 section 7.2) */ + if (ul_grant_available && ul_mac_grant.has_cqi_request) { + set_uci_aperiodic_cqi(); + } else { + set_uci_periodic_cqi(); + } + + /* Send UL grant or HARQ information (from PHICH) to MAC */ + if (ul_grant_available && ul_ack_available) { + phy->mac->new_grant_ul_ack(ul_mac_grant, ul_ack, &ul_action); + } else if (ul_grant_available && !ul_ack_available) { + phy->mac->new_grant_ul(ul_mac_grant, &ul_action); + } else if (!ul_grant_available && ul_ack_available) { + phy->mac->harq_recv(tti, ul_ack, &ul_action); + } + + /* Set UL CFO before transmission */ + srslte_ue_ul_set_cfo(&ue_ul, cfo); + + /* Transmit PUSCH, PUCCH or SRS */ + bool signal_ready = false; + if (ul_action.tx_enabled) { + encode_pusch(&ul_action.phy_grant.ul, ul_action.payload_ptr, ul_action.current_tx_nb, + ul_action.softbuffer, ul_action.rv, ul_action.rnti, ul_mac_grant.is_from_rar); + signal_ready = true; + if (ul_action.expect_ack) { + phy->set_pending_ack(tti + 8, ue_ul.pusch_cfg.grant.n_prb_tilde[0], ul_action.phy_grant.ul.ncs_dmrs); + } + + } else if (dl_action.generate_ack || uci_data.scheduling_request || uci_data.uci_cqi_len > 0) { + encode_pucch(); + signal_ready = true; + } else if (srs_is_ready_to_send()) { + encode_srs(); + signal_ready = true; + } + + tr_log_end(); + + phy->worker_end(tx_tti, signal_ready, signal_buffer[0], SRSLTE_SF_LEN_PRB(cell.nof_prb), tx_time); + + if (dl_action.decode_enabled && !dl_action.generate_ack_callback) { + if (dl_mac_grant.rnti_type == SRSLTE_RNTI_PCH) { + phy->mac->pch_decoded_ok(dl_mac_grant.n_bytes); + } else { + phy->mac->tb_decoded(dl_ack, dl_mac_grant.rnti_type, dl_mac_grant.pid); + } + } + + update_measurements(); + + /* Tell the plotting thread to draw the plots */ +#ifdef ENABLE_GUI + if (get_id() == plot_worker_id) { + sem_post(&plot_sem); + } +#endif +} + + +bool phch_worker::extract_fft_and_pdcch_llr() { + bool decode_pdcch = false; + if (phy->get_ul_rnti(tti) || phy->get_dl_rnti(tti) || phy->get_pending_rar(tti)) { + decode_pdcch = true; + } + + /* Without a grant, we might need to do fft processing if need to decode PHICH */ + if (phy->get_pending_ack(tti) || decode_pdcch) { + + // Setup estimator filter + float w_coeff = phy->args->estimator_fil_w; + if (w_coeff > 0.0) { + srslte_chest_dl_set_smooth_filter3_coeff(&ue_dl.chest, w_coeff); + } else if (w_coeff == 0.0) { + srslte_chest_dl_set_smooth_filter(&ue_dl.chest, NULL, 0); + } + + if (!phy->args->snr_estim_alg.compare("refs")) { + srslte_chest_dl_set_noise_alg(&ue_dl.chest, SRSLTE_NOISE_ALG_REFS); + } else if (!phy->args->snr_estim_alg.compare("empty")) { + srslte_chest_dl_set_noise_alg(&ue_dl.chest, SRSLTE_NOISE_ALG_EMPTY); + } else { + srslte_chest_dl_set_noise_alg(&ue_dl.chest, SRSLTE_NOISE_ALG_PSS); + } + + if (srslte_ue_dl_decode_fft_estimate_multi(&ue_dl, signal_buffer, tti%10, &cfi) < 0) { + Error("Getting PDCCH FFT estimate\n"); + return false; + } + chest_done = true; + } else { + chest_done = false; + } + if (chest_done && decode_pdcch) { /* and not in DRX mode */ + + float noise_estimate = phy->avg_noise; + + if (!phy->args->equalizer_mode.compare("zf")) { + noise_estimate = 0; + } + + if (srslte_pdcch_extract_llr_multi(&ue_dl.pdcch, ue_dl.sf_symbols_m, ue_dl.ce_m, noise_estimate, tti%10, cfi)) { + Error("Extracting PDCCH LLR\n"); + return false; + } + } + return (decode_pdcch || phy->get_pending_ack(tti)); +} + + + + + + + + + +/********************* Downlink processing functions ****************************/ + +bool phch_worker::decode_pdcch_dl(srsue::mac_interface_phy::mac_grant_t* grant) +{ + char timestr[64]; + timestr[0]='\0'; + + dl_rnti = phy->get_dl_rnti(tti); + if (dl_rnti) { + + srslte_rnti_type_t type = phy->get_dl_rnti_type(); + + srslte_dci_msg_t dci_msg; + srslte_ra_dl_dci_t dci_unpacked; + + Debug("Looking for RNTI=0x%x\n", dl_rnti); + + if (srslte_ue_dl_find_dl_dci_type(&ue_dl, cfi, tti%10, dl_rnti, type, &dci_msg) != 1) { + return false; + } + + if (srslte_dci_msg_to_dl_grant(&dci_msg, dl_rnti, cell.nof_prb, cell.nof_ports, &dci_unpacked, &grant->phy_grant.dl)) { + Error("Converting DCI message to DL grant\n"); + return false; + } + + /* Fill MAC grant structure */ + grant->ndi = dci_unpacked.ndi; + grant->pid = dci_unpacked.harq_process; + grant->n_bytes = grant->phy_grant.dl.mcs.tbs/8; + grant->tti = tti; + grant->rv = dci_unpacked.rv_idx; + grant->rnti = dl_rnti; + grant->rnti_type = type; + grant->last_tti = 0; + + last_dl_pdcch_ncce = srslte_ue_dl_get_ncce(&ue_dl); + + char hexstr[16]; + hexstr[0]='\0'; + if (phy->log_h->get_level() >= srslte::LOG_LEVEL_INFO) { + srslte_vec_sprint_hex(hexstr, dci_msg.data, dci_msg.nof_bits); + } + Info("PDCCH: DL DCI %s cce_index=%2d, L=%d, n_data_bits=%d, hex=%s\n", srslte_dci_format_string(dci_msg.format), + last_dl_pdcch_ncce, (1<= 0 && rv <= 3) { + if (!srslte_ue_dl_cfg_grant(&ue_dl, grant, cfi, tti%10, rv)) { + if (ue_dl.pdsch_cfg.grant.mcs.mod > 0 && ue_dl.pdsch_cfg.grant.mcs.tbs >= 0) { + + float noise_estimate = srslte_chest_dl_get_noise_estimate(&ue_dl.chest); + + if (!phy->args->equalizer_mode.compare("zf")) { + noise_estimate = 0; + } + + /* Set decoder iterations */ + if (phy->args->pdsch_max_its > 0) { + srslte_sch_set_max_noi(&ue_dl.pdsch.dl_sch, phy->args->pdsch_max_its); + } + + + #ifdef LOG_EXECTIME + struct timeval t[3]; + gettimeofday(&t[1], NULL); + #endif + + bool ack = srslte_pdsch_decode_multi(&ue_dl.pdsch, &ue_dl.pdsch_cfg, softbuffer, ue_dl.sf_symbols_m, + ue_dl.ce_m, noise_estimate, rnti, payload) == 0; + #ifdef LOG_EXECTIME + gettimeofday(&t[2], NULL); + get_time_interval(t); + snprintf(timestr, 64, ", dec_time=%4d us", (int) t[0].tv_usec); + #endif + + Info("PDSCH: l_crb=%2d, harq=%d, tbs=%d, mcs=%d, rv=%d, crc=%s, snr=%.1f dB, n_iter=%d%s\n", + grant->nof_prb, harq_pid, + grant->mcs.tbs/8, grant->mcs.idx, rv, + ack?"OK":"KO", + 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest)), + srslte_pdsch_last_noi(&ue_dl.pdsch), + timestr); + + //printf("tti=%d, cfo=%f\n", tti, cfo*15000); + //srslte_vec_save_file("pdsch", signal_buffer, sizeof(cf_t)*SRSLTE_SF_LEN_PRB(cell.nof_prb)); + + // Store metrics + dl_metrics.mcs = grant->mcs.idx; + + return ack; + } else { + Warning("Received grant for TBS=0\n"); + } + } else { + Error("Error configuring DL grant\n"); + } + } else { + Error("Error RV is not set or is invalid (%d)\n", rv); + } + return true; +} + +bool phch_worker::decode_phich(bool *ack) +{ + uint32_t I_lowest, n_dmrs; + if (phy->get_pending_ack(tti, &I_lowest, &n_dmrs)) { + if (ack) { + *ack = srslte_ue_dl_decode_phich(&ue_dl, tti%10, I_lowest, n_dmrs); + Info("PHICH: hi=%d, I_lowest=%d, n_dmrs=%d\n", *ack, I_lowest, n_dmrs); + } + phy->reset_pending_ack(tti); + return true; + } else { + return false; + } +} + + + + +/********************* Uplink processing functions ****************************/ + +bool phch_worker::decode_pdcch_ul(mac_interface_phy::mac_grant_t* grant) +{ + char timestr[64]; + timestr[0]='\0'; + + phy->reset_pending_ack(tti + 8); + + srslte_dci_msg_t dci_msg; + srslte_ra_ul_dci_t dci_unpacked; + srslte_dci_rar_grant_t rar_grant; + srslte_rnti_type_t type = phy->get_ul_rnti_type(); + + bool ret = false; + if (phy->get_pending_rar(tti, &rar_grant)) { + + if (srslte_dci_rar_to_ul_grant(&rar_grant, cell.nof_prb, pusch_hopping.hopping_offset, + &dci_unpacked, &grant->phy_grant.ul)) + { + Error("Converting RAR message to UL grant\n"); + return false; + } + grant->rnti_type = SRSLTE_RNTI_TEMP; + grant->is_from_rar = true; + grant->has_cqi_request = false; // In contention-based Random Access CQI request bit is reserved + Debug("RAR grant found for TTI=%d\n", tti); + ret = true; + } else { + ul_rnti = phy->get_ul_rnti(tti); + if (ul_rnti) { + if (srslte_ue_dl_find_ul_dci(&ue_dl, cfi, tti%10, ul_rnti, &dci_msg) != 1) { + return false; + } + + if (srslte_dci_msg_to_ul_grant(&dci_msg, cell.nof_prb, pusch_hopping.hopping_offset, + &dci_unpacked, &grant->phy_grant.ul, tti)) + { + Error("Converting DCI message to UL grant\n"); + return false; + } + grant->rnti_type = type; + grant->is_from_rar = false; + grant->has_cqi_request = dci_unpacked.cqi_request; + ret = true; + + char hexstr[16]; + hexstr[0]='\0'; + if (phy->log_h->get_level() >= srslte::LOG_LEVEL_INFO) { + srslte_vec_sprint_hex(hexstr, dci_msg.data, dci_msg.nof_bits); + } + // Change to last_location_ul + Info("PDCCH: UL DCI Format0 cce_index=%d, L=%d, n_data_bits=%d, hex=%s\n", + ue_dl.last_location_ul.ncce, (1<phy_grant.ul.mcs.tbs==0) { + srslte_vec_fprint_hex(stdout, dci_msg.data, dci_msg.nof_bits); + } + } + } + + /* Limit UL modulation if not supported by the UE or disabled by higher layers */ + if (!phy->config->enable_64qam) { + if (grant->phy_grant.ul.mcs.mod == SRSLTE_MOD_64QAM) { + grant->phy_grant.ul.mcs.mod = SRSLTE_MOD_16QAM; + grant->phy_grant.ul.Qm = 4; + } + } + + /* Make sure the grant is valid */ + if (ret && !srslte_dft_precoding_valid_prb(grant->phy_grant.ul.L_prb) && grant->phy_grant.ul.L_prb <= cell.nof_prb) { + Warning("Received invalid UL grant. L=%d\n", grant->phy_grant.ul.L_prb); + ret = false; + } + + if (ret) { + grant->ndi = dci_unpacked.ndi; + grant->pid = 0; // This is computed by MAC from TTI + grant->n_bytes = grant->phy_grant.ul.mcs.tbs/8; + grant->tti = tti; + grant->rnti = ul_rnti; + grant->rv = dci_unpacked.rv_idx; + if (SRSLTE_VERBOSE_ISINFO()) { + srslte_ra_pusch_fprint(stdout, &dci_unpacked, cell.nof_prb); + } + + return true; + } else { + return false; + } +} + +void phch_worker::reset_uci() +{ + bzero(&uci_data, sizeof(srslte_uci_data_t)); +} + +void phch_worker::set_uci_ack(bool ack) +{ + uci_data.uci_ack = ack?1:0; + uci_data.uci_ack_len = 1; +} + +void phch_worker::set_uci_sr() +{ + uci_data.scheduling_request = false; + if (phy->sr_enabled) { + uint32_t sr_tx_tti = (tti+4)%10240; + // Get I_sr parameter + if (srslte_ue_ul_sr_send_tti(I_sr, sr_tx_tti)) { + Info("PUCCH: SR transmission at TTI=%d, I_sr=%d\n", sr_tx_tti, I_sr); + uci_data.scheduling_request = true; + phy->sr_last_tx_tti = sr_tx_tti; + phy->sr_enabled = false; + } + } +} + +void phch_worker::set_uci_periodic_cqi() +{ + int cqi_fixed = phy->args->cqi_fixed; + int cqi_max = phy->args->cqi_max; + + if (period_cqi.configured && rnti_is_set) { + if (srslte_cqi_send(period_cqi.pmi_idx, (tti+4)%10240)) { + srslte_cqi_value_t cqi_report; + if (period_cqi.format_is_subband) { + // TODO: Implement subband periodic reports + cqi_report.type = SRSLTE_CQI_TYPE_SUBBAND; + cqi_report.subband.subband_cqi = srslte_cqi_from_snr(phy->avg_snr_db); + cqi_report.subband.subband_label = 0; + phy->log_h->console("Warning: Subband CQI periodic reports not implemented\n"); + Info("PUCCH: Periodic CQI=%d, SNR=%.1f dB\n", cqi_report.subband.subband_cqi, phy->avg_snr_db); + } else { + cqi_report.type = SRSLTE_CQI_TYPE_WIDEBAND; + if (cqi_fixed >= 0) { + cqi_report.wideband.wideband_cqi = cqi_fixed; + } else { + cqi_report.wideband.wideband_cqi = srslte_cqi_from_snr(phy->avg_snr_db); + } + if (cqi_max >= 0 && cqi_report.wideband.wideband_cqi > cqi_max) { + cqi_report.wideband.wideband_cqi = cqi_max; + } + Info("PUCCH: Periodic CQI=%d, SNR=%.1f dB\n", cqi_report.wideband.wideband_cqi, phy->avg_snr_db); + } + uci_data.uci_cqi_len = srslte_cqi_value_pack(&cqi_report, uci_data.uci_cqi); + rar_cqi_request = false; + } + } +} + +void phch_worker::set_uci_aperiodic_cqi() +{ + if (phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic_present) { + switch(phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic) { + case LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30: + /* only Higher Layer-configured subband feedback support right now, according to TS36.213 section 7.2.1 + - A UE shall report a wideband CQI value which is calculated assuming transmission on set S subbands + - The UE shall also report one subband CQI value for each set S subband. The subband CQI + value is calculated assuming transmission only in the subband + - Both the wideband and subband CQI represent channel quality for the first codeword, + even when RI>1 + - For transmission mode 3 the reported CQI values are calculated conditioned on the + reported RI. For other transmission modes they are reported conditioned on rank 1. + */ + if (rnti_is_set) { + srslte_cqi_value_t cqi_report; + cqi_report.type = SRSLTE_CQI_TYPE_SUBBAND_HL; + cqi_report.subband_hl.wideband_cqi = srslte_cqi_from_snr(phy->avg_snr_db); + + // TODO: implement subband CQI properly + cqi_report.subband_hl.subband_diff_cqi = 0; // Always report zero offset on all subbands + cqi_report.subband_hl.N = (cell.nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(cell.nof_prb) : 0; + + Info("PUSCH: Aperiodic CQI=%d, SNR=%.1f dB, for %d subbands\n", cqi_report.wideband.wideband_cqi, phy->avg_snr_db, cqi_report.subband_hl.N); + uci_data.uci_cqi_len = srslte_cqi_value_pack(&cqi_report, uci_data.uci_cqi); + } + break; + default: + Warning("Received CQI request but mode %s is not supported\n", + liblte_rrc_cqi_report_mode_aperiodic_text[phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic]); + break; + } + } else { + Warning("Received CQI request but aperiodic mode is not configured\n"); + } +} + +bool phch_worker::srs_is_ready_to_send() { + if (srs_cfg.configured) { + if (srslte_refsignal_srs_send_cs(srs_cfg.subframe_config, (tti+4)%10) == 1 && + srslte_refsignal_srs_send_ue(srs_cfg.I_srs, (tti+4)%10240) == 1) + { + return true; + } + } + return false; +} + +void phch_worker::set_tx_time(srslte_timestamp_t _tx_time) +{ + memcpy(&tx_time, &_tx_time, sizeof(srslte_timestamp_t)); +} + +void phch_worker::encode_pusch(srslte_ra_ul_grant_t *grant, uint8_t *payload, uint32_t current_tx_nb, + srslte_softbuffer_tx_t* softbuffer, uint32_t rv, uint16_t rnti, bool is_from_rar) +{ + char timestr[64]; + timestr[0]='\0'; + + if (srslte_ue_ul_cfg_grant(&ue_ul, grant, (tti+4)%10240, rv, current_tx_nb)) { + Error("Configuring UL grant\n"); + } + + if (srslte_ue_ul_pusch_encode_rnti_softbuffer(&ue_ul, + payload, uci_data, + softbuffer, + rnti, + signal_buffer[0])) + { + Error("Encoding PUSCH\n"); + } + + float p0_preamble = 0; + if (is_from_rar) { + p0_preamble = phy->p0_preamble; + } + float tx_power = srslte_ue_ul_pusch_power(&ue_ul, phy->pathloss, p0_preamble); + float gain = set_power(tx_power); + + // Save PUSCH power for PHR calculation + phy->cur_pusch_power = tx_power; + +#ifdef LOG_EXECTIME + gettimeofday(&logtime_start[2], NULL); + get_time_interval(logtime_start); + snprintf(timestr, 64, ", total_time=%4d us", (int) logtime_start[0].tv_usec); +#endif + + Info("PUSCH: tti_tx=%d, n_prb=%d, rb_start=%d, tbs=%d, mod=%d, mcs=%d, rv_idx=%d, ack=%s, cfo=%.1f Hz%s\n", + (tti+4)%10240, + grant->L_prb, grant->n_prb[0], + grant->mcs.tbs/8, grant->mcs.mod, grant->mcs.idx, rv, + uci_data.uci_ack_len>0?(uci_data.uci_ack?"1":"0"):"no", + cfo*15000, timestr); + + // Store metrics + ul_metrics.mcs = grant->mcs.idx; + ul_metrics.power = tx_power; + phy->set_ul_metrics(ul_metrics); +} + +void phch_worker::encode_pucch() +{ + char timestr[64]; + timestr[0]='\0'; + + if (uci_data.scheduling_request || uci_data.uci_ack_len > 0 || uci_data.uci_cqi_len > 0) + { + + // Drop CQI if there is collision with ACK + if (!period_cqi.simul_cqi_ack && uci_data.uci_ack_len > 0 && uci_data.uci_cqi_len > 0) { + uci_data.uci_cqi_len = 0; + } + +#ifdef LOG_EXECTIME + struct timeval t[3]; + gettimeofday(&t[1], NULL); +#endif + + if (srslte_ue_ul_pucch_encode(&ue_ul, uci_data, last_dl_pdcch_ncce, (tti+4)%10240, signal_buffer[0])) { + Error("Encoding PUCCH\n"); + } + +#ifdef LOG_EXECTIME + gettimeofday(&logtime_start[2], NULL); + memcpy(&t[2], &logtime_start[2], sizeof(struct timeval)); + get_time_interval(logtime_start); + get_time_interval(t); + snprintf(timestr, 64, ", enc_time=%d, total_time=%d us", (int) t[0].tv_usec, (int) logtime_start[0].tv_usec); +#endif + + float tx_power = srslte_ue_ul_pucch_power(&ue_ul, phy->pathloss, ue_ul.last_pucch_format, uci_data.uci_cqi_len, uci_data.uci_ack_len); + float gain = set_power(tx_power); + + Info("PUCCH: power=%.2f dBm, tti_tx=%d, n_cce=%3d, n_pucch=%d, n_prb=%d, ack=%s, sr=%s, cfo=%.1f Hz%s\n", + tx_power, (tti+4)%10240, + last_dl_pdcch_ncce, ue_ul.pucch.last_n_pucch, ue_ul.pucch.last_n_prb, + uci_data.uci_ack_len>0?(uci_data.uci_ack?"1":"0"):"no",uci_data.scheduling_request?"yes":"no", + cfo*15000, timestr); + } + + if (uci_data.scheduling_request) { + phy->sr_enabled = false; + } +} + +void phch_worker::encode_srs() +{ + char timestr[64]; + timestr[0]='\0'; + + if (srslte_ue_ul_srs_encode(&ue_ul, (tti+4)%10240, signal_buffer[0])) + { + Error("Encoding SRS\n"); + } + +#ifdef LOG_EXECTIME + gettimeofday(&logtime_start[2], NULL); + get_time_interval(logtime_start); + snprintf(timestr, 64, ", total_time=%4d us", (int) logtime_start[0].tv_usec); +#endif + + float tx_power = srslte_ue_ul_srs_power(&ue_ul, phy->pathloss); + float gain = set_power(tx_power); + uint32_t fi = srslte_vec_max_fi((float*) signal_buffer, SRSLTE_SF_LEN_PRB(cell.nof_prb)); + float *f = (float*) signal_buffer; + Info("SRS: power=%.2f dBm, tti_tx=%d%s\n", tx_power, (tti+4)%10240, timestr); + +} + +void phch_worker::enable_pregen_signals(bool enabled) +{ + pregen_enabled = enabled; + if (enabled) { + Info("Pre-generating UL signals worker=%d\n", get_id()); + srslte_ue_ul_pregen_signals(&ue_ul); + Info("Done pre-generating signals worker=%d\n", get_id()); + } +} + +void phch_worker::set_ul_params(bool pregen_disabled) +{ + phy_interface_rrc::phy_cfg_common_t *common = &phy->config->common; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated = &phy->config->dedicated; + + Info("Setting new params worker_id=%d, pregen_disabled=%d\n", get_id(), pregen_disabled); + + /* PUSCH DMRS signal configuration */ + bzero(&dmrs_cfg, sizeof(srslte_refsignal_dmrs_pusch_cfg_t)); + dmrs_cfg.group_hopping_en = common->pusch_cnfg.ul_rs.group_hopping_enabled; + dmrs_cfg.sequence_hopping_en = common->pusch_cnfg.ul_rs.sequence_hopping_enabled; + dmrs_cfg.cyclic_shift = common->pusch_cnfg.ul_rs.cyclic_shift; + dmrs_cfg.delta_ss = common->pusch_cnfg.ul_rs.group_assignment_pusch; + + /* PUSCH Hopping configuration */ + bzero(&pusch_hopping, sizeof(srslte_pusch_hopping_cfg_t)); + pusch_hopping.n_sb = common->pusch_cnfg.n_sb; + pusch_hopping.hop_mode = common->pusch_cnfg.hopping_mode == LIBLTE_RRC_HOPPING_MODE_INTRA_AND_INTER_SUBFRAME ? + pusch_hopping.SRSLTE_PUSCH_HOP_MODE_INTRA_SF : + pusch_hopping.SRSLTE_PUSCH_HOP_MODE_INTER_SF; + pusch_hopping.hopping_offset = common->pusch_cnfg.pusch_hopping_offset; + + /* PUSCH UCI configuration */ + bzero(&uci_cfg, sizeof(srslte_uci_cfg_t)); + uci_cfg.I_offset_ack = dedicated->pusch_cnfg_ded.beta_offset_ack_idx; + uci_cfg.I_offset_cqi = dedicated->pusch_cnfg_ded.beta_offset_cqi_idx; + uci_cfg.I_offset_ri = dedicated->pusch_cnfg_ded.beta_offset_ri_idx; + + /* PUCCH configuration */ + bzero(&pucch_cfg, sizeof(srslte_pucch_cfg_t)); + pucch_cfg.delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[common->pucch_cnfg.delta_pucch_shift%LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS]; + pucch_cfg.N_cs = common->pucch_cnfg.n_cs_an; + pucch_cfg.n_rb_2 = common->pucch_cnfg.n_rb_cqi; + pucch_cfg.srs_configured = dedicated->srs_ul_cnfg_ded.setup_present; + if (pucch_cfg.srs_configured) { + pucch_cfg.srs_cs_subf_cfg = liblte_rrc_srs_subfr_config_num[common->srs_ul_cnfg.subfr_cnfg%LIBLTE_RRC_SRS_SUBFR_CONFIG_N_ITEMS]; + pucch_cfg.srs_simul_ack = common->srs_ul_cnfg.ack_nack_simul_tx; + } + + /* PUCCH Scheduling configuration */ + bzero(&pucch_sched, sizeof(srslte_pucch_sched_t)); + pucch_sched.n_pucch_1[0] = 0; // TODO: n_pucch_1 for SPS + pucch_sched.n_pucch_1[1] = 0; + pucch_sched.n_pucch_1[2] = 0; + pucch_sched.n_pucch_1[3] = 0; + pucch_sched.N_pucch_1 = common->pucch_cnfg.n1_pucch_an; + pucch_sched.n_pucch_2 = dedicated->cqi_report_cnfg.report_periodic.pucch_resource_idx; + pucch_sched.n_pucch_sr = dedicated->sched_request_cnfg.sr_pucch_resource_idx; + + /* SRS Configuration */ + bzero(&srs_cfg, sizeof(srslte_refsignal_srs_cfg_t)); + srs_cfg.configured = dedicated->srs_ul_cnfg_ded.setup_present; + if (pucch_cfg.srs_configured) { + srs_cfg.subframe_config = liblte_rrc_srs_subfr_config_num[common->srs_ul_cnfg.subfr_cnfg%LIBLTE_RRC_SRS_SUBFR_CONFIG_N_ITEMS]; + srs_cfg.bw_cfg = liblte_rrc_srs_bw_config_num[common->srs_ul_cnfg.bw_cnfg%LIBLTE_RRC_SRS_BW_CONFIG_N_ITEMS]; + srs_cfg.I_srs = dedicated->srs_ul_cnfg_ded.srs_cnfg_idx; + srs_cfg.B = dedicated->srs_ul_cnfg_ded.srs_bandwidth; + srs_cfg.b_hop = dedicated->srs_ul_cnfg_ded.srs_hopping_bandwidth; + srs_cfg.n_rrc = dedicated->srs_ul_cnfg_ded.freq_domain_pos; + srs_cfg.k_tc = dedicated->srs_ul_cnfg_ded.tx_comb; + srs_cfg.n_srs = dedicated->srs_ul_cnfg_ded.cyclic_shift; + } + + /* UL power control configuration */ + bzero(&power_ctrl, sizeof(srslte_ue_ul_powerctrl_t)); + power_ctrl.p0_nominal_pusch = common->ul_pwr_ctrl.p0_nominal_pusch; + power_ctrl.alpha = liblte_rrc_ul_power_control_alpha_num[common->ul_pwr_ctrl.alpha%LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_N_ITEMS]; + power_ctrl.p0_nominal_pucch = common->ul_pwr_ctrl.p0_nominal_pucch; + power_ctrl.delta_f_pucch[0] = liblte_rrc_delta_f_pucch_format_1_num[common->ul_pwr_ctrl.delta_flist_pucch.format_1%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_N_ITEMS]; + power_ctrl.delta_f_pucch[1] = liblte_rrc_delta_f_pucch_format_1b_num[common->ul_pwr_ctrl.delta_flist_pucch.format_1b%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_N_ITEMS]; + power_ctrl.delta_f_pucch[2] = liblte_rrc_delta_f_pucch_format_2_num[common->ul_pwr_ctrl.delta_flist_pucch.format_2%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_N_ITEMS]; + power_ctrl.delta_f_pucch[3] = liblte_rrc_delta_f_pucch_format_2a_num[common->ul_pwr_ctrl.delta_flist_pucch.format_2a%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_N_ITEMS]; + power_ctrl.delta_f_pucch[4] = liblte_rrc_delta_f_pucch_format_2b_num[common->ul_pwr_ctrl.delta_flist_pucch.format_2b%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_N_ITEMS]; + + power_ctrl.delta_preamble_msg3 = common->ul_pwr_ctrl.delta_preamble_msg3; + + power_ctrl.p0_ue_pusch = dedicated->ul_pwr_ctrl_ded.p0_ue_pusch; + power_ctrl.delta_mcs_based = dedicated->ul_pwr_ctrl_ded.delta_mcs_en==LIBLTE_RRC_DELTA_MCS_ENABLED_EN0; + power_ctrl.acc_enabled = dedicated->ul_pwr_ctrl_ded.accumulation_en; + power_ctrl.p0_ue_pucch = dedicated->ul_pwr_ctrl_ded.p0_ue_pucch; + power_ctrl.p_srs_offset = dedicated->ul_pwr_ctrl_ded.p_srs_offset; + + srslte_ue_ul_set_cfg(&ue_ul, &dmrs_cfg, &srs_cfg, &pucch_cfg, &pucch_sched, &uci_cfg, &pusch_hopping, &power_ctrl); + + /* CQI configuration */ + bzero(&period_cqi, sizeof(srslte_cqi_periodic_cfg_t)); + period_cqi.configured = dedicated->cqi_report_cnfg.report_periodic_setup_present; + period_cqi.pmi_idx = dedicated->cqi_report_cnfg.report_periodic.pmi_cnfg_idx; + period_cqi.simul_cqi_ack = dedicated->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi; + period_cqi.format_is_subband = dedicated->cqi_report_cnfg.report_periodic.format_ind_periodic == + LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_SUBBAND_CQI; + period_cqi.subband_size = dedicated->cqi_report_cnfg.report_periodic.format_ind_periodic_subband_k; + + /* SR configuration */ + I_sr = dedicated->sched_request_cnfg.sr_cnfg_idx; + + + if (pregen_enabled && !pregen_disabled) { + Info("Pre-generating UL signals worker=%d\n", get_id()); + srslte_ue_ul_pregen_signals(&ue_ul); + Info("Done pre-generating signals worker=%d\n", get_id()); + } +} + +float phch_worker::set_power(float tx_power) { + float gain = 0; + /* Check if UL power control is enabled */ + if(phy->args->ul_pwr_ctrl_en) { + /* Adjust maximum power if it changes significantly */ + if (tx_power < phy->cur_radio_power - 5 || tx_power > phy->cur_radio_power + 5) { + phy->cur_radio_power = tx_power; + float radio_tx_power = phy->cur_radio_power; + gain = phy->get_radio()->set_tx_power(radio_tx_power); + } + } + return gain; +} + +void phch_worker::start_plot() { +#ifdef ENABLE_GUI + if (plot_worker_id == -1) { + plot_worker_id = get_id(); + phy->log_h->console("Starting plot for worker_id=%d\n", plot_worker_id); + init_plots(this); + } else { + phy->log_h->console("Trying to start a plot but already started by worker_id=%d\n", plot_worker_id); + } +#else + phy->log_h->console("Trying to start a plot but plots are disabled (ENABLE_GUI constant in phch_worker.cc)\n"); +#endif +} + +int phch_worker::read_ce_abs(float *ce_abs) { + int i=0; + int sz = srslte_symbol_sz(cell.nof_prb); + bzero(ce_abs, sizeof(float)*sz); + int g = (sz - 12*cell.nof_prb)/2; + for (i = 0; i < 12*cell.nof_prb; i++) { + ce_abs[g+i] = 20 * log10(cabs(ue_dl.ce[0][i])); + if (isinf(ce_abs[g+i])) { + ce_abs[g+i] = -80; + } + } + return sz; +} + +int phch_worker::read_pdsch_d(cf_t* pdsch_d) +{ + memcpy(pdsch_d, ue_dl.pdsch.d, ue_dl.pdsch_cfg.nbits.nof_re*sizeof(cf_t)); + return ue_dl.pdsch_cfg.nbits.nof_re; +} + + + +/**************************** Measurements **************************/ + +void phch_worker::update_measurements() +{ + float snr_ema_coeff = phy->args->snr_ema_coeff; + if (chest_done) { + /* Compute ADC/RX gain offset every 20 ms */ + if ((tti%20) == 0 || phy->rx_gain_offset == 0) { + float rx_gain_offset = 0; + if (phy->get_radio()->has_rssi() && phy->args->rssi_sensor_enabled) { + float rssi_all_signal = srslte_chest_dl_get_rssi(&ue_dl.chest); + if (rssi_all_signal) { + rx_gain_offset = 10*log10(rssi_all_signal)-phy->get_radio()->get_rssi(); + } else { + rx_gain_offset = 0; + } + } else { + rx_gain_offset = phy->get_radio()->get_rx_gain(); + } + if (phy->rx_gain_offset) { + phy->rx_gain_offset = SRSLTE_VEC_EMA(phy->rx_gain_offset, rx_gain_offset, 0.1); + } else { + phy->rx_gain_offset = rx_gain_offset; + } + } + + // Average RSRQ + float cur_rsrq = 10*log10(srslte_chest_dl_get_rsrq(&ue_dl.chest)); + if (isnormal(cur_rsrq)) { + phy->avg_rsrq_db = SRSLTE_VEC_EMA(phy->avg_rsrq_db, cur_rsrq, snr_ema_coeff); + } + + // Average RSRP + float cur_rsrp = srslte_chest_dl_get_rsrp(&ue_dl.chest); + if (isnormal(cur_rsrp)) { + phy->avg_rsrp = SRSLTE_VEC_EMA(phy->avg_rsrp, cur_rsrp, snr_ema_coeff); + } + + /* Correct absolute power measurements by RX gain offset */ + float rsrp = 10*log10(srslte_chest_dl_get_rsrp(&ue_dl.chest)) + 30 - phy->rx_gain_offset; + float rssi = 10*log10(srslte_chest_dl_get_rssi(&ue_dl.chest)) + 30 - phy->rx_gain_offset; + + // TODO: Send UE measurements to RRC where filtering is done. Now do filtering here + if (isnormal(rsrp)) { + if (!phy->avg_rsrp_db) { + phy->avg_rsrp_db = rsrp; + } else { + uint32_t k = 4; // Set by RRC reconfiguration message + float coeff = pow(0.5,(float) k/4); + phy->avg_rsrp_db = SRSLTE_VEC_EMA(phy->avg_rsrp_db, rsrp, coeff); + } + } + // Compute PL + float tx_crs_power = phy->config->common.pdsch_cnfg.rs_power; + phy->pathloss = tx_crs_power - phy->avg_rsrp_db; + + // Average noise + float cur_noise = srslte_chest_dl_get_noise_estimate(&ue_dl.chest); + if (isnormal(cur_noise)) { + if (!phy->avg_noise) { + phy->avg_noise = cur_noise; + } else { + phy->avg_noise = SRSLTE_VEC_EMA(phy->avg_noise, cur_noise, snr_ema_coeff); + } + } + + // Compute SNR + phy->avg_snr_db = 10*log10(phy->avg_rsrp/phy->avg_noise); + + // Store metrics + dl_metrics.n = phy->avg_noise; + dl_metrics.rsrp = phy->avg_rsrp_db; + dl_metrics.rsrq = phy->avg_rsrq_db; + dl_metrics.rssi = rssi; + dl_metrics.pathloss = phy->pathloss; + dl_metrics.sinr = phy->avg_snr_db; + dl_metrics.turbo_iters = srslte_pdsch_last_noi(&ue_dl.pdsch); + phy->set_dl_metrics(dl_metrics); + + } +} + + +/********** Execution time trace function ************/ + +void phch_worker::start_trace() { + trace_enabled = true; +} + +void phch_worker::write_trace(std::string filename) { + tr_exec.writeToBinary(filename + ".exec"); +} + +void phch_worker::tr_log_start() +{ + if (trace_enabled) { + gettimeofday(&tr_time[1], NULL); + } +} + +void phch_worker::tr_log_end() +{ + if (trace_enabled) { + gettimeofday(&tr_time[2], NULL); + get_time_interval(tr_time); + tr_exec.push(tti, tr_time[0].tv_usec); + } +} + +} + + + + + + + + +/*********************************************************** + * + * PLOT TO VISUALIZE THE CHANNEL RESPONSEE + * + ***********************************************************/ + + +#ifdef ENABLE_GUI +plot_real_t pce; +plot_scatter_t pconst; +#define SCATTER_PDSCH_BUFFER_LEN (20*6*SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)) +#define SCATTER_PDSCH_PLOT_LEN 4000 +float tmp_plot[SCATTER_PDSCH_BUFFER_LEN]; +cf_t tmp_plot2[SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)]; + +void *plot_thread_run(void *arg) { + srsue::phch_worker *worker = (srsue::phch_worker*) arg; + + sdrgui_init(); + plot_real_init(&pce); + plot_real_setTitle(&pce, (char*) "Channel Response - Magnitude"); + plot_real_setLabels(&pce, (char*) "Index", (char*) "dB"); + plot_real_setYAxisScale(&pce, -40, 40); + + plot_scatter_init(&pconst); + plot_scatter_setTitle(&pconst, (char*) "PDSCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pconst, -4, 4); + plot_scatter_setYAxisScale(&pconst, -4, 4); + + plot_real_addToWindowGrid(&pce, (char*)"srsue", 0, 0); + plot_scatter_addToWindowGrid(&pconst, (char*)"srsue", 0, 1); + + + int n; + int readed_pdsch_re=0; + while(1) { + sem_wait(&plot_sem); + + if (readed_pdsch_re < SCATTER_PDSCH_PLOT_LEN) { + n = worker->read_pdsch_d(&tmp_plot2[readed_pdsch_re]); + readed_pdsch_re += n; + } else { + n = worker->read_ce_abs(tmp_plot); + if (n>0) { + plot_real_setNewData(&pce, tmp_plot, n); + } + if (readed_pdsch_re > 0) { + plot_scatter_setNewData(&pconst, tmp_plot2, readed_pdsch_re); + } + readed_pdsch_re = 0; + } + } + return NULL; +} + + +void init_plots(srsue::phch_worker *worker) { + + if (sem_init(&plot_sem, 0, 0)) { + perror("sem_init"); + exit(-1); + } + + pthread_attr_t attr; + struct sched_param param; + param.sched_priority = 0; + pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + pthread_attr_setschedparam(&attr, ¶m); + if (pthread_create(&plot_thread, &attr, plot_thread_run, worker)) { + perror("pthread_create"); + exit(-1); + } +} +#endif + diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc new file mode 100644 index 000000000..bd11a5490 --- /dev/null +++ b/srsue/src/phy/phy.cc @@ -0,0 +1,364 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +#include "common/threads.h" +#include "common/log.h" +#include "phy/phy.h" +#include "phy/phch_worker.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + + + +using namespace std; + + +namespace srsue { + +phy::phy() : workers_pool(MAX_WORKERS), + workers(MAX_WORKERS), + workers_common(phch_recv::MUTEX_X_WORKER*MAX_WORKERS) +{ +} + +void phy::set_default_args(phy_args_t *args) +{ + args->ul_pwr_ctrl_en = false; + args->prach_gain = -1; + args->cqi_max = -1; + args->cqi_fixed = -1; + args->snr_ema_coeff = 0.1; + args->snr_estim_alg = "refs"; + args->pdsch_max_its = 4; + args->attach_enable_64qam = false; + args->nof_phy_threads = DEFAULT_WORKERS; + args->equalizer_mode = "mmse"; + args->cfo_integer_enabled = false; + args->cfo_correct_tol_hz = 50; + args->time_correct_period = 5; + args->sfo_correct_disable = false; + args->sss_algorithm = "full"; + args->estimator_fil_w = 0.1; +} + +bool phy::check_args(phy_args_t *args) +{ + if (args->nof_phy_threads > 3) { + log_h->console("Error in PHY args: nof_phy_threads must be 1, 2 or 3\n"); + return false; + } + if (args->estimator_fil_w > 1.0) { + log_h->console("Error in PHY args: estimator_fil_w must be 0<=w<=1\n"); + return false; + } + if (args->snr_ema_coeff > 1.0) { + log_h->console("Error in PHY args: snr_ema_coeff must be 0<=w<=1\n"); + return false; + } + return true; +} + +bool phy::init(srslte::radio_multi* radio_handler_, mac_interface_phy *mac, rrc_interface_phy *rrc, + srslte::log *log_h_, phy_args_t *phy_args) +{ + + mlockall(MCL_CURRENT | MCL_FUTURE); + + n_ta = 0; + log_h = log_h_; + radio_handler = radio_handler_; + + if (!phy_args) { + args = &default_args; + set_default_args(args); + } else { + args = phy_args; + } + + if (!check_args(args)) { + return false; + } + + nof_workers = args->nof_phy_threads; + + // Add workers to workers pool and start threads + for (int i=0;iworker_cpu_mask); + } + prach_buffer.init(&config.common.prach_cnfg, args, log_h); + workers_common.init(&config, args, log_h, radio_handler, mac); + + // Warning this must be initialized after all workers have been added to the pool + sf_recv.init(radio_handler, mac, rrc, &prach_buffer, &workers_pool, &workers_common, log_h, args->nof_rx_ant, SF_RECV_THREAD_PRIO, args->sync_cpu_affinity); + + // Disable UL signal pregeneration until the attachment + enable_pregen_signals(false); + + return true; +} + +void phy::set_agc_enable(bool enabled) +{ + sf_recv.set_agc_enable(enabled); +} + +void phy::start_trace() +{ + for (int i=0;i( &(ostringstream() << i) )->str(); + workers[i].write_trace(filename + "_" + i_str); + } +} + +void phy::stop() +{ + sf_recv.stop(); + workers_pool.stop(); +} + +void phy::get_metrics(phy_metrics_t &m) { + workers_common.get_dl_metrics(m.dl); + workers_common.get_ul_metrics(m.ul); + workers_common.get_sync_metrics(m.sync); + int dl_tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(m.dl.mcs), workers_common.get_nof_prb()); + int ul_tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(m.ul.mcs), workers_common.get_nof_prb()); + m.dl.mabr_mbps = dl_tbs/1000.0; // TBS is bits/ms - convert to mbps + m.ul.mabr_mbps = ul_tbs/1000.0; // TBS is bits/ms - convert to mbps + Info("PHY: MABR estimates. DL: %4.6f Mbps. UL: %4.6f Mbps.\n", m.dl.mabr_mbps, m.ul.mabr_mbps); +} + +void phy::set_timeadv_rar(uint32_t ta_cmd) { + n_ta = srslte_N_ta_new_rar(ta_cmd); + sf_recv.set_time_adv_sec(((float) n_ta)*SRSLTE_LTE_TS); + Info("PHY: Set TA RAR: ta_cmd: %d, n_ta: %d, ta_usec: %.1f\n", ta_cmd, n_ta, ((float) n_ta)*SRSLTE_LTE_TS*1e6); +} + +void phy::set_timeadv(uint32_t ta_cmd) { + n_ta = srslte_N_ta_new(n_ta, ta_cmd); + //sf_recv.set_time_adv_sec(((float) n_ta)*SRSLTE_LTE_TS); + Warning("Not supported: Set TA: ta_cmd: %d, n_ta: %d, ta_usec: %.1f\n", ta_cmd, n_ta, ((float) n_ta)*SRSLTE_LTE_TS*1e6); +} + +void phy::configure_prach_params() +{ + if (sf_recv.status_is_sync()) { + Debug("Configuring PRACH parameters\n"); + srslte_cell_t cell; + sf_recv.get_current_cell(&cell); + if (!prach_buffer.init_cell(cell)) { + Error("Configuring PRACH parameters\n"); + } + } else { + Error("Cell is not synchronized\n"); + } +} + +void phy::configure_ul_params(bool pregen_disabled) +{ + Info("PHY: Configuring UL parameters\n"); + for (int i=0;iget_max_tx_power() - workers_common.cur_pusch_power; + return phr; +} + +float phy::get_pathloss_db() +{ + return workers_common.cur_pathloss; +} + +void phy::pdcch_ul_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start, int tti_end) +{ + workers_common.set_ul_rnti(rnti_type, rnti, tti_start, tti_end); +} + +void phy::pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start, int tti_end) +{ + workers_common.set_dl_rnti(rnti_type, rnti, tti_start, tti_end); +} + +void phy::pdcch_dl_search_reset() +{ + workers_common.set_dl_rnti(SRSLTE_RNTI_USER, 0); +} + +void phy::pdcch_ul_search_reset() +{ + workers_common.set_ul_rnti(SRSLTE_RNTI_USER, 0); +} + +void phy::get_current_cell(srslte_cell_t *cell) +{ + sf_recv.get_current_cell(cell); +} + +void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm) +{ + + if (!prach_buffer.prepare_to_send(preamble_idx, allowed_subframe, target_power_dbm)) { + Error("Preparing PRACH to send\n"); + } +} + +int phy::prach_tx_tti() +{ + return prach_buffer.tx_tti(); +} + +void phy::reset() +{ + // TODO + n_ta = 0; + pdcch_dl_search_reset(); + for(uint32_t i=0;i +#include +#include + +#include "srslte/srslte.h" +#include "common/log.h" +#include "phy/prach.h" +#include "phy/phy.h" +#include "common/phy_interface.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +namespace srsue { + + +void prach::free_cell() +{ + if (initiated) { + for (int i=0;i<64;i++) { + if (buffer[i]) { + free(buffer[i]); + } + } + if (signal_buffer) { + free(signal_buffer); + } + srslte_cfo_free(&cfo_h); + srslte_prach_free(&prach_obj); + } +} + +void prach::init(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *config_, phy_args_t *args_, srslte::log* log_h_) +{ + log_h = log_h_; + config = config_; + args = args_; +} + +bool prach::init_cell(srslte_cell_t cell_) +{ + // TODO: Check if other PRACH parameters changed + if (cell_.id != cell.id || !initiated) { + if (initiated) { + free_cell(); + } + cell = cell_; + preamble_idx = -1; + + uint32_t configIdx = config->prach_cnfg_info.prach_config_index; + uint32_t rootSeq = config->root_sequence_index; + uint32_t zeroCorrConfig = config->prach_cnfg_info.zero_correlation_zone_config; + uint32_t freq_offset = config->prach_cnfg_info.prach_freq_offset; + bool highSpeed = config->prach_cnfg_info.high_speed_flag; + + if (6 + freq_offset > cell.nof_prb) { + log_h->console("Error no space for PRACH: frequency offset=%d, N_rb_ul=%d\n", freq_offset, cell.nof_prb); + log_h->error("Error no space for PRACH: frequency offset=%d, N_rb_ul=%d\n", freq_offset, cell.nof_prb); + return false; + } + + if (srslte_prach_init(&prach_obj, srslte_symbol_sz(cell.nof_prb), + configIdx, rootSeq, highSpeed, zeroCorrConfig)) + { + Error("Initiating PRACH library\n"); + return false; + } + + len = prach_obj.N_seq + prach_obj.N_cp; + for (int i=0;i<64;i++) { + buffer[i] = (cf_t*) srslte_vec_malloc(len*sizeof(cf_t)); + if(!buffer[i]) { + return false; + } + if(srslte_prach_gen(&prach_obj, i, freq_offset, buffer[i])) { + Error("Generating PRACH preamble %d\n", i); + return false; + } + } + srslte_cfo_init(&cfo_h, len); + srslte_cfo_set_tol(&cfo_h, 0); + signal_buffer = (cf_t*) srslte_vec_malloc(len*sizeof(cf_t)); + initiated = signal_buffer?true:false; + transmitted_tti = -1; + Debug("PRACH Initiated %s\n", initiated?"OK":"KO"); + } + return initiated; +} + +bool prach::prepare_to_send(uint32_t preamble_idx_, int allowed_subframe_, float target_power_dbm_) +{ + if (initiated && preamble_idx_ < 64) { + preamble_idx = preamble_idx_; + target_power_dbm = target_power_dbm_; + allowed_subframe = allowed_subframe_; + transmitted_tti = -1; + Debug("PRACH prepare to send preamble %d\n", preamble_idx); + return true; + } else { + if (!initiated) { + Error("PRACH not initiated\n"); + } else if (preamble_idx_ >= 64) { + Error("Invalid preamble %d\n", preamble_idx_); + } + return false; + } +} + +bool prach::is_ready_to_send(uint32_t current_tti_) { + if (initiated && preamble_idx >= 0 && preamble_idx < 64) { + // consider the number of subframes the transmission must be anticipated + uint32_t current_tti = (current_tti_ + tx_advance_sf)%10240; + if (srslte_prach_tti_opportunity(&prach_obj, current_tti, allowed_subframe)) { + Debug("PRACH Buffer: Ready to send at tti: %d (now is %d)\n", current_tti, current_tti_); + transmitted_tti = current_tti; + return true; + } + } + return false; +} + +int prach::tx_tti() { + return transmitted_tti; +} + +float prach::get_p0_preamble() +{ + return target_power_dbm; +} + + +void prach::send(srslte::radio *radio_handler, float cfo, float pathloss, srslte_timestamp_t tx_time) +{ + + // Get current TX gain + float old_gain = radio_handler->get_tx_gain(); + + // Correct CFO before transmission + srslte_cfo_correct(&cfo_h, buffer[preamble_idx], signal_buffer, cfo / srslte_symbol_sz(cell.nof_prb)); + + // If power control is enabled, choose amplitude and power + if (args->ul_pwr_ctrl_en) { + // Get PRACH transmission power + float tx_power = SRSLTE_MIN(SRSLTE_PC_MAX, pathloss + target_power_dbm); + + // Get output power for amplitude 1 + radio_handler->set_tx_power(tx_power); + + // Scale signal + float digital_power = srslte_vec_avg_power_cf(signal_buffer, len); + float scale = sqrtf(pow(10,tx_power/10)/digital_power); + + srslte_vec_sc_prod_cfc(signal_buffer, scale, signal_buffer, len); + log_h->console("PRACH: Pathloss=%.2f dB, Target power %.2f dBm, TX_power %.2f dBm, TX_gain %.1f dB\n", + pathloss, target_power_dbm, tx_power, radio_handler->get_tx_gain(), scale); + + } else { + float prach_gain = args->prach_gain; + if (prach_gain > 0) { + radio_handler->set_tx_gain(prach_gain); + } + Debug("TX PRACH: Power control for PRACH is disabled, setting gain to %.0f dB\n", prach_gain); + } + + radio_handler->tx(signal_buffer, len, tx_time); + radio_handler->tx_end(); + + Info("PRACH: Transmitted preamble=%d, CFO=%.2f KHz, tx_time=%f\n", + preamble_idx, cfo*15, tx_time.frac_secs); + preamble_idx = -1; + + radio_handler->set_tx_gain(old_gain); + Debug("Restoring TX gain to %.0f dB\n", old_gain); +} + +} // namespace srsue + diff --git a/srsue/src/set_net_admin_caps.cc b/srsue/src/set_net_admin_caps.cc new file mode 100644 index 000000000..1485a26d9 --- /dev/null +++ b/srsue/src/set_net_admin_caps.cc @@ -0,0 +1,53 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include + +using namespace std; + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + std::cout << "Please call with the binary to provide net admin capabilities to as a parameter." << std::endl; + std::cout << "E.g. ./set_net_admin_caps myprogCalling " << std::endl; + return -1; + } + + std::string command("setcap 'cap_net_admin=eip' "); + command += argv[1]; + + std::cout << "Calling " << command << " with root rights." << std::endl; + setuid(0); + system(command.c_str()); + + return 0; +} + diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc new file mode 100644 index 000000000..97f59c15e --- /dev/null +++ b/srsue/src/ue.cc @@ -0,0 +1,319 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include "ue.h" +//#include "srslte_version_check.h" +#include "srslte/srslte.h" +#include +#include +#include +#include +#include + +using namespace srslte; + +namespace srsue{ + +ue* ue::instance = NULL; +pthread_mutex_t ue_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +ue* ue::get_instance(void) +{ + pthread_mutex_lock(&ue_instance_mutex); + if(NULL == instance) { + instance = new ue(); + } + pthread_mutex_unlock(&ue_instance_mutex); + return(instance); +} +void ue::cleanup(void) +{ + pthread_mutex_lock(&ue_instance_mutex); + if(NULL != instance) { + delete instance; + instance = NULL; + } + pthread_mutex_unlock(&ue_instance_mutex); +} + +ue::ue() + :started(false) +{ + pool = byte_buffer_pool::get_instance(); +} + +ue::~ue() +{ + byte_buffer_pool::cleanup(); +} + +bool ue::init(all_args_t *args_) +{ + args = args_; + + logger.init(args->log.filename); + rf_log.init("RF ", &logger); + phy_log.init("PHY ", &logger, true); + mac_log.init("MAC ", &logger, true); + rlc_log.init("RLC ", &logger); + pdcp_log.init("PDCP", &logger); + rrc_log.init("RRC ", &logger); + nas_log.init("NAS ", &logger); + gw_log.init("GW ", &logger); + usim_log.init("USIM", &logger); + + // Init logs + logger.log("\n\n"); + rf_log.set_level(srslte::LOG_LEVEL_INFO); + phy_log.set_level(level(args->log.phy_level)); + mac_log.set_level(level(args->log.mac_level)); + rlc_log.set_level(level(args->log.rlc_level)); + pdcp_log.set_level(level(args->log.pdcp_level)); + rrc_log.set_level(level(args->log.rrc_level)); + nas_log.set_level(level(args->log.nas_level)); + gw_log.set_level(level(args->log.gw_level)); + usim_log.set_level(level(args->log.usim_level)); + + phy_log.set_hex_limit(args->log.phy_hex_limit); + mac_log.set_hex_limit(args->log.mac_hex_limit); + rlc_log.set_hex_limit(args->log.rlc_hex_limit); + pdcp_log.set_hex_limit(args->log.pdcp_hex_limit); + rrc_log.set_hex_limit(args->log.rrc_hex_limit); + nas_log.set_hex_limit(args->log.nas_hex_limit); + gw_log.set_hex_limit(args->log.gw_hex_limit); + usim_log.set_hex_limit(args->log.usim_hex_limit); + + // Set up pcap and trace + if(args->pcap.enable) + { + mac_pcap.open(args->pcap.filename.c_str()); + mac.start_pcap(&mac_pcap); + } + if(args->trace.enable) + { + phy.start_trace(); + radio.start_trace(); + } + + // Init layers + + /* Start Radio */ + char *dev_name = NULL; + if (args->rf.device_name.compare("auto")) { + dev_name = (char*) args->rf.device_name.c_str(); + } + + char *dev_args = NULL; + if (args->rf.device_args.compare("auto")) { + dev_args = (char*) args->rf.device_args.c_str(); + } + + printf("Opening RF device with %d RX antennas...\n", args->rf.nof_rx_ant); + if(!radio.init_multi(args->rf.nof_rx_ant, dev_args, dev_name)) + { + printf("Failed to find device %s with args %s\n", + args->rf.device_name.c_str(), args->rf.device_args.c_str()); + return false; + } + + // Set RF options + if (args->rf.time_adv_nsamples.compare("auto")) { + radio.set_tx_adv(atoi(args->rf.time_adv_nsamples.c_str())); + } + if (args->rf.burst_preamble.compare("auto")) { + radio.set_burst_preamble(atof(args->rf.burst_preamble.c_str())); + } + + radio.set_manual_calibration(&args->rf_cal); + + // Set PHY options + args->expert.phy.nof_rx_ant = args->rf.nof_rx_ant; + + if (args->rf.tx_gain > 0) { + args->expert.phy.ul_pwr_ctrl_en = false; + } else { + args->expert.phy.ul_pwr_ctrl_en = true; + } + phy.init(&radio, &mac, &rrc, &phy_log, &args->expert.phy); + + if (args->rf.rx_gain < 0) { + radio.start_agc(false); + radio.set_tx_rx_gain_offset(10); + phy.set_agc_enable(true); + } else { + radio.set_rx_gain(args->rf.rx_gain); + } + if (args->rf.tx_gain > 0) { + radio.set_tx_gain(args->rf.tx_gain); + } else { + radio.set_tx_gain(args->rf.rx_gain); + std::cout << std::endl << + "Warning: TX gain was not set. " << + "Using open-loop power control (not working properly)" << std::endl << std::endl; + } + + radio.register_error_handler(rf_msg); + + radio.set_rx_freq(args->rf.dl_freq); + radio.set_tx_freq(args->rf.ul_freq); + + phy_log.console("Setting frequency: DL=%.1f Mhz, UL=%.1f MHz\n", args->rf.dl_freq/1e6, args->rf.ul_freq/1e6); + + mac.init(&phy, &rlc, &rrc, &mac_log); + rlc.init(&pdcp, &rrc, this, &rlc_log, &mac); + pdcp.init(&rlc, &rrc, &gw, &pdcp_log, SECURITY_DIRECTION_UPLINK); + rrc.init(&phy, &mac, &rlc, &pdcp, &nas, &usim, &mac, &rrc_log); + + rrc.set_ue_category(args->expert.ue_cateogry); + + nas.init(&usim, &rrc, &gw, &nas_log); + gw.init(&pdcp, &rrc, this, &gw_log); + usim.init(&args->usim, &usim_log); + + started = true; + return true; +} + +void ue::pregenerate_signals(bool enable) +{ + phy.enable_pregen_signals(enable); +} + +void ue::test_con_restablishment() { + rrc.test_con_restablishment(); +} + +void ue::stop() +{ + if(started) + { + usim.stop(); + nas.stop(); + rrc.stop(); + + // Caution here order of stop is very important to avoid locks + + + // Stop RLC and PDCP before GW to avoid locking on queue + rlc.stop(); + pdcp.stop(); + gw.stop(); + + // PHY must be stopped before radio otherwise it will lock on rf_recv() + mac.stop(); + phy.stop(); + radio.stop(); + + usleep(1e5); + if(args->pcap.enable) + { + mac_pcap.close(); + } + if(args->trace.enable) + { + phy.write_trace(args->trace.phy_filename); + radio.write_trace(args->trace.radio_filename); + } + started = false; + } +} + +bool ue::is_attached() +{ + return (EMM_STATE_REGISTERED == nas.get_state()); +} + +void ue::start_plot() { + phy.start_plot(); +} + +bool ue::get_metrics(ue_metrics_t &m) +{ + m.rf = rf_metrics; + bzero(&rf_metrics, sizeof(rf_metrics_t)); + rf_metrics.rf_error = false; // Reset error flag + + if(EMM_STATE_REGISTERED == nas.get_state()) { + if(RRC_STATE_RRC_CONNECTED == rrc.get_state()) { + phy.get_metrics(m.phy); + mac.get_metrics(m.mac); + rlc.get_metrics(m.rlc); + gw.get_metrics(m.gw); + return true; + } + } + return false; +} + +void ue::rf_msg(srslte_rf_error_t error) +{ + ue *u = ue::get_instance(); + u->handle_rf_msg(error); +} + +void ue::handle_rf_msg(srslte_rf_error_t error) +{ + if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OVERFLOW) { + rf_metrics.rf_o++; + rf_metrics.rf_error = true; + rf_log.warning("Overflow\n"); + }else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_UNDERFLOW) { + rf_metrics.rf_u++; + rf_metrics.rf_error = true; + rf_log.warning("Underflow\n"); + } else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_LATE) { + rf_metrics.rf_l++; + rf_metrics.rf_error = true; + rf_log.warning("Late\n"); + } else if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OTHER) { + std::string str(error.msg); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + str.erase(std::remove(str.begin(), str.end(), '\r'), str.end()); + str.push_back('\n'); + rf_log.info(str); + } +} + +srslte::LOG_LEVEL_ENUM ue::level(std::string l) +{ + std::transform(l.begin(), l.end(), l.begin(), ::toupper); + if("NONE" == l){ + return srslte::LOG_LEVEL_NONE; + }else if("ERROR" == l){ + return srslte::LOG_LEVEL_ERROR; + }else if("WARNING" == l){ + return srslte::LOG_LEVEL_WARNING; + }else if("INFO" == l){ + return srslte::LOG_LEVEL_INFO; + }else if("DEBUG" == l){ + return srslte::LOG_LEVEL_DEBUG; + }else{ + return srslte::LOG_LEVEL_NONE; + } +} + +} // namespace srsue diff --git a/srsue/test/CMakeLists.txt b/srsue/test/CMakeLists.txt new file mode 100644 index 000000000..4ad133d17 --- /dev/null +++ b/srsue/test/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2015 Software Radio Systems Limited +# +# This file is part of srsUE +# +# srsUE 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. +# +# srsUE 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. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_subdirectory(phy) +add_subdirectory(mac) +add_subdirectory(upper) diff --git a/srsue/test/mac/CMakeLists.txt b/srsue/test/mac/CMakeLists.txt new file mode 100644 index 000000000..b1bf4e07f --- /dev/null +++ b/srsue/test/mac/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2015 Software Radio Systems Limited +# +# This file is part of srsUE +# +# srsUE 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. +# +# srsUE 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. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(mac_test mac_test.cc) +target_link_libraries(mac_test srsue_mac srsue_phy srslte_common srslte_phy srslte_radio srslte_asn1 ${SRSLTE_LIBRARIES} ${LIBLTE_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) + diff --git a/srsue/test/mac/mac_test.cc b/srsue/test/mac/mac_test.cc new file mode 100644 index 000000000..cf28eb407 --- /dev/null +++ b/srsue/test/mac/mac_test.cc @@ -0,0 +1,495 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include +#include + +#include "asn1/liblte_rrc.h" +#include "radio/radio_multi.h" +#include "phy/phy.h" +#include "common/mac_interface.h" +#include "common/log_stdout.h" +#include "mac/mac.h" +#include "common/mac_pcap.h" + + + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +typedef struct { + float rf_rx_freq; + float rf_tx_freq; + float rf_rx_gain; + float rf_tx_gain; + int verbose; + bool do_trace; + bool do_pcap; +}prog_args_t; + +void args_default(prog_args_t *args) { + args->rf_rx_freq = -1.0; + args->rf_tx_freq = -1.0; + args->rf_rx_gain = -1; // set to autogain + args->rf_tx_gain = -1; + args->verbose = 0; + args->do_trace = false; + args->do_pcap = false; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gGtpv] -f rx_frequency (in Hz) -F tx_frequency (in Hz)\n", prog); + printf("\t-g RF RX gain [Default AGC]\n"); + printf("\t-G RF TX gain [Default same as RX gain (AGC)]\n"); + printf("\t-t Enable trace [Default disabled]\n"); + printf("\t-p Enable PCAP capture [Default disabled]\n"); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gGftpFv")) != -1) { + switch (opt) { + case 'g': + args->rf_rx_gain = atof(argv[optind]); + break; + case 'G': + args->rf_tx_gain = atof(argv[optind]); + break; + case 'f': + args->rf_rx_freq = atof(argv[optind]); + break; + case 'F': + args->rf_tx_freq = atof(argv[optind]); + break; + case 't': + args->do_trace = true; + break; + case 'p': + args->do_pcap = true; + break; + case 'v': + args->verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rf_rx_freq < 0 || args->rf_tx_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + +// Determine SI messages scheduling as in 36.331 5.2.3 Acquisition of an SI message +uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t x) { + return (period*10*(1+tti/(period*10))+x)%10240; // the 1 means next opportunity +} + +void setup_mac_phy_sib2(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, srsue::mac *mac, srsue::phy *phy) { + + // Apply RACH configuration + srsue::mac_interface_rrc::mac_cfg_t mac_cfg; + mac->get_config(&mac_cfg); + memcpy(&mac_cfg.rach, &sib2->rr_config_common_sib.rach_cnfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + mac->set_config(&mac_cfg); + + printf("Set RACH ConfigCommon: NofPreambles=%d, ResponseWindow=%d, ContentionResolutionTimer=%d ms, MaxTrials=%d\n", + liblte_rrc_number_of_ra_preambles_num[sib2->rr_config_common_sib.rach_cnfg.num_ra_preambles], + liblte_rrc_ra_response_window_size_num[sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size], + liblte_rrc_mac_contention_resolution_timer_num[sib2->rr_config_common_sib.rach_cnfg.mac_con_res_timer], + liblte_rrc_preamble_trans_max_num[sib2->rr_config_common_sib.rach_cnfg.preamble_trans_max]); + + // Apply PHY RR Config Common + srsue::phy_interface_rrc::phy_cfg_common_t common; + memcpy(&common.pdsch_cnfg, &sib2->rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pusch_cnfg, &sib2->rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pucch_cnfg, &sib2->rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.ul_pwr_ctrl, &sib2->rr_config_common_sib.ul_pwr_ctrl, sizeof(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT)); + memcpy(&common.prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_STRUCT)); + if (sib2->rr_config_common_sib.srs_ul_cnfg.present) { + memcpy(&common.srs_ul_cnfg, &sib2->rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + } else { + // default is release + common.srs_ul_cnfg.present = false; + } + phy->set_config_common(&common); + phy->configure_ul_params(); + + printf("Set PUSCH ConfigCommon: HopOffset=%d, RSGroup=%d, RSNcs=%d, N_sb=%d\n", + sib2->rr_config_common_sib.pusch_cnfg.pusch_hopping_offset, + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch, + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift, + sib2->rr_config_common_sib.pusch_cnfg.n_sb); + + printf("Set PUCCH ConfigCommon: DeltaShift=%d, CyclicShift=%d, N1=%d, NRB=%d\n", + liblte_rrc_delta_pucch_shift_num[sib2->rr_config_common_sib.pucch_cnfg.delta_pucch_shift], + sib2->rr_config_common_sib.pucch_cnfg.n_cs_an, + sib2->rr_config_common_sib.pucch_cnfg.n1_pucch_an, + sib2->rr_config_common_sib.pucch_cnfg.n_rb_cqi); + + printf("Set PRACH ConfigCommon: SeqIdx=%d, HS=%d, FreqOffset=%d, ZC=%d, ConfigIndex=%d\n", + sib2->rr_config_common_sib.prach_cnfg.root_sequence_index, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag?1:0, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index); + + printf("Set SRS ConfigCommon: BW-Configuration=%d, SF-Configuration=%d, ACKNACK=%d\n", + sib2->rr_config_common_sib.srs_ul_cnfg.bw_cnfg, + sib2->rr_config_common_sib.srs_ul_cnfg.subfr_cnfg, + sib2->rr_config_common_sib.srs_ul_cnfg.ack_nack_simul_tx); + +} + +void process_connsetup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *msg, srsue::mac *mac, srsue::phy *phy) { + + // FIXME: There's an error parsing the connectionSetup message. This value is hard-coded: + + if (msg->rr_cnfg.phy_cnfg_ded_present) { + phy->set_config_dedicated(&msg->rr_cnfg.phy_cnfg_ded); + } + printf("Set PHY configuration: SR-n_pucch=%d, SR-ConfigIndex=%d, SRS-ConfigIndex=%d, SRS-bw=%d, SRS-Nrcc=%d, SRS-hop=%d, SRS-Ncs=%d\n", + msg->rr_cnfg.phy_cnfg_ded.sched_request_cnfg.sr_pucch_resource_idx, + msg->rr_cnfg.phy_cnfg_ded.sched_request_cnfg.sr_cnfg_idx, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.srs_cnfg_idx, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.srs_bandwidth, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.freq_domain_pos, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.srs_hopping_bandwidth, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.cyclic_shift); + + srsue::mac_interface_rrc::mac_cfg_t mac_set; + mac->get_config(&mac_set); + memcpy(&mac_set.main, &msg->rr_cnfg.mac_main_cnfg, sizeof(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT)); + // SR is a PHY config but is needed by SR procedure in 36.321 5.4.4 + memcpy(&mac_set.sr, &msg->rr_cnfg.phy_cnfg_ded.sched_request_cnfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + mac->set_config(&mac_set); + + printf("Set MAC configuration: dsr-TransMAX: %d, harq-MaxReTX=%d, bsr-TimerReTX=%d, bsr-TimerPeriodic=%d\n", + liblte_rrc_dsr_trans_max_num[msg->rr_cnfg.phy_cnfg_ded.sched_request_cnfg.dsr_trans_max], + liblte_rrc_max_harq_tx_num[msg->rr_cnfg.mac_main_cnfg.explicit_value.ulsch_cnfg.max_harq_tx], + liblte_rrc_retransmission_bsr_timer_num[msg->rr_cnfg.mac_main_cnfg.explicit_value.ulsch_cnfg.retx_bsr_timer], + liblte_rrc_periodic_bsr_timer_num[msg->rr_cnfg.mac_main_cnfg.explicit_value.ulsch_cnfg.periodic_bsr_timer]); + + phy->configure_ul_params(); + + // Setup radio bearers + for (int i=0;irr_cnfg.srb_to_add_mod_list_size;i++) { + if (msg->rr_cnfg.srb_to_add_mod_list[i].lc_default_cnfg_present) { + printf("Setting up Default Configuration for SRB%d \n", msg->rr_cnfg.srb_to_add_mod_list[i].srb_id); + switch(msg->rr_cnfg.srb_to_add_mod_list[i].srb_id) { + case 1: + mac->setup_lcid(1, 0, 1, -1, -1); + break; + case 2: + mac->setup_lcid(2, 0, 3, -1, -1); + break; + } + } + } +// for (int i=0;irr_cnfg.drb_to_add_mod_list_size;i++) { +// printf("Setting up DRB%d\n", msg->rr_cnfg.drb_to_add_mod_list[i].drb_id); +// // todo +// } +} + + +// Hex bytes for the connection setup complete packet +// Got hex bytes from http://www.sharetechnote.com/html/RACH_LTE.html +uint8_t setupComplete_segm[2][41] ={ { + 0x88, 0x00, 0x00, 0x20, 0x21, 0x90, 0xa0, 0x12, 0x00, 0x00, 0x80, 0xf0, 0x5e, 0x3b, 0xf1, 0x04, + 0x64, 0x04, 0x1d, 0x20, 0x44, 0x2f, 0xd8, 0x4b, 0xd1, 0x02, 0x00, 0x00, 0x83, 0x03, 0x41, 0xb0, + 0xe5, 0x60, 0x13, 0x81, 0x83}, + + {0xb0, 0x01, 0x01, 0x01, 0x48, 0x4b, 0xd1, 0x00, 0x7d, 0x21, 0x70, 0x28, 0x01, 0x5c, 0x08, 0x80, + 0x00, 0xc4, 0x0f, 0x97, 0x80, 0xd0, 0x4c, 0x4b, 0xd1, 0x00, 0xc0, 0x58, 0x44, 0x0d, 0x5d, 0x62, + 0x99, 0x74, 0x04, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00} +}; +uint8_t setupComplete[80] = { + 0x88, 0x00, 0x00, 0x20, 0x21, 0x90, 0xa0, 0x12, 0x00, 0x00, 0x80, 0xf0, 0x5e, 0x3b, 0xf1, 0x04, + 0x64, 0x04, 0x1d, 0x20, 0x44, 0x2f, 0xd8, 0x4b, 0xd1, 0x02, 0x00, 0x00, 0x83, 0x03, 0x41, 0xb0, + 0xe5, 0x60, 0x13, 0x81, 0x83, 0x48, 0x4b, 0xd1, 0x00, 0x7d, 0x21, 0x70, 0x28, 0x01, 0x5c, 0x08, 0x80, + 0x00, 0xc4, 0x0f, 0x97, 0x80, 0xd0, 0x4c, 0x4b, 0xd1, 0x00, 0xc0, 0x58, 0x44, 0x0d, 0x5d, 0x62, + 0x99, 0x74, 0x04, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00}; + +uint32_t lengths[2] = {37, 41}; +uint8_t reply[2] = {0x00, 0x04}; + + +srslte::radio_multi radio; +srsue::phy phy; +srsue::mac mac; +srslte::mac_pcap mac_pcap; + +prog_args_t prog_args; + +void sig_int_handler(int signo) +{ + if (prog_args.do_trace) { + //radio.write_trace("radio"); + phy.write_trace("phy"); + } + if (prog_args.do_pcap) { + mac_pcap.close(); + } + mac.stop(); + exit(0); +} + +class rlctest : public srsue::rlc_interface_mac { +public: + bool mib_decoded; + bool sib1_decoded; + bool sib2_decoded; + bool connsetup_decoded; + int nsegm_dcch; + int send_ack; + uint8_t si_window_len, sib2_period; + + rlctest() { + mib_decoded = false; + sib1_decoded = false; + sib2_decoded = false; + connsetup_decoded = false; + nsegm_dcch = 0; + si_window_len = 0; + sib2_period = 0; + send_ack = 0; + } + uint32_t get_total_buffer_state(uint32_t lcid) { + return get_buffer_state(lcid); + } + uint32_t get_buffer_state(uint32_t lcid) { + if (lcid == 0) { + if (sib2_decoded && !connsetup_decoded) { + return 6; + } + } else if (lcid == 1) { + if (connsetup_decoded && nsegm_dcch < 2) { + return lengths[nsegm_dcch]; + } else if (send_ack == 1) { + return 2; + } + } + return 0; + } + + int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) + { + if (lcid == 0) { + LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; + // Prepare ConnectionRequest packet + ul_ccch_msg.msg_type = LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ; + ul_ccch_msg.msg.rrc_con_req.ue_id_type = LIBLTE_RRC_CON_REQ_UE_ID_TYPE_RANDOM_VALUE; + ul_ccch_msg.msg.rrc_con_req.ue_id.random = 1000; + ul_ccch_msg.msg.rrc_con_req.cause = LIBLTE_RRC_CON_REQ_EST_CAUSE_MO_SIGNALLING; + liblte_rrc_pack_ul_ccch_msg(&ul_ccch_msg, &bit_msg); + + uint64_t uecri=0; + uint8_t *ue_cri_ptr = (uint8_t*) &uecri; + uint32_t nbytes = bit_msg.N_bits/8; + uint8_t *ptr = bit_msg.msg; + for (int i=0;i= 80) { + printf("Sending Connection Setup Complete length 80\n"); + memcpy(payload, setupComplete, 80); + return 80; + } else { + uint32_t r = 0; + if (nof_bytes >= lengths[nsegm_dcch]) { + printf("Sending Connection Setup Complete %d/2 length %d\n", nsegm_dcch, lengths[nsegm_dcch]); + memcpy(payload, setupComplete_segm[nsegm_dcch], lengths[nsegm_dcch]); + r = lengths[nsegm_dcch]; + nsegm_dcch++; + } else { + r = 0; + } + return r; + } + } else if (send_ack == 1) { + printf("Send RLC ACK\n"); + memcpy(payload, reply, 2*sizeof(uint8_t)); + send_ack = 2; + return 2; + } + } + return 0; + } + + void write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) { + if (lcid == 0) { + LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg; + printf("ConnSetup received %d bytes\n", nof_bytes); + srslte_vec_fprint_byte(stdout, payload, nof_bytes); + srslte_bit_unpack_vector(payload, bit_msg.msg, nof_bytes*8); + bit_msg.N_bits = nof_bytes*8; + liblte_rrc_unpack_dl_ccch_msg(&bit_msg, &dl_ccch_msg); + printf("Response: %s\n", liblte_rrc_dl_ccch_msg_type_text[dl_ccch_msg.msg_type]); + switch (dl_ccch_msg.msg_type) { + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP: + // Process ConnectionSetup + process_connsetup(&dl_ccch_msg.msg.rrc_con_setup, &mac, &phy); + connsetup_decoded = true; + break; + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REJ: + break; + } + } else if (lcid == 1) { + printf("Received on DCCH0 %d bytes\n", nof_bytes); + if (send_ack == 0) { + send_ack = 1; + } + } + } + + void write_pdu_bcch_bch(uint8_t *payload, uint32_t nof_bytes) + { + LIBLTE_RRC_MIB_STRUCT mib; + srslte_vec_fprint_byte(stdout, payload, nof_bytes); + srslte_bit_unpack_vector(payload, bit_msg.msg, nof_bytes*8); + bit_msg.N_bits = nof_bytes*8; + liblte_rrc_unpack_bcch_bch_msg(&bit_msg, &mib); + printf("MIB received %d bytes, BW=%s MHz\n", nof_bytes, liblte_rrc_dl_bandwidth_text[mib.dl_bw]); + mib_decoded = true; + } + + void write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes) + { + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT dlsch_msg; + srslte_bit_unpack_vector(payload, bit_msg.msg, nof_bytes*8); + bit_msg.N_bits = nof_bytes*8; + liblte_rrc_unpack_bcch_dlsch_msg(&bit_msg, &dlsch_msg); + if (dlsch_msg.N_sibs > 0) { + if (dlsch_msg.sibs[0].sib_type == LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1 && !sib1_decoded) { + si_window_len = liblte_rrc_si_window_length_num[dlsch_msg.sibs[0].sib.sib1.si_window_length]; + sib2_period = liblte_rrc_si_periodicity_num[dlsch_msg.sibs[0].sib.sib1.sched_info[0].si_periodicity]; + printf("SIB1 received %d bytes, CellID=%d, si_window=%d, sib2_period=%d\n", + nof_bytes, dlsch_msg.sibs[0].sib.sib1.cell_id&0xfff, si_window_len, sib2_period); + sib1_decoded = true; + mac.bcch_stop_rx(); + } else if (dlsch_msg.sibs[0].sib_type == LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2) { + + printf("SIB2 received %d bytes\n", nof_bytes); + setup_mac_phy_sib2(&dlsch_msg.sibs[0].sib.sib2, &mac, &phy); + sib2_decoded = true; + mac.bcch_stop_rx(); + } + } + } + + void write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes) {} + +private: + LIBLTE_BIT_MSG_STRUCT bit_msg; + LIBLTE_BYTE_MSG_STRUCT byte_msg; +}; + + +int main(int argc, char *argv[]) +{ + srslte::log_stdout mac_log("MAC"), phy_log("PHY"); + rlctest my_rlc; + parse_args(&prog_args, argc, argv); + + switch (prog_args.verbose) { + case 1: + mac_log.set_level(srslte::LOG_LEVEL_INFO); + phy_log.set_level(srslte::LOG_LEVEL_INFO); + break; + case 2: + mac_log.set_level(srslte::LOG_LEVEL_DEBUG); + phy_log.set_level(srslte::LOG_LEVEL_DEBUG); + break; + } + + // Capture SIGINT to write traces + if (prog_args.do_trace) { + signal(SIGINT, sig_int_handler); + //radio.start_trace(); + phy.start_trace(); + } + + if (prog_args.do_pcap) { + if (!prog_args.do_trace) { + signal(SIGINT, sig_int_handler); + } + mac_pcap.open("/tmp/ue_mac.pcap"); + mac.start_pcap(&mac_pcap); + } + + // Init Radio and PHY + radio.init(); + phy.init(&radio, &mac, NULL, &phy_log); + if (prog_args.rf_rx_gain > 0 && prog_args.rf_tx_gain > 0) { + radio.set_rx_gain(prog_args.rf_rx_gain); + radio.set_tx_gain(prog_args.rf_tx_gain); + } else { + radio.start_agc(false); + radio.set_tx_rx_gain_offset(10); + phy.set_agc_enable(true); + } + // Init MAC + mac.init(&phy, &my_rlc, NULL, &mac_log); + + // Set RX freq + radio.set_rx_freq(prog_args.rf_rx_freq); + radio.set_tx_freq(prog_args.rf_tx_freq); + + + while(1) { + uint32_t tti; + if (my_rlc.mib_decoded && mac.get_current_tti()) { + if (!my_rlc.sib1_decoded) { + usleep(10000); + tti = mac.get_current_tti(); + mac.bcch_start_rx(sib_start_tti(tti, 2, 5), 1); + } else if (!my_rlc.sib2_decoded) { + usleep(10000); + tti = mac.get_current_tti(); + mac.bcch_start_rx(sib_start_tti(tti, my_rlc.sib2_period, 0), my_rlc.si_window_len); + } + } + usleep(50000); + } +} + + + diff --git a/srsue/test/phy/CMakeLists.txt b/srsue/test/phy/CMakeLists.txt new file mode 100644 index 000000000..84356a023 --- /dev/null +++ b/srsue/test/phy/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright 2015 Software Radio Systems Limited +# +# This file is part of srsUE +# +# srsUE 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. +# +# srsUE 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. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(ue_itf_test_sib1 ue_itf_test_sib1.cc) +target_link_libraries(ue_itf_test_sib1 srsue_phy srslte_common srslte_phy srslte_radio ${SRSLTE_LIBRARIES} ${LIBLTE_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) + +add_executable(ue_itf_test_prach ue_itf_test_prach.cc) +target_link_libraries(ue_itf_test_prach srsue_phy srslte_common srslte_phy srslte_radio ${SRSLTE_LIBRARIES} ${LIBLTE_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) diff --git a/srsue/test/phy/ue_itf_test_prach.cc b/srsue/test/phy/ue_itf_test_prach.cc new file mode 100644 index 000000000..ce58f73c5 --- /dev/null +++ b/srsue/test/phy/ue_itf_test_prach.cc @@ -0,0 +1,388 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include + +#include "srslte/phy/utils/debug.h" +#include "phy/phy.h" +#include "common/phy_interface.h" +#include "common/log_stdout.h" +#include "radio/radio_multi.h" + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +typedef struct { + float rf_rx_freq; + float rf_tx_freq; + float rf_rx_gain; + float rf_tx_gain; + bool continous; +}prog_args_t; + +prog_args_t prog_args; +uint32_t srsapps_verbose = 0; + +void args_default(prog_args_t *args) { + args->rf_rx_freq = -1.0; + args->rf_tx_freq = -1.0; + args->rf_rx_gain = -1; // set to autogain + args->rf_tx_gain = -1; + args->continous = false; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gGcv] -f rx_frequency -F tx_frequency (in Hz)\n", prog); + printf("\t-g RF RX gain [Default AGC]\n"); + printf("\t-G RF TX gain [Default same as RX gain (AGC)]\n"); + printf("\t-c Run continuously [Default only once]\n"); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gGfFcv")) != -1) { + switch (opt) { + case 'g': + args->rf_rx_gain = atof(argv[optind]); + break; + case 'G': + args->rf_tx_gain = atof(argv[optind]); + break; + case 'f': + args->rf_rx_freq = atof(argv[optind]); + break; + case 'F': + args->rf_tx_freq = atof(argv[optind]); + break; + case 'c': + args->continous = true; + break; + case 'v': + srsapps_verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rf_rx_freq < 0 || args->rf_tx_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + + + +typedef enum{ + rar_header_type_bi = 0, + rar_header_type_rapid, + rar_header_type_n_items, +}rar_header_t; +static const char rar_header_text[rar_header_type_n_items][8] = {"BI", "RAPID"}; + +typedef struct { + rar_header_t hdr_type; + bool hopping_flag; + uint32_t tpc_command; + bool ul_delay; + bool csi_req; + uint16_t rba; + uint16_t timing_adv_cmd; + uint16_t temp_c_rnti; + uint8_t mcs; + uint8_t RAPID; + uint8_t BI; +}rar_msg_t; + + +int rar_unpack(uint8_t *buffer, rar_msg_t *msg) +{ + int ret = SRSLTE_ERROR; + uint8_t *ptr = buffer; + + if(buffer != NULL && + msg != NULL) + { + ptr++; + msg->hdr_type = (rar_header_t) *ptr++; + if(msg->hdr_type == rar_header_type_bi) { + ptr += 2; + msg->BI = srslte_bit_pack(&ptr, 4); + ret = SRSLTE_SUCCESS; + } else if (msg->hdr_type == rar_header_type_rapid) { + msg->RAPID = srslte_bit_pack(&ptr, 6); + ptr++; + + msg->timing_adv_cmd = srslte_bit_pack(&ptr, 11); + msg->hopping_flag = *ptr++; + msg->rba = srslte_bit_pack(&ptr, 10); + msg->mcs = srslte_bit_pack(&ptr, 4); + msg->tpc_command = srslte_bit_pack(&ptr, 3); + msg->ul_delay = *ptr++; + msg->csi_req = *ptr++; + msg->temp_c_rnti = srslte_bit_pack(&ptr, 16); + ret = SRSLTE_SUCCESS; + } + } + + return(ret); +} + + + +srsue::phy my_phy; +bool bch_decoded = false; + +uint8_t payload[10240]; +uint8_t payload_bits[10240]; +const uint8_t conn_request_msg[] = {0x20, 0x06, 0x1F, 0x5C, 0x2C, 0x04, 0xB2, 0xAC, 0xF6, 0x00, 0x00, 0x00}; + +enum mac_state {RA, RAR, CONNREQUEST, CONNSETUP} state = RA; + +uint32_t preamble_idx = 0; +rar_msg_t rar_msg; + +uint32_t nof_rtx_connsetup = 0; +uint32_t rv_value[4] = {0, 2, 3, 1}; + +void config_phy() { + srsue::phy_interface_rrc::phy_cfg_t config; + + config.common.prach_cnfg.prach_cnfg_info.prach_config_index = 0; + config.common.prach_cnfg.prach_cnfg_info.prach_freq_offset = 0; + config.common.prach_cnfg.prach_cnfg_info.high_speed_flag = false; + config.common.prach_cnfg.root_sequence_index = 0; + config.common.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config = 11; + + config.common.pusch_cnfg.ul_rs.group_hopping_enabled = false; + config.common.pusch_cnfg.ul_rs.sequence_hopping_enabled = false; + config.common.pusch_cnfg.n_sb = 2; + config.common.pusch_cnfg.ul_rs.cyclic_shift = 0; + config.common.pusch_cnfg.ul_rs.group_assignment_pusch = 0; + config.common.pusch_cnfg.pusch_hopping_offset = 0; + + config.common.pucch_cnfg.delta_pucch_shift = LIBLTE_RRC_DELTA_PUCCH_SHIFT_DS2; + config.common.pucch_cnfg.n_cs_an = 0; + config.common.pucch_cnfg.n1_pucch_an = 1; + + my_phy.configure_ul_params(); + my_phy.configure_prach_params(); +} + +srslte_softbuffer_rx_t softbuffer_rx; +srslte_softbuffer_tx_t softbuffer_tx; + +uint16_t temp_c_rnti; + +/******** MAC Interface implementation */ +class testmac : public srsue::mac_interface_phy +{ +public: + + testmac() { + rar_rnti_set = false; + } + + bool rar_rnti_set; + + void pch_decoded_ok(uint32_t len) {} + + + void tti_clock(uint32_t tti) { + if (!rar_rnti_set) { + int prach_tti = my_phy.prach_tx_tti(); + if (prach_tti > 0) { + my_phy.pdcch_dl_search(SRSLTE_RNTI_RAR, 1+prach_tti%10, prach_tti+3, prach_tti+13); + rar_rnti_set = true; + } + } + } + + void new_grant_ul(mac_grant_t grant, tb_action_ul_t *action) { + printf("New grant UL\n"); + memcpy(payload, conn_request_msg, grant.n_bytes*sizeof(uint8_t)); + action->current_tx_nb = nof_rtx_connsetup; + action->rv = rv_value[nof_rtx_connsetup%4]; + action->softbuffer = &softbuffer_tx; + action->rnti = temp_c_rnti; + action->expect_ack = (nof_rtx_connsetup < 5)?true:false; + action->payload_ptr = payload; + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + memcpy(&last_grant, &grant, sizeof(mac_grant_t)); + action->tx_enabled = true; + if (action->rv == 0) { + srslte_softbuffer_tx_reset(&softbuffer_tx); + } + my_phy.pdcch_dl_search(SRSLTE_RNTI_USER, temp_c_rnti); + } + + void new_grant_ul_ack(mac_grant_t grant, bool ack, tb_action_ul_t *action) { + printf("New grant UL ACK\n"); + } + + void harq_recv(uint32_t tti, bool ack, tb_action_ul_t *action) { + printf("harq recv hi=%d\n", ack?1:0); + if (!ack) { + nof_rtx_connsetup++; + action->current_tx_nb = nof_rtx_connsetup; + action->rv = rv_value[nof_rtx_connsetup%4]; + action->softbuffer = &softbuffer_tx; + action->rnti = temp_c_rnti; + action->expect_ack = true; + memcpy(&action->phy_grant, &last_grant.phy_grant, sizeof(srslte_phy_grant_t)); + action->tx_enabled = true; + if (action->rv == 0) { + srslte_softbuffer_tx_reset(&softbuffer_tx); + } + printf("Retransmission %d, rv=%d\n", nof_rtx_connsetup, action->rv); + } + } + + void new_grant_dl(mac_grant_t grant, tb_action_dl_t *action) { + action->decode_enabled = true; + action->default_ack = false; + if (grant.rnti == 2) { + action->generate_ack = false; + } else { + action->generate_ack = true; + } + action->payload_ptr = payload; + action->rnti = grant.rnti; + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + memcpy(&last_grant, &grant, sizeof(mac_grant_t)); + action->rv = grant.rv; + action->softbuffer = &softbuffer_rx; + + if (action->rv == 0) { + srslte_softbuffer_rx_reset(&softbuffer_rx); + } + } + + void tb_decoded(bool ack, srslte_rnti_type_t rnti_type, uint32_t harq_pid) { + if (ack) { + if (rnti_type == SRSLTE_RNTI_RAR) { + my_phy.pdcch_dl_search_reset(); + srslte_bit_unpack_vector(payload, payload_bits, last_grant.n_bytes*8); + rar_unpack(payload_bits, &rar_msg); + if (rar_msg.RAPID == preamble_idx) { + + printf("Received RAR at TTI: %d\n", last_grant.tti); + my_phy.set_timeadv_rar(rar_msg.timing_adv_cmd); + + temp_c_rnti = rar_msg.temp_c_rnti; + + if (last_grant.n_bytes*8 > 20 + SRSLTE_RAR_GRANT_LEN) { + uint8_t rar_grant[SRSLTE_RAR_GRANT_LEN]; + memcpy(rar_grant, &payload_bits[20], sizeof(uint8_t)*SRSLTE_RAR_GRANT_LEN); + my_phy.set_rar_grant(last_grant.tti, rar_grant); + } + } else { + printf("Received RAR RAPID=%d\n", rar_msg.RAPID); + } + } else { + printf("Received Connection Setup\n"); + my_phy.pdcch_dl_search_reset(); + } + } + } + + void bch_decoded_ok(uint8_t *payload, uint32_t len) { + printf("BCH decoded\n"); + bch_decoded = true; + srslte_cell_t cell; + my_phy.get_current_cell(&cell); + srslte_softbuffer_rx_init(&softbuffer_rx, cell.nof_prb); + srslte_softbuffer_tx_init(&softbuffer_tx, cell.nof_prb); + } + +private: + mac_grant_t last_grant; +}; + + +testmac my_mac; +srslte::radio_multi radio; + +int main(int argc, char *argv[]) +{ + srslte::log_stdout log("PHY"); + + parse_args(&prog_args, argc, argv); + + // Init Radio and PHY + radio.init(); + my_phy.init(&radio, &my_mac, NULL, &log); + if (prog_args.rf_rx_gain > 0 && prog_args.rf_tx_gain > 0) { + radio.set_rx_gain(prog_args.rf_rx_gain); + radio.set_tx_gain(prog_args.rf_tx_gain); + } else { + radio.start_agc(false); + radio.set_tx_rx_gain_offset(10); + my_phy.set_agc_enable(true); + } + + if (srsapps_verbose == 1) { + log.set_level(srslte::LOG_LEVEL_INFO); + printf("Log level info\n"); + } + if (srsapps_verbose == 2) { + log.set_level(srslte::LOG_LEVEL_DEBUG); + printf("Log level debug\n"); + } + + // Give it time to create thread + sleep(1); + + // Set RX freq + radio.set_rx_freq(prog_args.rf_rx_freq); + radio.set_tx_freq(prog_args.rf_tx_freq); + + // Instruct the PHY to configure PRACH parameters and sync to current cell + my_phy.sync_start(); + + while(!my_phy.status_is_sync()) { + usleep(20000); + } + + // Setup PHY parameters + config_phy(); + + /* Instruct PHY to send PRACH and prepare it for receiving RAR */ + my_phy.prach_send(preamble_idx); + + /* go to idle and process each tti */ + bool running = true; + while(running) { + sleep(1); + } + my_phy.stop(); + radio.stop_rx(); +} + + + diff --git a/srsue/test/phy/ue_itf_test_sib1.cc b/srsue/test/phy/ue_itf_test_sib1.cc new file mode 100644 index 000000000..fcf55794f --- /dev/null +++ b/srsue/test/phy/ue_itf_test_sib1.cc @@ -0,0 +1,214 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include + +#include "srslte/phy/utils/debug.h" +#include "phy/phy.h" +#include "common/log_stdout.h" +#include "common/mac_interface.h" +#include "radio/radio_multi.h" + + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ + +typedef struct { + float rf_freq; + float rf_gain; +}prog_args_t; + +uint32_t srsapps_verbose = 0; + +prog_args_t prog_args; + +void args_default(prog_args_t *args) { + args->rf_freq = -1.0; + args->rf_gain = -1.0; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gv] -f rx_frequency (in Hz)\n", prog); + printf("\t-g RF RX gain [Default AGC]\n"); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gfv")) != -1) { + switch (opt) { + case 'g': + args->rf_gain = atof(argv[optind]); + break; + case 'f': + args->rf_freq = atof(argv[optind]); + break; + case 'v': + srsapps_verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rf_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + +srsue::phy my_phy; +bool bch_decoded = false; +uint32_t total_pkts=0; +uint32_t total_dci=0; +uint32_t total_oks=0; +uint8_t payload[1024]; +srslte_softbuffer_rx_t softbuffer; + +/******** MAC Interface implementation */ +class testmac : public srsue::mac_interface_phy +{ +public: + void new_grant_ul(mac_grant_t grant, tb_action_ul_t *action) { + printf("New grant UL\n"); + } + void new_grant_ul_ack(mac_grant_t grant, bool ack, tb_action_ul_t *action) { + printf("New grant UL ACK\n"); + } + + void harq_recv(uint32_t tti, bool ack, tb_action_ul_t *action) { + printf("harq recv\n"); + } + + void new_grant_dl(mac_grant_t grant, tb_action_dl_t *action) { + total_dci++; + + + action->decode_enabled = true; + action->default_ack = false; + action->generate_ack = false; + action->payload_ptr = payload; + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + action->rv = ((uint32_t) ceilf((float)3*((my_phy.tti_to_SFN(grant.tti)/2)%4)/2))%4; + action->softbuffer = &softbuffer; + action->rnti = grant.rnti; + if (action->rv == 0) { + srslte_softbuffer_rx_reset(&softbuffer); + } + } + + + + void tb_decoded(bool ack, srslte_rnti_type_t rnti, uint32_t harq_pid) { + if (ack) { + total_oks++; + } + } + + void pch_decoded_ok(uint32_t len) {} + + void bch_decoded_ok(uint8_t *payload, uint32_t len) { + printf("BCH decoded\n"); + bch_decoded = true; + srslte_cell_t cell; + my_phy.get_current_cell(&cell); + srslte_softbuffer_rx_init(&softbuffer, cell.nof_prb); + } + void tti_clock(uint32_t tti) { + + } +}; + + +testmac my_mac; +srslte::radio_multi radio; + + + + +int main(int argc, char *argv[]) +{ + srslte::log_stdout log("PHY"); + + parse_args(&prog_args, argc, argv); + + // Init Radio and PHY + radio.init(); + my_phy.init(&radio, &my_mac, NULL, &log); + if (prog_args.rf_gain > 0) { + radio.set_rx_gain(prog_args.rf_gain); + } else { + radio.start_agc(false); + my_phy.set_agc_enable(true); + } + + if (srsapps_verbose == 1) { + log.set_level(srslte::LOG_LEVEL_INFO); + printf("Log level info\n"); + } + if (srsapps_verbose == 2) { + log.set_level(srslte::LOG_LEVEL_DEBUG); + printf("Log level debug\n"); + } + + // Give it time to create thread + sleep(1); + + // Set RX freq and gain + radio.set_rx_freq(prog_args.rf_freq); + + my_phy.sync_start(); + + bool running = true; + while(running) { + if (bch_decoded && my_phy.status_is_sync()) { + uint32_t tti = my_phy.get_current_tti(); + + // SIB1 is scheduled in subframe #5 of even frames, try to decode next frame SIB1 + tti = (((tti/20)*20) + 25)%10240; + my_phy.pdcch_dl_search(SRSLTE_RNTI_SI, SRSLTE_SIRNTI, tti, tti+1); + + total_pkts++; + } + usleep(30000); + if (bch_decoded && my_phy.status_is_sync() && total_pkts > 0) { + if (srslte_verbose == SRSLTE_VERBOSE_NONE && srsapps_verbose == 0) { + float gain = prog_args.rf_gain; + if (gain < 0) { + gain = radio.get_rx_gain(); + } + printf("PDCCH BLER %.1f \%% PDSCH BLER %.1f \%% (total pkts: %5u) Gain: %.1f dB\r", + 100-(float) 100*total_dci/total_pkts, + (float) 100*(1 - total_oks/total_pkts), + total_pkts, gain); + } + } + } + my_phy.stop(); + radio.stop_rx(); +} diff --git a/srsue/test/upper/CMakeLists.txt b/srsue/test/upper/CMakeLists.txt new file mode 100644 index 000000000..b69973f7f --- /dev/null +++ b/srsue/test/upper/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright 2015 Software Radio Systems Limited +# +# This file is part of srsUE +# +# srsUE 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. +# +# srsUE 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. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +# IP traffic over RLC test +add_executable(ip_test ip_test.cc) +target_link_libraries(ip_test srsue_mac + srsue_phy + srslte_common + srslte_phy + srslte_radio + srslte_upper + ${SRSLTE_LIBRARIES} + ${LIBLTE_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "Added custom post-build command: ${BUILD_CMD}") + add_custom_command(TARGET ip_test POST_BUILD COMMAND ${BUILD_CMD}) +else(NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "No post-build command defined") +endif (NOT ${BUILD_CMD} STREQUAL "") + diff --git a/srsue/test/upper/ip_test.cc b/srsue/test/upper/ip_test.cc new file mode 100644 index 000000000..d2461b791 --- /dev/null +++ b/srsue/test/upper/ip_test.cc @@ -0,0 +1,645 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/utils/debug.h" +#include "mac/mac.h" +#include "phy/phy.h" +#include "common/threads.h" +#include "common/common.h" +#include "common/buffer_pool.h" +#include "common/logger.h" +#include "common/log_filter.h" +#include "upper/rlc.h" +#include "upper/rrc.h" +#include "radio/radio_multi.h" + +#define START_TUNTAP +#define USE_RADIO +#define PRINT_GW 0 + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ + +#define LCID 3 + +typedef struct { + float rx_freq; + float tx_freq; + float rx_gain; + float tx_gain; + int time_adv; + std::string ip_address; +}prog_args_t; + +uint32_t srsapps_verbose = 1; + +prog_args_t prog_args; + +void args_default(prog_args_t *args) { + args->tx_freq = 2.505e9; + args->rx_freq = 2.625e9; + args->rx_gain = 50.0; + args->tx_gain = 70.0; + args->time_adv = -1; // calibrated for b210 + args->ip_address = "192.168.3.2"; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gGIrfFtv]\n", prog); + printf("\t-f RX frequency [Default %.1f MHz]\n", args->rx_freq/1e6); + printf("\t-F TX frequency [Default %.1f MHz]\n", args->tx_freq/1e6); + printf("\t-g RX gain [Default %.1f]\n", args->rx_gain); + printf("\t-G TX gain [Default %.1f]\n", args->tx_gain); + printf("\t-I IP address [Default %s]\n", args->ip_address.c_str()); + printf("\t-t time advance (in samples) [Default %d]\n", args->time_adv); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gGfFItv")) != -1) { + switch (opt) { + case 'g': + args->rx_gain = atof(argv[optind]); + break; + case 'G': + args->tx_gain = atof(argv[optind]); + break; + case 'f': + args->rx_freq = atof(argv[optind]); + break; + case 'F': + args->tx_freq = atof(argv[optind]); + break; + case 'I': + args->ip_address = argv[optind]; + break; + case 't': + args->time_adv = atoi(argv[optind]); + break; + case 'v': + srsapps_verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rx_freq < 0 || args->tx_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + +int setup_if_addr(char *ip_addr); + +// Define dummy RLC always transmitts +class tester : public srsue::pdcp_interface_rlc, + public srsue::rrc_interface_rlc, + public srsue::rrc_interface_phy, + public srsue::rrc_interface_mac, + public srsue::ue_interface, + public thread +{ +public: + + tester() { + state = srsue::RRC_STATE_SIB1_SEARCH; + read_enable = true; + } + + void init(srsue::phy *phy_, srsue::mac *mac_, srsue::rlc *rlc_, srslte::log *log_h_, std::string ip_address) { + log_h = log_h_; + rlc = rlc_; + mac = mac_; + phy = phy_; + +#ifdef START_TUNTAP + if (init_tuntap((char*) ip_address.c_str())) { + log_h->error("Initiating IP address\n"); + } +#endif + + pool = srslte::byte_buffer_pool::get_instance(); + + // Start reader thread + running=true; + start(); + + } + + + void sib_search() + { + bool searching = true; + uint32_t tti ; + uint32_t si_win_start, si_win_len; + uint16_t period; + uint32_t nof_sib1_trials = 0; + const int SIB1_SEARCH_TIMEOUT = 30; + + while(searching) + { + switch(state) + { + case srsue::RRC_STATE_SIB1_SEARCH: + // Instruct MAC to look for SIB1 + while(!phy->status_is_sync()){ + usleep(50000); + } + usleep(10000); + tti = mac->get_current_tti(); + si_win_start = sib_start_tti(tti, 2, 5); + mac->bcch_start_rx(si_win_start, 1); + log_h->info("Instructed MAC to search for SIB1, win_start=%d, win_len=%d\n", + si_win_start, 1); + nof_sib1_trials++; + if (nof_sib1_trials >= SIB1_SEARCH_TIMEOUT) { + log_h->info("Timeout while searching for SIB1. Resynchronizing SFN...\n"); + log_h->console("Timeout while searching for SIB1. Resynchronizing SFN...\n"); + phy->resync_sfn(); + nof_sib1_trials = 0; + } + break; + case srsue::RRC_STATE_SIB2_SEARCH: + // Instruct MAC to look for SIB2 + usleep(10000); + tti = mac->get_current_tti(); + period = liblte_rrc_si_periodicity_num[sib1.sched_info[0].si_periodicity]; + si_win_start = sib_start_tti(tti, period, 0); + si_win_len = liblte_rrc_si_window_length_num[sib1.si_window_length]; + + mac->bcch_start_rx(si_win_start, si_win_len); + log_h->info("Instructed MAC to search for SIB2, win_start=%d, win_len=%d\n", + si_win_start, si_win_len); + + break; + default: + searching = false; + break; + } + usleep(100000); + } + } + + bool is_sib_received() { + return state == srsue::RRC_STATE_WAIT_FOR_CON_SETUP; + } + + + void release_pucch_srs() {} + void ra_problem() {} + void write_pdu_bcch_bch(srslte::byte_buffer_t *pdu) {} + void write_pdu_bcch_dlsch(srslte::byte_buffer_t *pdu) + { + log_h->info_hex(pdu->msg, pdu->N_bytes, "BCCH DLSCH message received."); + log_h->info("BCCH DLSCH message Stack latency: %ld us\n", pdu->get_latency_us()); + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT dlsch_msg; + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes*8); + bit_buf.N_bits = pdu->N_bytes*8; + pool->deallocate(pdu); + liblte_rrc_unpack_bcch_dlsch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &dlsch_msg); + + if (dlsch_msg.N_sibs > 0) { + if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1 == dlsch_msg.sibs[0].sib_type && srsue::RRC_STATE_SIB1_SEARCH == state) { + // Handle SIB1 + memcpy(&sib1, &dlsch_msg.sibs[0].sib.sib1, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT)); + log_h->info("SIB1 received, CellID=%d, si_window=%d, sib2_period=%d\n", + sib1.cell_id&0xfff, + liblte_rrc_si_window_length_num[sib1.si_window_length], + liblte_rrc_si_periodicity_num[sib1.sched_info[0].si_periodicity]); + std::stringstream ss; + for(int i=0;iconsole("SIB1 received, CellID=%d, %s\n", + sib1.cell_id&0xfff, + ss.str().c_str()); + + state = srsue::RRC_STATE_SIB2_SEARCH; + mac->bcch_stop_rx(); + //TODO: Use all SIB1 info + + } else if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2 == dlsch_msg.sibs[0].sib_type && srsue::RRC_STATE_SIB2_SEARCH == state) { + // Handle SIB2 + memcpy(&sib2, &dlsch_msg.sibs[0].sib.sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + log_h->console("SIB2 received\n"); + log_h->info("SIB2 received\n"); + state = srsue::RRC_STATE_WAIT_FOR_CON_SETUP; + mac->bcch_stop_rx(); + apply_sib2_configs(); + + srslte::byte_buffer_t *sdu = pool->allocate(); + assert(sdu); + + // Send Msg3 + sdu->N_bytes = 10; + for (int i=0;iN_bytes;i++) { + sdu->msg[i] = i+1; + } + uint64_t uecri = 0; + uint8_t *ue_cri_ptr = (uint8_t*) &uecri; + uint32_t nbytes = 6; + for (int i=0;imsg[i]; + } + log_h->info("Setting UE contention resolution ID: %d\n", uecri); + mac->set_contention_id(uecri); + + rlc->write_sdu(0, sdu); + + } + } + } + void write_pdu_pcch(srslte::byte_buffer_t *sdu) {} + void max_retx_attempted(){} + void in_sync() {}; + void out_of_sync() {}; + + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu) + { + int n=0; + switch(lcid) { + case LCID: + n = write(tun_fd, sdu->msg, sdu->N_bytes); + if (n != sdu->N_bytes) { + log_h->error("TUN/TAP write failure n=%d, nof_bytes=%d\n", n, sdu->N_bytes); + return; + } + log_h->debug_hex(sdu->msg, sdu->N_bytes, + "Wrote %d bytes to TUN/TAP\n", + sdu->N_bytes); + pool->deallocate(sdu); + break; + case 0: + log_h->info("Received ConnectionSetupComplete\n"); + + // Setup a single UM bearer + LIBLTE_RRC_RLC_CONFIG_STRUCT cfg; + bzero(&cfg, sizeof(LIBLTE_RRC_RLC_CONFIG_STRUCT)); + cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS100; + cfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + cfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + rlc->add_bearer(LCID, &cfg); + + mac->setup_lcid(LCID, 0, 1, -1, 100000); + + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated; + bzero(&dedicated, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + dedicated.pusch_cnfg_ded.beta_offset_ack_idx = 5; + dedicated.pusch_cnfg_ded.beta_offset_ri_idx = 12; + dedicated.pusch_cnfg_ded.beta_offset_cqi_idx = 15; + dedicated.pusch_cnfg_ded_present = true; + dedicated.sched_request_cnfg.dsr_trans_max = LIBLTE_RRC_DSR_TRANS_MAX_N4; + dedicated.sched_request_cnfg.sr_pucch_resource_idx = 0; + dedicated.sched_request_cnfg.sr_cnfg_idx = 35; + dedicated.sched_request_cnfg.setup_present = true; + dedicated.sched_request_cnfg_present = true; + phy->set_config_dedicated(&dedicated); + phy->configure_ul_params(); + + srsue::mac_interface_rrc::mac_cfg_t mac_cfg; + mac->get_config(&mac_cfg); + memcpy(&mac_cfg.sr, &dedicated.sched_request_cnfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + mac_cfg.main.ulsch_cnfg.periodic_bsr_timer = LIBLTE_RRC_PERIODIC_BSR_TIMER_SF40; + mac->set_config(&mac_cfg); + + break; + default: + log_h->error("Received message for lcid=%d\n", lcid); + break; + } + } + +private: + int tun_fd; + bool running; + srslte::log *log_h; + srslte::byte_buffer_pool *pool; + srsue::rlc *rlc; + srsue::mac *mac; + srsue::phy *phy; + srslte::bit_buffer_t bit_buf; + srsue::rrc_state_t state; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT sib1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + bool read_enable; + + + // Determine SI messages scheduling as in 36.331 5.2.3 Acquisition of an SI message + uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t x) { + return (period*10*(1+tti/(period*10))+x)%10240; // the 1 means next opportunity + } + + int init_tuntap(char *ip_address) { + read_enable = true; + tun_fd = setup_if_addr(ip_address); + if (tun_fd<0) { + fprintf(stderr, "Error setting up IP %s\n", ip_address); + return -1; + } + + printf("Created tun/tap interface at IP %s\n", ip_address); + return 0; + } + + void run_thread() { + struct iphdr *ip_pkt; + uint32_t idx = 0; + int32_t N_bytes; + srslte::byte_buffer_t *pdu = pool->allocate(); + + log_h->info("TUN/TAP reader thread running\n"); + + while(running) { + N_bytes = read(tun_fd, &pdu->msg[idx], SRSUE_MAX_BUFFER_SIZE_BYTES-SRSUE_BUFFER_HEADER_OFFSET - idx); + if(N_bytes > 0 && read_enable) + { + pdu->N_bytes = idx + N_bytes; + ip_pkt = (struct iphdr*)pdu->msg; + + log_h->debug_hex(pdu->msg, pdu->N_bytes, + "Read %d bytes from TUN/TAP\n", + N_bytes); + + // Check if entire packet was received + if(ntohs(ip_pkt->tot_len) == pdu->N_bytes) + { + log_h->info_hex(pdu->msg, pdu->N_bytes, "UL PDU"); + + // Send PDU directly to PDCP + pdu->set_timestamp(); + rlc->write_sdu(LCID, pdu); + + pdu = pool->allocate(); + idx = 0; + } else{ + idx += N_bytes; + } + }else{ + log_h->error("Failed to read from TUN interface - gw receive thread exiting.\n"); + break; + } + } + } + + + void apply_sib2_configs() + { + + // Apply RACH timeAlginmentTimer configuration + srsue::mac_interface_rrc::mac_cfg_t cfg; + mac->get_config(&cfg); + cfg.main.time_alignment_timer = sib2.time_alignment_timer; + memcpy(&cfg.rach, &sib2.rr_config_common_sib.rach_cnfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + cfg.prach_config_index = sib2.rr_config_common_sib.prach_cnfg.root_sequence_index; + mac->set_config(&cfg); + + log_h->info("Set RACH ConfigCommon: NofPreambles=%d, ResponseWindow=%d, ContentionResolutionTimer=%d ms\n", + liblte_rrc_number_of_ra_preambles_num[sib2.rr_config_common_sib.rach_cnfg.num_ra_preambles], + liblte_rrc_ra_response_window_size_num[sib2.rr_config_common_sib.rach_cnfg.ra_resp_win_size], + liblte_rrc_mac_contention_resolution_timer_num[sib2.rr_config_common_sib.rach_cnfg.mac_con_res_timer]); + + // Apply PHY RR Config Common + srsue::phy_interface_rrc::phy_cfg_common_t common; + memcpy(&common.pdsch_cnfg, &sib2.rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pusch_cnfg, &sib2.rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pucch_cnfg, &sib2.rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.ul_pwr_ctrl, &sib2.rr_config_common_sib.ul_pwr_ctrl, sizeof(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT)); + memcpy(&common.prach_cnfg, &sib2.rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + if (sib2.rr_config_common_sib.srs_ul_cnfg.present) { + memcpy(&common.srs_ul_cnfg, &sib2.rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + } else { + // default is release + common.srs_ul_cnfg.present = false; + } + phy->set_config_common(&common); + + phy->configure_ul_params(); + + log_h->info("Set PUSCH ConfigCommon: HopOffset=%d, RSGroup=%d, RSNcs=%d, N_sb=%d\n", + sib2.rr_config_common_sib.pusch_cnfg.pusch_hopping_offset, + sib2.rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch, + sib2.rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift, + sib2.rr_config_common_sib.pusch_cnfg.n_sb); + + log_h->info("Set PUCCH ConfigCommon: DeltaShift=%d, CyclicShift=%d, N1=%d, NRB=%d\n", + liblte_rrc_delta_pucch_shift_num[sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift], + sib2.rr_config_common_sib.pucch_cnfg.n_cs_an, + sib2.rr_config_common_sib.pucch_cnfg.n1_pucch_an, + sib2.rr_config_common_sib.pucch_cnfg.n_rb_cqi); + + log_h->info("Set PRACH ConfigCommon: SeqIdx=%d, HS=%d, FreqOffset=%d, ZC=%d, ConfigIndex=%d\n", + sib2.rr_config_common_sib.prach_cnfg.root_sequence_index, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag?1:0, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index); + + log_h->info("Set SRS ConfigCommon: BW-Configuration=%d, SF-Configuration=%d, ACKNACK=%d\n", + sib2.rr_config_common_sib.srs_ul_cnfg.bw_cnfg, + sib2.rr_config_common_sib.srs_ul_cnfg.subfr_cnfg, + sib2.rr_config_common_sib.srs_ul_cnfg.ack_nack_simul_tx); + + } +}; + + + +// Create classes +srslte::logger logger; +srslte::log_filter log_phy; +srslte::log_filter log_mac; +srslte::log_filter log_rlc; +srslte::log_filter log_tester; +srslte::mac_pcap mac_pcap; +srsue::phy my_phy; +srsue::mac my_mac; +srsue::rlc rlc; +srslte::radio_multi my_radio; + +// Local classes for testing +tester my_tester; + + +bool running = true; + +void sig_int_handler(int signo) +{ + running = false; +} + +int main(int argc, char *argv[]) +{ + + parse_args(&prog_args, argc, argv); + + // set to null to disable pcap + const char *pcap_filename = "/tmp/ip_test.pcap"; + + logger.init("/tmp/ip_test_ue.log"); + log_phy.init("PHY ", &logger, true); + log_mac.init("MAC ", &logger, true); + log_rlc.init("RLC ", &logger); + log_tester.init("TEST", &logger); + logger.log("\n\n"); + + if (srsapps_verbose == 1) { + log_phy.set_level(srslte::LOG_LEVEL_INFO); + log_phy.set_hex_limit(100); + log_mac.set_level(srslte::LOG_LEVEL_DEBUG); + log_mac.set_hex_limit(100); + log_rlc.set_level(srslte::LOG_LEVEL_DEBUG); + log_rlc.set_hex_limit(1000); + log_tester.set_level(srslte::LOG_LEVEL_DEBUG); + log_tester.set_hex_limit(100); + printf("Log level info\n"); + } + if (srsapps_verbose == 2) { + log_phy.set_level(srslte::LOG_LEVEL_DEBUG); + log_phy.set_hex_limit(100); + log_mac.set_level(srslte::LOG_LEVEL_DEBUG); + log_mac.set_hex_limit(100); + log_rlc.set_level(srslte::LOG_LEVEL_DEBUG); + log_rlc.set_hex_limit(100); + log_tester.set_level(srslte::LOG_LEVEL_DEBUG); + log_tester.set_hex_limit(100); + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + printf("Log level debug\n"); + } + + // Init Radio and PHY +#ifdef USE_RADIO + my_radio.init(); +#else + my_radio.init(NULL, "dummy"); +#endif + + my_radio.set_tx_freq(prog_args.tx_freq); + my_radio.set_tx_gain(prog_args.tx_gain); + my_radio.set_rx_freq(prog_args.rx_freq); + my_radio.set_rx_gain(prog_args.rx_gain); + if (prog_args.time_adv >= 0) { + printf("Setting TA=%d samples\n",prog_args.time_adv); + my_radio.set_tx_adv(prog_args.time_adv); + } + + my_phy.init(&my_radio, &my_mac, &my_tester, &log_phy, NULL); + my_mac.init(&my_phy, &rlc, &my_tester, &log_mac); + rlc.init(&my_tester, &my_tester, &my_tester, &log_rlc, &my_mac); + my_tester.init(&my_phy, &my_mac, &rlc, &log_tester, prog_args.ip_address); + + + if (pcap_filename) { + mac_pcap.open(pcap_filename); + my_mac.start_pcap(&mac_pcap); + signal(SIGINT, sig_int_handler); + } + + // Set MAC defaults + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT default_cfg; + bzero(&default_cfg, sizeof(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT)); + default_cfg.ulsch_cnfg.max_harq_tx = LIBLTE_RRC_MAX_HARQ_TX_N5; + default_cfg.ulsch_cnfg.periodic_bsr_timer = LIBLTE_RRC_PERIODIC_BSR_TIMER_INFINITY; + default_cfg.ulsch_cnfg.retx_bsr_timer = LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF2560; + default_cfg.ulsch_cnfg.tti_bundling = false; + default_cfg.drx_cnfg.setup_present = false; + default_cfg.phr_cnfg.setup_present = false; + default_cfg.time_alignment_timer = LIBLTE_RRC_TIME_ALIGNMENT_TIMER_INFINITY; + my_mac.set_config_main(&default_cfg); + + while(running) { + if (my_tester.is_sib_received()) { + printf("Main running\n"); + sleep(1); + } else { + my_tester.sib_search(); + } + } + + if (pcap_filename) { + mac_pcap.close(); + } + + my_phy.stop(); + my_mac.stop(); +} + + + + +/******************* This is copied from srsue gw **********************/ +int setup_if_addr(char *ip_addr) +{ + + char *dev = (char*) "tun_srsue"; + + // Construct the TUN device + int tun_fd = open("/dev/net/tun", O_RDWR); + if(0 > tun_fd) + { + perror("open"); + return(-1); + } + + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ); + if(0 > ioctl(tun_fd, TUNSETIFF, &ifr)) + { + perror("ioctl"); + return -1; + } + + // Bring up the interface + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr)) + { + perror("socket"); + return -1; + } + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr)) + { + perror("ioctl"); + return -1; + } + + // Setup the IP address + sock = socket(AF_INET, SOCK_DGRAM, 0); + ifr.ifr_addr.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = inet_addr(ip_addr); + if(0 > ioctl(sock, SIOCSIFADDR, &ifr)) + { + perror("ioctl"); + return -1; + } + ifr.ifr_netmask.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); + if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) + { + perror("ioctl"); + return -1; + } + + return(tun_fd); +} diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example new file mode 100644 index 000000000..065538710 --- /dev/null +++ b/srsue/ue.conf.example @@ -0,0 +1,172 @@ +##################################################################### +# srsUE configuration file +##################################################################### +# RF configuration +# +# dl_freq: Downlink centre frequency (Hz). +# ul_freq: Uplink centre frequency (Hz). +# tx_gain: Transmit gain (dB). +# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# +# Optional parameters: +# nof_rx_ant: Number of RX antennas (Default 1, supported 1 or 2) +# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" +# device_args: Arguments for the device driver. Options are "auto" or any string. +# Default for UHD: "recv_frame_size=9232,send_frame_size=9232" +# Default for bladeRF: "" +# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay +# from antenna to timestamp insertion. +# Default "auto". B210 USRP: 100 samples, bladeRF: 27. +# burst_preamble_us: Preamble length to transmit before start of burst. +# Default "auto". B210 USRP: 400 us, bladeRF: 0 us. +##################################################################### +[rf] +dl_freq = 2680000000 +ul_freq = 2560000000 +tx_gain = 60 +rx_gain = 50 + +#nof_rx_ant = 1 +#device_name = auto +#device_args = auto +#time_adv_nsamples = auto +#burst_preamble_us = auto + + +##################################################################### +# MAC-layer packet capture configuration +# +# Packets are captured to file in the compact format decoded by +# the Wireshark mac-lte-framed dissector and with DLT 147. +# To use the dissector, edit the preferences for DLT_USER to +# add an entry with DLT=147, Payload Protocol=mac-lte-framed. +# For more information see: https://wiki.wireshark.org/MAC-LTE +# +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for packet captures +##################################################################### +[pcap] +enable = false +filename = /tmp/ue.pcap + +##################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. phy_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. phy_hex_limit = 32 +# +# Logging layers: phy, mac, rlc, pdcp, rrc, nas, gw, usim, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output +##################################################################### +[log] +all_level = info +all_hex_limit = 32 +filename = /tmp/ue.log + +##################################################################### +# USIM configuration +# +# algo: Authentication algorithm (xor/milenage) +# op: 128-bit Operator Variant Algorithm Configuration Field (hex) +# amf: 16-bit Authentication Management Field (hex) +# k: 128-bit subscriber key (hex) +# imsi: 15 digit International Mobile Subscriber Identity +# imei: 15 digit International Mobile Station Equipment Identity +##################################################################### +[usim] +algo = milenage +op = 63BFA50EE6523365FF14C1F45F88737D +amf = 8000 +k = 00112233445566778899aabbccddeeff +imsi = 001010123456789 +imei = 353490069873319 + +[gui] +enable = false + +##################################################################### +# Expert configuration options +# +# ue_category: Sets UE category (range 1-5). Default: 4 +# +# prach_gain: PRACH gain (dB). If defined, forces a gain for the tranmsission of PRACH only., +# Default is to use tx_gain in [rf] section. +# cqi_max: Upper bound on the maximum CQI to be reported. Default 15. +# cqi_fixed: Fixes the reported CQI to a constant value. Default disabled. +# snr_ema_coeff: Sets the SNR exponential moving average coefficient (Default 0.1) +# snr_estim_alg: Sets the noise estimation algorithm. (Default refs) +# Options: pss: use difference between received and known pss signal, +# refs: use difference between noise references and noiseless (after filtering) +# empty: use empty subcarriers in the boarder of pss/sss signal +# pdsch_max_its: Maximum number of turbo decoder iterations (Default 4) +# attach_enable_64qam: Enables PUSCH 64QAM modulation before attachment (Necessary for old +# Amarisoft LTE 100 eNodeB, disabled by default) +# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) +# equalizer_mode: Selects equalizer mode. Valid modes are: "mmse", "zf" or any +# non-negative real number to indicate a regularized zf coefficient. +# Default is MMSE. +# cfo_integer_enabled: Enables integer CFO estimation and correction. This needs improvement +# and may lead to incorrect synchronization. Use with caution. +# cfo_correct_tol_hz: Tolerance (in Hz) for digial CFO compensation. Lower tolerance means that +# a new table will be generated more often. +# time_correct_period: Period for sampling time offset correction. Default is 10 (ue_sync.c), +# good for long channels. For best performance at highest SNR reduce it to 1. +# sfo_correct_disable: Disables phase correction before channel estimation to compensate for +# sampling frequency offset. Default is enabled. +# sss_algorithm: Selects the SSS estimation algorithm. Can choose between +# {full, partial, diff}. +# estimator_fil_w: Chooses the coefficients for the 3-tap channel estimator centered filter. +# The taps are [w, 1-2w, w] +# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# +# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# +##################################################################### +[expert] +#ue_category = 4 +#prach_gain = 30 +#cqi_max = 15 +#cqi_offset = 0 +#cqi_fixed = 10 +#cqi_random_ms = 0 +#cqi_period_ms = 0 +#cqi_period_duty = 0.5 +#snr_ema_coeff = 0.1 +#snr_estim_alg = refs +#pdsch_max_its = 4 +#attach_enable_64qam = false +#nof_phy_threads = 2 +#equalizer_mode = mmse +#cfo_integer_enabled = false +#cfo_correct_tol_hz = 50 +#time_correct_period = 5 +#sfo_correct_disable = false +#sss_algorithm = full +#estimator_fil_w = 0.1 +#pregenerate_signals = false + +##################################################################### +# Manual RF calibration +# +# Applies DC offset and IQ imbalance to TX and RX modules. +# Currently this configuration is only used if the detected device is a bladeRF +# +# tx_corr_dc_gain: TX DC offset gain correction +# tx_corr_dc_phase: TX DC offset phase correction +# tx_corr_iq_i: TX IQ imbalance inphase correction +# tx_corr_iq_q: TX IQ imbalance quadrature correction +# same can be configured for rx_* +##################################################################### +[rf_calibration] +tx_corr_dc_gain = 20 +tx_corr_dc_phase = 184 +tx_corr_iq_i = 19 +tx_corr_iq_q = 97